你好呀。今天我想与大家分享我的第一个 CVE,它对应于几个月前在TP-Link Tapo c200 摄像头中发现的命令注入漏洞,它允许攻击者以 root 权限完全控制设备。它被 INCIBE 分配了 CVE-2021-4045,你可以在这里查看官方公告。该漏洞影响 1.1.16 Build 211209 Rel 之前的所有固件版本。37726N,所以如果你拥有这个模型,我建议你更新它。
这篇文章将总结我对该设备的研究以及它如何导致该漏洞的发现。这对我来说是对物联网和硬件黑客的介绍,也是对逆向工程的介绍,所以请不要难为我,因为可能会有一些错误。我还要感谢网络安全社区,因为每当我遇到困难时,总会有视频、文章或其他东西激励我学习或尝试新事物。最后,请记住,失败是你最好的朋友,尽管如果你能正确阅读 shell 命令的结果,有时它会让你花六个月的时间来完成你可以在一两个时间内完成的事情。
无论如何,让我们从文章开始。感谢您的阅读,希望您喜欢。
初步侦察
一旦我把相机从盒子里拿出来,我就开始用我的手机和我的 WiFi 来配置它。这很容易做到。我开始玩它,我很惊讶 30 欧元的 IP 摄像机有这么多功能。它可以将视频和音频录制到 SD 卡中,它可以水平移动几乎 360º,垂直移动 90º,甚至可以从手机应用程序实时再现音频。
我最初的计划只是扫描一些端口并尝试找到一些漏洞,并检查 WiFi 配置是如何工作的,因为当时我对 WiFi 安全非常感兴趣。我不会谈论 WiFi 配置,因为在使用自定义hostapd
+dnsmasq
接入点和分析它之后Wireshark
,除了创建无线接入点或连接到一个无线接入点等常见功能之外,我没有发现任何有趣的东西。
那么,让我们谈谈端口扫描:
$ nmap -sV -p- 192.168.1.81
Nmap scan report for C200_F81AB2.home (192.168.1.81)
Host is up (0.022s latency).
Not shown: 996 closed ports
PORT STATE SERVICE
443/tcp open https
554/tcp open rtsp
2020/tcp open xinupageserver
8800/tcp open sunwebadmin
如您所见,该设备有一些有趣的开放端口。我测试的第一件事是端口 443。虽然nmap
清楚地表明它使用了https
,但当我最初进行扫描时,我只是忽略了它,并花了相当长的时间认为端口 443 正在运行http
。正因为如此,我只测试http://192.168.1.81:443
而不是https://192.168.1.81:443
,所以我得到的唯一结果是 400 个响应。正如我在介绍中所说,这段旅程充满了失败。关于其他端口,我完全不知道在它们上运行的服务,也没有找到任何关于它们的明确信息。此刻,我已经用完了已知的选项,所以是时候进一步调查了。
得到一个外壳
在购买相机之前,我在网上查找了有关该设备的先前研究,幸运的是,我找到了这个Github 存储库,人们正在合作对其进行逆向工程。其中一个问题解释了如何通过UART 端口获取 shell ,当时我对此一无所知。所以我学习了基础知识并购买了一个USB 到 TTL 转换器来连接它:
在上述问题的帮助下,我能够用刀和螺丝刀打开设备并快速找到 UART。经过几次尝试和极大的耐心,我终于设法将一些电线焊接到焊盘上:
[外链图片转存中…(img-47tykPWi-1647393894241)]
然后,是时候测试焊接是否足以传输数据了。我将电线连接到 USB 适配器,考虑到 UART 的 Rx 连接到适配器的 Tx,反之亦然,并将适配器连接到我的计算机。同样,由于提到的问题,我知道串行连接的波特率为 57600,所以我执行了$ sudo screen /dev/tty.usbserial-0001 57600
,/dev/tty.usbserial-0001
适配器连接的 USB 端口在哪里,并打开了设备电源。我立即开始接收数据,太好了。
但是,我还没有壳。我收到的只是设备的启动顺序,实际上是 U-Boot 引导加载程序。它看起来像这样:
U-Boot 2014.01-v1.2 (Jul 16 2021 - 18:41:10)
Board: IPCAM RTS3903 CPU: 500M :rx5281 prid=0xdc02
force spi nor mode
DRAM: 64 MiB @ 1066 MHz
Skipping flash_init
Flash: 0 Bytes
flash status is 0, 0, 0
SF: Detected XM25QH64A with page size 256 Bytes, erase size 64 KiB, total 8 MiB
Using default environment
Autobooting in 1 seconds
copying flash to 0x81500000
flash status is 0, 0, 0
SF: Detected XM25QH64A with page size 256 Bytes, erase size 64 KiB, total 8 MiB
SF: 8388608 bytes @ 0x0 Read: OK
[...]
通过输入 enter,我们被要求输入用户名和密码。再次感谢Github 问题,我们知道凭据,因此我们可以使用用户名root
和密码成功登录,slprealtek
并最终获得一个 shell。
一旦我知道连接有效,我需要使焊接不那么脆弱,因为它在实际获得外壳的过程中断裂了两次。我使用了一些热熔硅胶来固定所有电线并再次关闭设备,断开所有电机。现在,我的测试单元已准备就绪:
[外链图片转存中…(img-KQa3LDFM-1647393894242)] [外链图片转存中…(img-Eusqbwy0-1647393894243)]
探索设备
现在我们有了一个 shell,让我们来探索一下这个设备:
root@SLP:~# uname -a
Linux SLP 3.10.27 #1 PREEMPT Wed Nov 11 20:42:05 CST 2020 rlx GNU/Linux
root@SLP:~# cat /etc/openwrt_version
12.09-rc1
正如我们所见,这是一台运行 Linux 3.10.27 的 OpenWRT 机器。现在让我们检查活动进程和打开的端口:
root@SLP:~# ps
PID USER VSZ STAT COMMAND
1 root 2328 S init
2 root 0 SW [kthreadd]
3 root 0 SW [ksoftirqd/0]
4 root 0 SW [kworker/0:0]
5 root 0 SW< [kworker/0:0H]
6 root 0 SW [kworker/u2:0]
7 root 0 SW [rcu_preempt]
8 root 0 SW [rcu_bh]
9 root 0 SW [rcu_sched]
10 root 0 SW< [khelper]
11 root 0 SW< [writeback]
12 root 0 SW< [bioset]
13 root 0 SW< [kblockd]
14 root 0 SW [khubd]
15 root 0 SW [kworker/0:1]
16 root 0 SW [kswapd0]
17 root 0 SW [fsnotify_mark]
18 root 0 SW< [crypto]
27 root 0 SW [kworker/u2:1]
46 root 0 SW< [deferwq]
47 root 0 SW< [kworker/0:1H]
247 root 2328 S -ash
262 root 0 SW [irq/27-gpio res]
273 root 0 SW< [cryptodev_queue]
282 root 860 S /sbin/hotplug2 --override --persistent --set-rules-f
304 root 888 S /sbin/ubusd
325 root 8152 S tp_manage
357 root 3416 S /usr/bin/ledd
361 root 3408 S /sbin/msglogd
367 root 3220 S /usr/sbin/netlinkd
370 root 5468 S < /usr/bin/system_state_audio
379 root 10180 S /usr/sbin/wlan-manager
491 root 1636 S /sbin/netifd
492 root 1520 S /usr/sbin/connModed
494 root 11488 S /usr/bin/dsd
496 root 1532 S /usr/sbin/connModed
502 root 7640 S /bin/cloud-service
520 root 4360 S /bin/cloud-brd -c /var/etc/cloud_brd_conf
653 root 15020 S /bin/cloud-client
830 root 2320 S /usr/sbin/telnetd -b 127.0.0.1
861 root 3852 S /usr/sbin/uhttpd -f -h /www -T 180 -A 0 -n 8 -R -r C
870 root 6048 S /usr/bin/relayd
872 root 5948 S /usr/bin/rtspd
879 root 4612 S /usr/bin/p2pd
884 root 11152 S /bin/dn_switch
889 root 4180 S /bin/storage_manager
920 root 40940 S /bin/cet
956 root 32336 S /bin/vda
960 root 3808 S /bin/wtd
970 root 11288 S /bin/nvid
1019 root 2332 S udhcpc -p /var/run/static-dhcpc.pid -s /lib/netifd/s
1037 root 0 SW [RTW_CMD_THREAD]
1059 root 1212 S wpa_supplicant -B -Dwext -iwlan0 -P/tmp/supplicant_p
1089 root 2332 S /usr/sbin/ntpd -n -p time.nist.gov -p 133.100.9.2 -p
1103 root 3840 S /usr/bin/motord
1447 root 2324 R ps
root@SLP:~# netstat -natpu
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:8800 0.0.0.0:* LISTEN 920/cet
tcp 0 0 127.0.0.1:929 0.0.0.0:* LISTEN 875/p2pd
tcp 0 0 0.0.0.0:20002 0.0.0.0:* LISTEN 325/tp_manage
tcp 0 0 0.0.0.0:2020 0.0.0.0:* LISTEN 969/nvid
tcp 0 0 0.0.0.0:554 0.0.0.0:* LISTEN 920/cet
tcp 0 0 127.0.0.1:23 0.0.0.0:* LISTEN 832/telnetd
tcp 0 0 127.0.0.1:921 0.0.0.0:* LISTEN 878/relayd
tcp 0 0 127.0.0.1:922 0.0.0.0:* LISTEN 877/rtspd
tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 863/uhttpd
tcp 0 0 192.168.1.80:37380 52.19.66.90:443 ESTABLISHED 507/cloud-brd
udp 0 0 0.0.0.0:20002 0.0.0.0:* 325/tp_manage
udp 0 0 0.0.0.0:38000 0.0.0.0:* 1087/ntpd
udp 0 0 0.0.0.0:3702 0.0.0.0:* 969/nvid
我们可以看到nmap
扫描中看到的那些开放端口背后的进程,例如uhttpd
或cet
。我特别关注这个uhttpd
过程,因为它是服务器背后的那个https
(当时我仍然认为它是http
)而且我已经非常熟悉http
协议了。
uhttpd
是由 OpenWRT 制作的 Web 服务器,用于运行此发行版的嵌入式设备。在这一点上,我想知道是否可以获得更多关于它的信息,比如源代码,或者至少是路线。我去了OpenWRT wiki并大致了解了uhttpd
OpenWRT。在 OpenWRT 机器中,有一个称为统一配置接口(UCI)的系统,它基本上是用来轻松配置系统服务的。使用它,我们可以获得uhttpd
配置:
root@SLP:~# uci show | grep uhttpd
ucitrack.@uhttpd[0]=uhttpd
ucitrack.@uhttpd[0].init=uhttpd
uhttpd.main=uhttpd
uhttpd.main.listen_https=443
uhttpd.main.home=/www
uhttpd.main.rfc1918_filter=1
uhttpd.main.max_requests=8
uhttpd.main.cert=/tmp/uhttpd.crt
uhttpd.main.key=/tmp/uhttpd.key
uhttpd.main.cgi_prefix=/cgi-bin
uhttpd.main.lua_prefix=/luci
uhttpd.main.lua_handler=/usr/lib/lua/luci/sgi/uhttpd.lua
uhttpd.main.script_timeout=180
uhttpd.main.network_timeout=180
uhttpd.main.tcp_keepalive=0
uhttpd.px5g=cert
uhttpd.px5g.days=3600
uhttpd.px5g.bits=1024
uhttpd.px5g.country=CN
uhttpd.px5g.state=China
uhttpd.px5g.location=China
uhttpd.px5g.commonname=TP-Link
upnpc.uhttpd=entry
upnpc.uhttpd.proto=TCP
upnpc.uhttpd.ext_port=80
upnpc.uhttpd.desc=uhttpd
这里有一些有趣的参数。首先,uhttpd.main.home
指向服务器文档根目录,所以我们可能会找到一些网络服务器文件。接下来,uhttpd.main.lua_handler
指向用于在服务器启动时初始化 Lua 运行时的 Lua 处理程序脚本,因为uhttpd
支持 Lua 脚本,所以那里可能有更多有趣的文件。但是,/www
目录是空的,系统中也没有sgi
目录,/usr/lib/lua/luci
也没有uhttpd.lua
文件。我一直试图找到有关此uhttpd
实例如何工作的任何信息,但我什么也没找到,只有配置参数指向无处。
在这一点上,我知道解决这个问题的方法是直接分析uhttpd
二进制文件并对其进行逆向工程,但在此之前,我想创建一个测试环境,以便在我发出请求时知道 Web 服务器内部发生了什么,由于创建过程的方式,任何地方都没有输出。
我尝试运行ps
进程 861: 的命令输出中的命令/usr/sbin/uhttpd -f -h /www -T 180 -A 0 -n 8 -R -r C
。但是,我遇到了很多错误,无法使其正常工作。由于我无法uhttpd
在不同的端口中创建相同的进程,因此我尝试通过检查/proc
进程的条目来查找丢失的输出,以便尝试读取它们是否存在(如PwnFunction 的此视频中所述)。但是有一个大问题:
root@SLP:~#sudo ls -l /proc/864/fd/
lrwx------ 1 root root 64 Nov 10 22:44 0 -> /dev/null
lrwx------ 1 root root 64 Nov 10 22:44 1 -> /dev/null
lrwx------ 1 root root 64 Nov 10 22:44 2 -> /dev/null
lrwx------ 1 root root 64 Nov 10 22:44 3 -> anon_inode:[eventpoll]
lrwx------ 1 root root 64 Nov 10 22:44 4 -> socket:[1830]
和的所有文件描述符stdin
都被重定向到,这基本上是将它们重定向到无法找到它们的黑洞。我被困住了,我不知道该怎么办。由于我已经在条目中,因此我开始四处寻找,因为我不记得条目包含关于流程的那么多信息并且对此感到好奇。由于这种随机的好奇心,我偶然发现了该条目,其中包含该过程的所有环境变量。这些环境变量之一是. 我立即意识到显示的命令stdout``stderr``/dev/null``/proc``/proc``environ``UHTTPD_ARGS=-h /www -T 180 -A 0 -n 8 -R -r C200 -C /tmp/uhttpd.crt -K /tmp/uhttpd.key -s 443``ps
不正确,后来发现是因为UART shell没有足够的宽度来显示所有字符。还有一个失败让我学到了新的重要东西:永远不要相信 UART 端口提供的输出。
uhttpd
现在我终于可以创建另一个具有完全相同参数且没有管道的实例,/dev/null
以便在逆向工程时测试二进制文件。
使用 Ghidra 对 uhttpd 进行逆向工程
这是我第一次使用 Ghidra。我看过一些视频并阅读了一些关于它的文章(感谢stacksmashing和liveoverflow提供了令人惊叹且易于消化的内容),但从未真正玩过它,所以这是一个很好的学习机会。
我打开uhttpd
二进制文件,经过反复试验,发现语言是 MIPS32,小端,带有 mips16e。一些函数名默认随二进制文件一起提供,而另一些则没有。我还花了一些时间重命名函数,因为显然,Ghidra 通常与外部函数混淆,并且您会为它们得到奇怪的包装器,例如:
我查看了main
函数和其他重要的函数,以找出二进制文件的逻辑及其结构。我发现了一些有趣的,已经命名的,其中是do_login
或uh_slp_proto_request
。稍后我将详细讨论最后一个。
初次接触后,我开始寻找错误。由于我是一个有溢出漏洞的大菜鸟,我做的第一件事就是搜索和system
调用,以检查是否有任何我可以轻松利用的命令注入漏洞。哦,男孩,我很幸运。exec``popen
函数exec_and_read_json
用于popen
执行命令:
exec_and_read_json
由 2 个未命名的函数使用,我将其命名为set_language
和wifi_connect
。它们分别处理语言和 WiFi 配置(显然)。wifi_connect
似乎解析单引号('
),但是,set_language
没有。这意味着如果我们可以控制set_language
函数的输入,我们就可以成功地注入我们自己的命令。
函数set_language
由uh_slp_proto_request
我之前提到的函数使用,它将从用户接收到的一些解析数据作为输入传递。
要解析用户数据,uh_slp_proto_request
请检查它是否是有效的 JSON 对象。然后,它得到一个由 key 标识的字符串值"method"
和一个由 key 标识的字典值"params"
(至少我是这么认为的,因为 Ghidra 无法解析函数调用,但似乎以这种方式工作)。根据选择的方法,uh_slp_proto_request
选择将被调用的函数。
因此,通过发送,{"method": "setLanguage", "params":{}}
我们成功调用了set_language
函数并{}
作为language_json
参数传递。然后,在内部set_language
,language_json
对象被转换为字符串并直接插入"ubus call system_state_audio set_language \'%s\'"
执行。
通过提交{"method": "setLanguage", "params": {"payload": "'; touch poc;'"}}
,ubus call system_state_audio set_language '{"payload": "'; touch poc;'"}'
将被执行,其中实际包含 3 个命令:ubus call system_state_audio set_language '{"payload": "'
, touch poc
, 和'"}'
. 第二个给我们完整的代码执行。
现在,uh_slp_proto_request
由另一个管理所有请求的未命名函数使用,我将其命名为main_server_function
. 如果请求有效(不超过最大长度、使用http
或https
取决于服务器配置等),main_server_function
则检查 URL 是否包含/cgi-bin/luci
或/web-static
。如果没有,uh_slp_proto_request
则调用。
通过猜测并向相机发送几个请求,我们可以检查所使用的数据uh_slp_proto_request
是否是常规 POST 数据。/
因此,如果我们使用先前的有效负载发送 POST 请求,uh_slp_proto_request
将处理此数据,调用set_language
并且我们的有效负载将被注入到执行的命令中exec_and_get_result
。
如您所见,我没有提到任何关于身份验证的内容,因为setLanguage
无需登录即可调用该方法。这允许任何用户只需一个未经身份验证的请求即可完全控制相机。
开发
现在,是时候编写漏洞利用程序了。我花了一些时间弄清楚如何使用netcat
. 这似乎微不足道,但我无法让它发挥作用。我发现netcat
BusyBox 中安装的版本在功能方面非常有限,因此常规的反向 shell 无效。但是,我在PayloadsAllTheThings存储库中找到了我想要的东西(像往常一样),并得到了完美的反向 shell。由于uhttpd
以 root 身份运行(感谢 TP-Link),我们只需发送 POST 请求即可获得具有最高权限的 shell。所以,这是最终的 PoC:
今天的文章TP-Link Tapo c200 未经身份验证的 RCE – Hacefresko分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/61447.html