Sapido RB-1732路由器命令执行漏洞
0x01 漏洞介绍
Sapido 是 SAPIDO 公司开发的一款家用路由器,其 RB-1732 系列 v2.0.43 之前的固件版本存在一处命令执行漏洞。所谓命令执行漏洞,是指服务器没有对执行的命令进行过滤,导致用户可以执行任意的系统命令。命令执行漏洞属于高危的漏洞。该漏洞产生的原因是,服务器的 syscmd.asp 页面没有对传递过来的参数进行过滤,这使得用户以参数的形式将系统命令发送给服务器,并在服务器上执行。
0x02 漏洞分析
固件获取
本次分析使用的固件版本为:RB-1732_TC_v2.0.43
固件下载地址:
百度云:https://pan.baidu.com/s/1Gj9RDlAQdCDiaLdLzQ2Aag?pwd=8381
微云:https://share.weiyun.com/RjicYRKE
环境配置
本次分析使用的环境是AttifyOS,一个专门用于物联网渗透测试和分析的镜像,里边预装了许多工具,省去了自己部署环境的时间和精力。可通过Github仓库里的下载链接下载。
漏洞分析
将下载得到的固件放入虚拟机中,使用 binwalk 提取文件系统
可以看到,该固件使用的是 SquashFS 文件系统,也是使用相对比较多的一种文件系统。
在漏洞介绍中,我们提到,这个系列的路由器漏洞存在命令执行漏洞,原因出在这个 syscmd.asp 页面上。所以,我们先用 find 命令找到 syscmd.asp 文件的位置。
1 | iot@attifyos ~/D/f/_/squashfs-root> find ./ -name "syscmd.asp" |
可以发现,syscmd.asp 文件位于 web 目录下,我们进入该目录,打开 syscmd.asp 文件进行分析。查看该文件的源码如下:
1 | <html> |
我们可以看到第29行处,form 表单中的 action 指向了 /goform/formSysCmd。第37行处的 input 框的 name 属性为 sysCmd,这个在后边我们会看到他的作用,他其实就是传递我们发送的命令的参数名字。接着我们跟进 formSysCmd 文件,看看他对我们表单提交的数据如何处理。使用 grep 命令全局搜索 formSysCmd 字符串:
1 | iot@attifyos ~/D/f/_/squashfs-root> grep -r "formSysCmd" |
可以看到,除了 syscmd.asp 和 obama.asp 这两个页面程序,只有一个结果显示是包含在二进制应用程序 bin/webs 中,可以初步推测 webs 程序是真正的后台处理程序。 使用 file 查看下该文件:
1 | iot@attifyos ~/D/f/_/squashfs-root> file bin/webs |
可以看到,是 mips 架构 32 位程序。
我们把 webs 导入到IDA中进行静态分析,保持默认选项不动,点击 OK 。
在上方菜单栏,依次点击 view -> open subviews -> Strings 查看全部字符串,使用 ctrl+F 快捷键搜索 formSysCmd 字符串。
可以看到有两个位置存在该字符串,分别位于 0x004044DB 和 0x00471A44 这两个位置,我们依次查看。首先是 0x004044DB 位置,双击进入反汇编窗口后,如下所示:
再次双击红框处的 formSysCmd字符串,跳转到 formSysCmd 函数的真实位置。
(采用同样的方法我们查看 0x00471A44 位置处的字符串,可以看到两个地址上的内容相同。)
使用 F5 快捷键进行反编译查看 formSysCmd 函数的伪代码,如下:
1 | int __fastcall formSysCmd(int a1) |
可以看到,在23行处,v3 变量通过 websGetVar 函数获取sysCmd传递过来的内容。还记得吗?刚才提到过 sysCmd 这个字符串,他是 input 框的 name 属性的值。然后接下来,在 if 处,使用 snprintf函数将得到的结果进行拼接并赋值给 v20 变量。紧接着就调用 system 函数执行 v20 里边的内容。注意到,这里并没有对 v20 变量的内容做任何过滤,所以就是这里导致了命令执行漏洞。
Ps:这里有一个奇怪的地方就是,我用IDA反编译出来的 snprintf 函数参数列表里最后只有 v3 变量,而网上的博客里反编译出来的伪代码中,在 v3 之后还有一个 “/tmp/syscmd.log” 字符串,比较奇怪,不过影响不是特别大。我又尝试了使用Ghidra来反编译,如下图所示,可以看到Ghidra反编译出来,snprintf 函数参数列表最后是有字符串的,还是Ghidra对MIPS架构支持的比较好啊,当然也可能是我IDA没有额外装mips插件的原因。
这里补充下几个函数的定义,通过chatGPT查到的,不得不感叹下真的好用,比搜索引擎搜索关键词然后再翻博客效率高多了。
- websGetVar
websGetVar
函数是Web管理界面服务器WebMux
提供的API函数之一,用于从请求中获取指定名称的变量值,即从HTTP请求中提取参数值。其函数原型为:
1 | char_t *websGetVar(webs_t wp, char_t *name, char_t *defaultValue); |
函数参数的含义如下:
wp
:指向当前HTTP请求的webs_t
结构体指针。该结构体包含了请求相关的信息,如请求方法、URL、头部信息、请求体等等。name
:需要获取的变量名称,即需要提取的参数的名称。defaultValue
:当变量名称不存在时,返回的默认值。
该函数的返回值为字符串类型的变量值,即参数对应的值,如果变量不存在则返回默认值。需要注意的是,返回值是指向静态内存空间的指针,不要尝试释放该指针,也不要尝试修改其指向的内容。
下面是一个使用websGetVar
函数的例子:
1 | char_t *name = websGetVar(wp, "name", "unknown"); |
在上面的例子中,如果HTTP请求中包含名为 “name” 的参数,则函数将返回该参数的值,否则将返回 “unknown”。
- snprintf
snprintf
函数是C语言标准库中的一个函数,用于将格式化的字符串输出到指定的字符数组中,并且可以限制输出字符的数量,以防止溢出。其函数原型为:
1 | int snprintf(char *str, size_t size, const char *format, ...); |
函数参数的含义如下:
str
:目标字符数组的指针,即需要将格式化的字符串输出到该数组中。size
:目标字符数组的长度,即字符数组能够存储的最大字符数,包括末尾的空字符 ‘\0’,防止写入超过该长度的字符以防止缓冲区溢出。format
:格式化字符串,包含了输出的格式以及需要输出的变量值。...
:可变参数列表,即需要输出到格式化字符串中的变量值。格式化字符串中使用占位符(如 %d、%f 等)来表示变量值的类型和输出格式,可变参数列表中的值按照占位符的顺序输出到格式化字符串中。
该函数的返回值为实际写入字符数组的字符数(不包括末尾的空字符 ‘\0’)。如果实际写入的字符数超过了给定的最大字符数 size,则会在字符数组末尾添加空字符 ‘\0’,并返回应该写入字符数组的字符数(不包括末尾的空字符 ‘\0’)。如果出现其他错误,例如无法分配内存,则返回一个负数。
下面是一个使用snprintf函数的例子:
1 | char buffer[20]; |
0x03 漏洞复现
漏洞复现的第一步就是先在本地搭建固件模拟环境,这里使用FAT(Firmware Analysis Toolkit)搭建模拟环境。首先使用FAT将 bin 固件加载进来,模拟固件环境。
FAT在启动之后,会自动配置QEMU虚拟机以及网络环境,如上图,我们可以看到倒数第三行,bro 虚拟网卡的网络地址为 192.168.1.1 。我们按下ENTER键,稍等片刻,当看到下图所示内容时就表示环境已经部署好了。
此时,我们在浏览器中访问 192.168.1.1 这个地址后,会自动跳转到 /admin.asp ,显示路由器的管理页面
默认账号密码为 admin:admin,我们输入账号密码登录后,页面显示如下:
接下来,我们访问有漏洞的页面 /syscmd.asp ,如下图所示:
看到这个页面,再看我们之前看到的 syscmd.asp 的源码就容易理解多了。我们在 input 框输入命令 cat /etc/passwd
可以看到,命令成功执行。
我们可以抓包来看一下
可以看到 sysCmd 参数就是传递我们命令的参数,从第一行也可以看到,真正执行命令的后台程序为 /goform/formSysCmd 。感觉也是为我们分析漏洞多了一种思路。
我们也可以通过exploit脚本来攻击该漏洞:
1 | #rb1732_exploit.py |
执行结果如下:
1 | iot@attifyos ~/D/firmware> python rb1732_exploir.py 192.168.1.1 "cat /etc/passwd" |
0x04 参考链接
http://www.mchz.com.cn/cn/service/Safety-Lab/info_26_itemid_6142.html