简介
前段时间 TP-LINK TL-WR841N 设备爆出了一个认证后的栈溢出漏洞,借机复现了一下这个栈溢出漏洞,其中有一些在漏洞利用上的小技巧在此和大家分享一下。
漏洞信息如下:
漏洞编号:CVE-2020-8423
漏洞设备:TP-LINK TL-WR841N V10
漏洞效果:登陆过路由器web服务admin账户之后可以获取到路由器的shell。
漏洞描述:httpd获取参数时的栈溢出导致了覆盖返回地址shellcode执行
受影响设备以及版本信息:
cpe:2.3:o:tp-link:tl-wr841n_firmware:3.16.9:*:*:*:*:*:*:*
cpe:2.3:h:tp-link:tl-wr841n:v10:*:*:*:*:*:*:*
环境搭建
下载固件:https://www.tp-link.com/no/support/download/tl-wr841n/v10/
binwalk -Me xxx.bin命令对固件进行解压。
为了成功运行环境,必须hook 一些关键函数。编译hook函数,hook掉httpd文件里面的阻塞函数。
参考文章中,实际上只需要hook 掉fork和system函数:
#include
#include
int system(const char *command){
printf("HOOK: system(\"%s\")",command);
return 1337;
}
int fork(void){
return 1337;
}
编译:
mips-linux-gnu-gcc -shared -fPIC hook_mips.c -o hook_mips
运行 qemu 环境
启动 qemu 虚拟机之后,在里面运行,便成功启动调试环境。
mount --bind /proc squashfs-root/proc
chroot . bin/sh
LD_PRELOAD="/hook" /usr/bin/httpd
或者
export LD_PRELOAD="/hook"
./gdbserver 0.0.0.0:2333 /usr/bin/httpd
这里可能会出现一些报错:如没有 libc.so.6 或者 没有ld.so.1,解决方法是需要创建 lib 目录下对应的软连接,ln -s ld-uClibc-0.9.30.so ld.so.1,如图
搭建成功之后访问IP地址即可
如果是远程,没有界面可以使用ssh端口转发
漏洞分析
得到文件系统之后,在 /usr/bin/httpd 二进制文件中,找到了这个函数:
int stringModify(char *dest, int len, int src)
{
char src_index;
char *src_index_a_1;
int index;
if ((dest == (char *)0x0) || (src_index_a_1 = (char *)(src + 1), src == 0))
{
index = -1;
}
else {
index = 0;
while (true) {
src_index = src_index_a_1[-1];
if ((src_index == '\0') || (len <= index)) break;/* src为空或当index等于长度时结束 */
if (src_index == '/') { //处理 /
LAB_0043bb48:
*dest = '\\';
LAB_0043bb4c:
index = index + 1;
dest = dest + 1;/* /添加转义字符,变为\/ */
LAB_0043bb54:
*dest = src_index_a_1[-1];
dest = dest + 1;
}
else { //处理其他字符
if ('/' < src_index) {//左斜杠为2F小于0的ascii (处理数字和字母)
if ((src_index == '>') || (src_index == '\\'))
goto LAB_0043bb48;
if (src_index == '
*dest = '\\';
goto LAB_0043bb4c; //>,
\>>,\<
}
goto LAB_0043bb54;
} //下面是ascii小于x2f的字符
if (src_index != '\r') {//\r的ascii为DH (处理\r,\",\n)
if (src_index == '\"') goto LAB_0043bb48;//22h
if (src_index != '\n') goto LAB_0043bb54;//AH
}
if ((*src_index_a_1 != '\r') && (*src_index_a_1 !=
'\n')) {//处理前一个为\r或\n 后一个不是\r或\n 的组