TOTOLINK–webserver与cgi
| 0 | 前言
经过了nepctf的洗礼,感觉自己找回了对二进制安全的热爱
依旧不会web,依旧被拷打…不过已经开始补web的知识了
| 1 | web基础知识
固件包中,常见的二进制文件包括httpd/xxx(.cgi)
httpd用于
- 维护
webserver,监听端口,等待客户端发发送的请求 - 显示静态页面(资源)
- 调用后端程序(
cgi,php)处理请求 - 处理
HTTP协议细节,如重定向、状态码、缓存控制
cgi用于请求处理的具体实现
httpd会fork出一个子进程用于执行(execve)cgi程序
httpd获取client报文,将不同的字段序列化后传递给cgi
后cgi与httpd通过pipe管道进行数据的交互,最后cgi进程终止并发送终止信号,httpd将响应传回client

报文由客户端(通常是浏览器)发出,接收到httpd的回显后,如果包含重定向信息,浏览器会自动发送请求报文,跳转并访问到重定向的资源.
比如响应数据包为
1 | HTTP/1.1 302 Found |
浏览器就会自动发送访问http://192.168.52.134/formLoginAuth.htm?authCode=0&userName=&goURL=login.html&action=login的请求
POST与GET请求为web中最常见的两种请求方式
其中GET直接通过url传参
1 | GET(请求方式) /path/to/resource(请求资源路径)?key1=value1&key2=value2(请求变量) HTTP/1.1(协议版本) |
POST则通过请求体传参
1 | POST /submit-form HTTP/1.1 |
| 1 | 环境搭建
- 固件下载:totolink
- 固件解包
1 | binwalk -e --preserve-symlinks TOTOLINK_C834FR-1C_NR1800X_IP04469_MT7621A_SPI_16M256M_V9.1.0u.6279_B20210910_ALL.web |
搭建
qemu环境,将文件系统传入其中
下载qemu:qemu下载启动脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#!/bin/bash
# TAP接口配置
TAP_IF="tap0"
BR_IF="br0"
# 确保 tap0 存在,并添加进 br0
sudo ip tuntap add dev $TAP_IF mode tap
sudo ip link set $TAP_IF up
sudo brctl addif $BR_IF $TAP_IF
sudo qemu-system-mipsel \
-M malta \
-kernel vmlinux-3.2.0-4-4kc-malta \
-hda debian_wheezy_mipsel_standard.qcow2 \
-append "root=/dev/sda1 console=tty0" \
-net nic,macaddr=00:16:3e:00:00:01 \
-net tap,ifname=tap0,script=no,downscript=no \
-nographic我们观察文件系统,可以发现
lighttp/lighttpd.conf中已经配置好了,那我们就可以在挂载后直接启动httpd服务1
2
3mount -o bind /dev ./squashfs-root/dev
mount -t proc /proc ./squashfs-root/proc
chroot ./squashfs-root sh启动服务
1
lighttpd -f /lighttp/lighttpd.conf
我们在浏览器中输入qemu的IP,可以发现web服务已经出现,说明环境配置完成

| 2 | 登录密码绕过流程
我们先在brup的内嵌浏览器打开拦截,随便输入一个密码(123),可以看到POST请求为
1 | POST /cgi-bin/cstecgi.cgi?action=login HTTP/1.1 |
我们将这段报文复制到复发送,发送后查看响应包

可以看到被重定向到http://192.168.52.134/formLoginAuth.htm?authCode=0&userName=&goURL=login.html&action=login
我们直接点击跟随重定向
可以看到浏览器自动发送了一个如下GET请求
1 | GET /formLoginAuth.htm?authCode=0&userName=&goURL=login.html&action=login HTTP/1.1 |
可以看到访问资源为html,由httpd负责处理,所以我们使用IDA打开httpd,搜索formLoginAuth相关内容

明显通过函数名可以看出这个Form_Login是登录逻辑
1 | int __fastcall Form_Login(int a1, int a2, int a3) |
如果authCode不为0,可以获得一个有效的"SESSION_ID",表示登录成功
在cgi的登录行为中,我们可以发现
1 | int __fastcall check_login(int jsonData) |
我们可以看到cgi将登录判定(authCode)通过重定向标志传输给httpd服务
但是重定向的数据包我们是可以通过brup进行修改,也就是可以控制传入httpd的authCode值!
如果我们将重定向的url中的authCode字段修改为真,则可以在密码错误的情况下登录
也就是直接访问192.168.52.134/formLoginAuth.htm?authCode=1&action=login

使用brup修改重定向的GET请求,将重定向中的authCode字段修改为1,
1 | GET /formLoginAuth.htm?authCode=1&userName=&goURL=login.html&action=login HTTP/1.1 |
此时可以得到一个有效的登录SESSION_ID

| 3 | 漏洞分析
我们在cgi文件中对system/sprintf等危险函数进行搜索,便可以找到几个可能可以控制参数的利用点
CVE-2022-41525
在函数sub_421C98中可以看到

从POST请求中的hostName字段未经过检查,被拼接进入了doSystem
我们沿着调用链向上找,可以找出唯一的调用链
sub_42B9F8–>sub_422F3C–>sub_421C98
但是sub_42B9F8找不到交叉引用,我们还是可以通过objdump/xxd查看它在elf中何处出现这个函数地址
1 | zer00ne@zer00ne:~/Desktop/cve_review/stage2/TOTOLINK/_TOTOLINK_C834FR-1C_NR1800X_IP04469_MT7621A_SPI_16M256M_V9.1.0u.6279_B20210910_ALL.web.extracted/squashfs-root/www/cgi-bin$ xxd ./cstecgi.cgi | grep 'f8b9 42' |
我们定位回elf中可以看到
1 | .data:0044B110 aSetopmodecfg: .ascii "setOpModeCfg"<0> |
这个函数与一个字符串对应
回到cgi的登录逻辑,我们可以发现登录检查函数与sub_42B9F8相似,也是函数指针与一个字符串对应,且对应的地址还有符号
1 | .data:0044C034 key: .ascii "loginAuth"<0> # DATA XREF: LOAD:004007F4↑o |
我们查看这个符号在什么地方被引用,可以找到这是在main函数中的一段逻辑
1 | Var = websGetVar(v17, "topicurl", (int)""); |
这是个根据topicurl的字段,选择调用不同的函数
所以我们要在这里将topicurl字段注入setOpModeCfg便可以调用到sub_42B9F8

之后无论如何都可以调用到sub_422F3C

最后进入sub_421C98,简单逆向分析可以知道,
应该把proto设置为1,2或5
switchOpMode设置为1
hostName设置为';{command};'(绕过单引号),便可以注入命令
EXP
1 | import requests |
成功RCE

CVE-2022-41518
与上个cve相似,也是一处命令拼接无检查导致RCE
1 | .data:0044C188 aUploadfirmware:.ascii "UploadFirmwareFile"<0> |

没有任何检查,把上个EXP改改就能用
EXP
1 | import requests |

| 4 | 后记–如何快速定位webserver文件
启动脚本
一般/etc/rc.d中S开头的启动脚本脚本,会按S后数字顺序执行的
可以在脚本中找到启动的web服务文件
通过表单搜索
我们知道客户端请求会由webserver传递给cgi处理
那我们就可以先在www目录下寻找任意提交表单
1 | <form method="POST" ref="loginfrm" @keyup.enter.prevent @keydown.enter.prevent> |
我们就定位到了后端处理程序cgi
cgi是由httpd在fork后exec调用的,所有httpd中有着对cgi文件的引用
我们在文件系统根目录中搜索所有对cgi文件的引用,一般就可以找到webserver文件
1 | grep -r "cstecgi.cgi" . |