通过抓包登录的报文, 我们可以发现如果先是随便输入一对账密
会抓到一个发向http的包
可以看到有一个参数auth=0
此时如果放行报文就会登录失败, 但是将auth的值改成1就可以登录成功
首先可以来到固件与备份, 将这个项目下载得到二进制文件
首先可以分析得到请求的传输流程html -> /cgi-bin/*.cgi
对http进行分析, 可以发现这个文件:
接下来我们可以结合html页面和cgi综合分析每个业务逻辑的链路

除了重启按钮以外, 几乎每一个接口都有对应的cgi
直接从docker启动脚本中发现
起docker的时候顺手将mainproc拉起放置在后台, 可以猜测具体的功能实现在mainproc中
现在就该重点分析mainproc了
首先可以发现这个文件中的init_array存在一个函数指针
获取了堆的基地址, 并为其添加了x(可执行权限)
接着在dispatch_action函数中可以发现一个巨大的switch-case结构(IDA的反编译会变成if-else结构)
根据不同的魔数, 调用不同功能的函数, 从cgi中提取不同的功能可以整理出接口与处理函数的对应关系
接下来应该梳理不同结构体, 结构体从IDA静态分析不是很容易, 推荐通过gdb调试描绘结构体轮廓
黑白名单 :
wifi设置 :
这两种结构体只会在堆上创建两种不同大小的堆块, 没有具体的作用
vpn :
在vpn.cgi中
在mainproc中
可以发现vpn结构体在两个进程中的结构差异很大, 且在mainproc中存在函数和内存两种指针
不同的处理函数的逻辑很简单, 包括vpn也是, 从cgi结构体中将同名成员复制到mainproc结构体中
但是注意, 这里使用了不安全的strcpy且没有做保护
我们知道这些成员都是从json中取出来的, 一般的json都会在字段的结构加上'\0'
但如果我们前往.so审计
发现当字段的预定长度被充满后, 就不会在末尾加上null戳
结合strcpy我们就可以实现off-by-null
从结构体结构上看, 最有溢出价值的字段就是cert和pass字段, 可以修改两种指针的末尾
但是在IDA中审计发现, 如果把default_vpn_apply的末尾改成null, 会跳转到一个导致进程段错误的地址
所以可以利用的字段只剩下了pass字段, 可以修改custom字段的末尾, 而且custom指向的是堆地址
我们可以用堆风水的手段, 让custom能指向一个能够修改函数指针的地址
同时可以发现在vpn.cgi中, 当字段以B64开头, 整个字段会被base64编码后使用json传输, 这解决了json对不可见字符传递的局限性
现在可以考虑如何进行堆风水了, 既然想把custom指向函数指针, 我们就要让末尾被置零后的地址小于等于函数指针
可以看到这两个地址间的offset为8+0x20+0x20+0x20+0x30+0x20+0x20=0xd8
最好的解决方法是让void (*apply_cb)(struct vpn_config_req *)的最低字节为00
通过简单的计算可以得到, 我们只需要进行wifi*1 + list*7边可以将函数指针挤到末字节为00的地址
此时从pass字段溢出null, 就可以让custom指向04:0020│ 0x555555559500 —▸ 0x55555555540d (default_vpn_apply) ◂— endbr64
最后于 2小时前
被zer00ne编辑
,原因: