中华武术杯 2023 AWDP Pwn WriteUp
奔着领低保去的
朝着没去过的上海冲锋!
不报差旅差评 虽然有低保
2023 第一节 中华武术杯 AWDP Pwn WriteUp
碎碎念
定力还得练,这次比赛又在几道题之间反复横跳了。
最后一分钟写完exp,没来的及微调就结束力。
搬一段自己的空间:
今日份awdp
签到题急了,没沉下去,傻逼fix没拿到高分
然后急着去看第二题fix,但是并看不懂
等到最后一个半小时,签到题break思路来了
最后半小时进入利用,最后一分钟写完exp,但是bug了,很傻逼的那种
最后一秒interactive,服务器下线了
你妈
又是被带飞的一天
最后,以及再说一遍,我是傻逼
randomHeap
程序分析
初始化阶段随机抬了[0,15]个内存页,开头大小0x291的tcache列表也给做出来了,好评。
然后随机在堆上分配16个0x10
和0x28
大小的chunk,分别作为header
和data
,数据结构如下。
-
header:
0x0: size (signed long long)
0x8: ptr (char*)
-
data:
0x0: data (char[0x28])
从此不会再有新的malloc行为。
分配阶段,从header
和data
各随机取一个,放入bss上的列表中。整个流程申请超过16个直接exit(0)
。
删除也清空了header
中的指针,不存在UAF。
show
函数使用write(1,header->ptr,header->size)
,可以无视0截断泄露信息。
漏洞点分析
edit
函数中,offset
没有检查负数,导致可以向堆上较低地址任意写,具体为header->ptr[offset]
。
因为堆的header
和data
是在初始化时malloc
的,地址连续,仅在add
过程中随机分配。因此有较大概率header
会与data
相邻。
初始化时分配的大堆块总大小总是0x1000的倍数,只会对heap_base以内存页为单位产生影响,对我们下一步的利用影响不大。
那我们就可以开喷,假设至少存在一个header
和data
相邻,随便找一个data
,向前修改它相邻的header
的size域,再挨个show
所有堆块,如果假设成立,我们就会收到长度大于0x28的数据。
如此,我们可以拿到堆上地址,进而拿到header
和data
所在页地址。
记随机选取的堆块id为random_idx
,找到可以show
出数据的堆块id为target_idx
。
然后我们将random_idx
的header->data
指针低12位清零,进而拿到整页的内存分布。
接下来我们考虑怎么拿libc。
由于删除过程会删干净指针,我们不能直接使用上一步用过的random_idx
和target_idx
。我们需要再控制一个指针,使其指向初始化阶段分配的大小0xd60的堆块。
我们可以随意取一个与random_idx
和target_idx
不同id的header
,找到对应与之对应的data
,计算此header
与data
的距离,我们就又拿到一个可控的header
,并进一步利用它完成对大堆块的free。
构造任意读写就很简单了,拿着random_idx
和target_idx
就能随便改随便写。
有了libc和任意读写就可以考虑怎么getshell了。
main
可以正常return 0
,考虑泄露env打栈。
攻击exp
这个脚本其实还可以优化,就比如
fail to calc offset
那个手写的assert,可以再往下找一个堆块并重新计算。不过对于当时来说,重新跑花费的时间相比做优化花费的时间少得多,果断大力出奇迹了。
虽然本地命中概率不太高(体感低于50%),远程的命中概率还是挺高的,跑了三次都中了。
虽然最后就差一点。擦。
|
|
修复
edit
中有符号比较改成无符号比较jle=>jbe
即可。
ShortestPath
在这里先给Lilac的
cnw@ngjihe
磕一个
程序分析
程序主体实现了一个spfa计算最短路的算法。
主要数据结构:
- 节点
node_t
大小0x18
0x0:idx
0x8:edge_next
0x10:node_next
- 边
edge_t
大小0x28
0x0:val
0x8:data
0x10:dst
0x18:size
0x20:edge_next
- 计算最短路时的
testnode_t
大小0x18
0x0:all_nodes
curr_t[] 0x8:head
队列头指针 0x10:tail
队列尾指针 - 当前走道的节点
curr_t
大小0x18
0x0:val
0x8:idx
0x10:step
计算最短路时,存放节点的队列大小只有n+1
,但是由于SPFA中每个节点最多可以入队n次,这里存在堆溢出。
输入函数以回车结尾,但是没有将回车写到buf,也没有写0截断,发个回车就能泄露信息了。
curr_t
第一个字段是val
,我们只要将chunk_size
写进edge_t
的val
,再造几条负边,通过最短路计算就可以改大chunk_size
然后就是风水了。稍微调一调,打栈就行了。
攻击exp
|
|
修复
Lilac的师傅直接把节点队列改大。
处理大小的部分有一个shr rax,3
,据Lilac的师傅说改9就可以过。
至于预期解就不得而知了,也许跟那个更新队列的函数以及sub_1993
有关?