前言
上个月在护网杯打了个酱油,第一次打某宁的线下赛,风格跟某春秋差别还是挺大的,没什么端口限制,提flag接口也很友好,属于最开放的网络环境了,可以用各种大杀器(然而我没有准备= =|||)。比赛当时只用到了最简单的洞,虽然官方给了流量(队友不说我都没发现。。。),但是没啥经验,比赛的时候也没成功重放。总体来说题目质量不错,难得有攻击流量,赛后来学习一波吧~
shell
这题有一个超级简单的命令执行洞,直接ls
后闭合命令就可以了,如/ && cat ../flag
。另外还有一个堆的洞,网上有大神分析过,我就不写了,直接给EXP,原理差不多,漏洞利用方式略有不同而已。
1 | def msend(size,smsg): |
store
这题的复杂程度简直丧心病狂,在此膜一下腾讯的大佬,4小时的比赛能做出这题目。从流量来看,很多队伍都是重发腾讯的流量,还有一队是另一种解法,姿势也是很值得学习。这条题一堆的结构体,单是看程序逻辑都看了半天。大佬们都是风水大师,堆风水6得飞起。
程序分析
这题目的主要功能有两个,一个是操作cake
的流程,一个是操作account
的流程。其中需要关键操作(对账号操作,对蛋糕下单)需要admin
权限,留意程序初始化生成了一个admin
账号。
1 | unsigned __int64 init_admin() |
- cake操作
几个重要的结构体如下:
1 | struct cake |
购买cake
的过程如下:1
2
3
4- add_to_list | malloc(0x18) 新增一个list
- order_cake | malloc(0x18) -> malloc(0x7c) -> free(0x18) 新增order及其desc,所有order完成后,从后往前清掉所有list
- cancel_list | free(0x18) 取消一个list
- buy | free(0x7c) -> free(0x18) -> malloc(0x4b0) 清除list的desc,清除list,新增一个desc
内存泄露漏洞点在,创建新cake_list
的时候,没有初始化内存,生成的随机数只有一位,重新利旧bins
的时候只会覆盖fd
的最低位,打印cake_list
信息的时候可以泄露地址。
1 | new_cake_list = (struct cake_list *)malloc(0x18uLL); |
- account操作
结构体如下:(其中user_list
存在bss
段)
1 | struct user |
对account
可以进行注册,删除,修改密码这3个操作:
1 | - 注册:malloc(0x80) |
存在一个明显的UAF
,如果能控制user_data
,就能任意地址写。
留意delete_account
一个坑点,user删除之后,虽然不会清空user_data
指针,但是指针-16
了,本来创建一个0x20
大小的chunk后,利用UAF可以很轻易地修改chunk size
,现在需要将两个0x90
的chunk拼起来再padding一堆0x20
大小的chunk才能修改chunk size
1 | if ( is_admin[4 * v3] == 3 ) |
exp编写
解题方法一
首先需要泄露地址,方法很简单
- 创建一个新账号(chunk是一个
unsorted bins
) - 利用
add_to_list
创建一个新list - 删掉这个账号
- 此时利用
add_to_list
创建一个新list就可以利旧刚刚free的user的chunk,unsorted bins
的fd
就能泄露出来
gets hell方法是:使用unlink
控制user_data
指针,然后劫持got表,关键时如何构造fake chunk
这题目,可控制的输入点只有几个:
- 创建账号时输入的
name
,password
,profile
,然而删除的时候内存会清空,并没有什么用 - 下order时输入的
desc
,这个比较好用长度有0x7c
- 购买cake后输入的
desc
,这个输入长度有0x4b0
,也很好用
构造关键点:
- 由于上面提到
delete_account
的坑,需要将两个0x90
的chunk,连在一起使用,才能修改Chunk size
- 程序没有
double free
和堆溢出,需要使allocated chunk
和free chunk
重叠
构造步骤如下:
- 创建一个新账号
chunk 0x90
- 创建一个新list0
chunk 0x20
- 创建一个新order0
chunk 0x20 chunk 0x90
(会删除list0free 0x20
) - 创建一个新账号
chunk 0x90
- 创建两个新list0,list1
chunk 0x20
chunk 0x20
- 删除list0
free 0x20
- 删除user2
free 0x90
- 创建一个新order1
chunk 0x20 chunk 0x90
(会删除list1free 0x20
) - 创建两个新list0,list1
chunk 0x20
chunk 0x20
- 删除user3
free 0x90
- 购买order0
free 0x90 free 0x20 chunk 0x4c0
1 | allocated 0x90 #user2 => order1[desc] |
此时,3个freed chunk
会合并一起为一个unsorted bins
,继续创建新list2-8。这时候,发现修改user3
的password
,可以修改list8
的size
,如果把size
修改为0x581
(0x4c0+0x20+0x20+0x80),那么删除list8
,就能把下面一整片的内存都free掉,top chunk
被抬高。
继续删掉list4-7,然后下order时,会创建desc
,会从top chunk
处开始分配内存,这个chunk
的最后16字节,刚好覆盖到list0
的chunk head
,经过精心构造,删除list0
的时候就可以触发unlink
。
观察一下当前heap的结构,list0
前面可控的chunk
有order1[desc]
,可以在这里构造fake chunk
,p64(&p-0x18)+p64(&p-0x10)
。将list0
的chunk head
改为p64(0x1f0)+p64(90)
,同时下一块chunk
需要补两个allocated chunk
,可以在desc0
中预留。
完整exp:
1 | def login(newID='',newPWD='',newDESC='',reg=0): |
解题方法二
泄露地址方法给上面一样,不过这次还需要泄露heap地址,可以通过泄露fastbins
的fd
地址计算。上面提到UAF修改chunk size
比较麻烦,不过可以修改smallbins
的bk
。过程很复杂,不想写了,具体过程动态调试看吧。
思路是控制smallbins
的bk
,指向构造的两个假smallbins
,假的smallbins
内存空间与其他已使用的chunk
重叠,可以修改其他chunk head
制造触发unlink
的条件。
完整EXP:
1 | login() |
后记
第一次分析pwn的题流量,真难受死了,只有输入的流量,没有回显。果然pwn手都比较单纯,没有混淆流量,不像web,一大堆wangyihang的fake requests[捂脸]。web题估计没时间复现了,搞这题store用了我好几张A4纸,真是累死了。。。。。。