搞了快三个小时才出来_(:з」∠)_几乎白给
雷泽太强了!
简单逆向后可以知道
- 该程序具有注册和登陆功能
- 注册后会给
name
赋值为userx
,而get_flag的需求为name
==adminx
passwd
成员存储原始密码加盐(随机数)加密后的结果code
成员存储其他成员加盐(随机数)加密后的结果- 登录时校验
passwd
和code
Struct:
00000000 User struc ; (sizeof=0x84, mappedto_6)
00000000 index dd ?
00000004 age dd ?
00000008 len_text dd ?
0000000C name db 24 dup(?)
00000024 passwd db 32 dup(?)
00000044 text db 32 dup(?)
00000064 code db 32 dup(?)
00000084 User ends
由于get_flag方法为修改name
,因此显然需要利用漏洞修改name
简单审计后发现存在两个溢出点
6. set_passwd里的length自由输入,buf的大小为0x100,存在栈溢出,但有canary
7. set_text里的length自由输入,buf的大小为0x20,存在堆溢出,可以覆盖后一个堆
作为一个单纯的逆向狗,对堆的结构完全不了解╮(╯_╰)╭所以不考虑 CHUNK 相关的利用
这里的堆溢出可以任意修改后面的块,但直接把name
改成adminx
以后会在check_code
的地方由于其他成员被修改而校验失败退出
那么在篡改name
的时候就必须一起把code
成员也篡改了
于是有两个方法
- 用现成的加密令其生成哈希,并泄露出来然后写入
code
成员 - 自行解密
code
,得到RANDOM然后再次加密
扫了一眼加密算法发现复杂的一批,懒得看了就当蜜汁哈希了
(后来提示里给出了是SM3的散列算法,然而其实没啥用233 并不知道咋泄露BSS段里的RANDOM)
思考了一阵子以后想起来set_passwd
里面可以根据用户使用的passwd
来生成哈希,另一方面login
以后whoami
方法可以print text
,而通过控制text
可以令其一直输出到下一个块的目标成员,从而泄露下一个块的passwd
和code
由于最多只能创建三个块,因此需要比较节省233
思考了一下感觉这个障碍对思路影响不大
思路:
- 创建两个块,令第一个块的
text
填到下一个块的passwd
前,从而泄露出某个密码此时的哈希 - 创建新的块,输入的
Passwd
为一整个结构体,从而使它的passwd
成员为100
个字节的校验 - 登录第一个块,使它的
text
填到第三个块的passw
前,从而泄露出目标code
- 还是第一个块,使它的
text
重写第二个块 - 登录第二个块,get_flag
先给一个比赛时的辣鸡脚本,回来再整理结构~
from pwn import *
#p = process("./pwn")
p = remote("172.16.9.23", 9014)
#context.log_level="debug"
def reg(p, passwd):
# age
p.sendline("1")
# passwdlen
p.sendline(str