作为一个曾经的Misc手,转去学Pwn也快一年了,博客也咕了好久,大概最近会总结下二进制的东西。。。
二进制是真的难呜呜呜,学Misc它不香吗?

去年XMAN夏令营买的的TOTOLINK T6,积灰快一年了,拿出来学习一下路由器固件漏洞挖掘
以下内容来源于XMAN夏令营IOT课程学习

事先准备

设备:一台TOTOLINK T6
固件:T6 V3_固件_V4.1.5cu.709_B20210518.zip

固件解包

推荐直接下个Kali2022,里面的binwalk功能很全
解压zip,用binwalk提取固件

//提取固件
binwalk -Me TOTOLINK_C818XR_T6_IP04338_8197FNT64_SPI_8M64M_V4.1.9cu.5241_B20210923_ALL.web
//识别架构
binwalk -A TOTOLINK_C8193R-1C_T6_V3_IP04447_8197F_SPI_8M64M_V4.1.5cu.709_B20210518_ALL.web

squashfs-root目录即为路由器文件系统的根目录
进入bin目录,file busybox,可以发现是32位小端序的mips程序,这样就得到了路由器文件系统的架构

未授权开启Telnet分析

在web_cste目录下可以找到telnet.html
访问192.168.0.1登录路由器,默认密码admin,登录后可访问telnet.html,是一个用来开关Telnet的网页
利用burp抓包开启的请求,发现即使cookie删掉也可以开启Telnet,即一个未授权开启Telnet的漏洞
python的poc如下:

import requests
requests.post("http://192.168.0.1/cgi-bin/cstecgi.cgi",data='{"telnet_enabled":"1","topicurl":"setTelnetCfg"}')

关于T6的telnet默认密码,可以从/etc/init.d/rcS文件找起,相关代码如下:

#for console login
hostname `flash get HOST_NAME | cut -f2 -d= | sed 's/\"//g'`
cp /etc/shadow.sample /var/shadow
cp /etc/passwd.sample /var/passwd
#cp /etc/vsftpd.conf /var/config/vsftpd.conf
cs password

查看shadow.sample文件可以发现密码,拿去cmd5解密一下可知是cs2012,但试了一下可以发现并不能登录
此时可以发现底下还调用了cs程序处理password,cs程序并不是常见的linux命令,很有可能是厂商自己编写的程序
IDA打开shift+F12,然后搜索字符串password,可以找到Telnet的默认密码为KL@UHeZ0,成功登录Telnet


此时我们就有了路由器的一个shell,对于调试,寻找漏洞,分析服务会方便许多
因为路由器自带的busybox功能不是很全,所以我们要上传一个busybox
从https://busybox.net/downloads/binaries/1.21.1/ 找到对应架构的busybox(busybox-mipsel),下载到本地,利用python搭一个web服务用于传输文件

#python2
python -m SimpleHTTPServer 8000

#python 3
python -m http.server 8000

在路由器的/tmp文件夹下用wget下载,因为路由器一般只有/tmp是可写的,其他均为只读文件

//ip可以通过cmd执行ipconfig查看自己本机ip
wget http://192.168.0.2:8000/busybox-mipsel

这边插个小技巧:
当我们需要修改非/tmp目录下的文件或文件夹时,可以利用mount实现

//挂载目录
cd /tmp
mkdir /tmp/tmplib
cp -r /usr/lib/* /tmp/tmplib/
mount -o loop /tmp/tmplib/ /usr/lib/
//挂载文件
mount -o bind /tmp/libxaudio_engine.so /usr/lib/libxaudio_engine.so

技巧:mount直接覆盖文件系统路径
要求:tmp目录足够大,可用df –h查看

言归正传,上传了busybox后,可以通过netstat -pantu来查看路由器开启TCP和UDP服务等信息,便于分析

可以看到占用80端口的是lighttpd程序,但是我们开启telnet时发包的目标是/cgi-bin/cstecgi.cgi
这是因为lighttpd负责解析一些浏览器请求,然后将参数通过环境变量等方式传递给/cgi-bin/cstecgi.cgi并启动来实现一些功能
可以从/cgi-bin/cstecgi.cgi的main函数中看到获取环境变量的函数

QUERY_STRING:请求串,比如https://example.com/over/there?name=ferret的请求串就是name=ferret
CONTENT-LENGTH:指明发送给接受方的消息主体的大小

因为发包发给的是/cgi-bin/cstecgi.cgi,自然要看一看里面处理telnet的代码,还是用查找字符串的方式找到相关函数

显然var接收了我们发过去的telnet_enabled的值,然后传给v2,v2在经过格式化为json格式后传给v4
接着调用了safe_cs_pub函数,可以看出跟开启telnet是有一定关联的,那么我们看一下这个函数都干了什么
双击点进函数其实可以发现他是函数表上的一个函数,显然是动态链接进来的,真正的代码并不在这个程序里,那么要怎么找呢?

很简单,直接目录底下字符串搜索即可

grep -ir safe_cs_pub 2>/dev/null

可以找到/lib/libmystdlib.so这么一个文件,IDA反编译一下

这里我们根据IDA反编译出的代码在shell上模拟关闭Telnet时执行的命令:

//源代码是在/tmp目录下新建一个临时文件写入消息后,利用-f参数导入,最后调用rm删除,这里直接用-m传消息
cste_pub -h 127.0.0.1 -t totolink/router/setTelnetCfg -m "{\"telnet_enabled\":\"0\"}"


可以看到成功关闭了Telnet服务,那么这条命令是如何做到关闭Telnet的呢

让我们来看看调用的cste_pub究竟是个什么程序,重新开启telnet,shell里运行一下cste_pub,发现其实是一个封装过的mosquitto_pub
百度一下可知是一款实现了消息推送协议 MQTT v3.1 的开源消息代理软件,端口是1883

简单来说就是一个提供一对多的消息发布功能的协议,MQTT消息的发布者和订阅者都是客户端,服务器只是作为一个中转的存在,将发布者发布的消息进行转发给所有订阅该主题的订阅者;发布者可以发布在其权限之内的所有主题,并且消息发布者可以同时是订阅者,实现了生产者与消费者的脱耦,发布的消息可以同时被多个订阅者订阅。

根据指令不难看出,其实这个程序就是个发布者,给totolink/router/setTelnetCfg这个主题发送了一条内容为{"telnet_enabled":"0"}的消息,订阅者收到消息后根据telnet_enabled的值将Telnet服务开启或关闭,那么又发现一种开启Telnet的办法:利用MQTT发布开启Telnet的消息,python脚本如下:

import paho.mqtt.client as mqtt

client = mqtt.Client()
client.connect("192.168.0.1",1883,60) # host,port,keepalive
client.publish("totolink/router/setTelnetCfg", b'{"telnet_enabled":"1"}')

看样子开启Telnet的思路理清了,但是真正开启Telnet的代码并没有看见,如果去订阅者程序cste_sub搜字符串会发现并没有telnet
但这并不代表cste_sub与开启Telnet无关,跟前面一样,程序是可以通过动态链接调用so里的函数的,那么搜一下可以发现/lib/cste_modules/wireless.so文件

可以在里面的函数找到真正开启Telnet的代码

如果查一下wireless.so字符串会发现没有一个程序里有这个字符串,实际上so库还可以通过dlopen函数加载,即通过目录加载,在cste_sub程序中找到

至此,启动Telnet这一过程彻底理清了,下面附上一张XMAN讲师@xuanxuanblingbling总结的图:

剩余内容待更新...


现在、你眼中看到了什么?