前言
上个月在护网杯打了个酱油,第一次打某宁的线下赛,风格跟某春秋差别还是挺大的,没什么端口限制,提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 0x20chunk 0x20 - 删除list0
free 0x20 - 删除user2
free 0x90 - 创建一个新order1
chunk 0x20 chunk 0x90(会删除list1free 0x20) - 创建两个新list0,list1
chunk 0x20chunk 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纸,真是累死了。。。。。。