资讯详情

TP-Link Tapo c200 未经身份验证的 RCE - Hacefresko

你好呀。今天,我想和大家分享我的第一个 CVE,它对应于几个月前TP-Link Tapo c200 相机中发现的命令注入漏洞,允许攻击者使用 root 完全控制设备的权限。它被 INCIBE 分配了 CVE-您可以在这里查看2021-4045的官方公告。该漏洞影响 1.1.16 Build 211209 Rel 之前的所有固件版本。37726N,所以如果你有这个模型,我建议你更新它。

Tapo_cam

本文将总结我对设备的研究及其如何导致漏洞的发现。这对我来说是对物联网和硬件黑客的介绍,也是对逆向工程的介绍,所以请不要难为我,因为可能会有一些错误。我也要感谢网络安全社区,因为每当遇到困难,总会有视频、文章或其他东西鼓励我学习或尝试新事物。最后,如果你能正确阅读,请记住失败是你最好的朋友 shell 命令的结果,有时它会让你花六个月的时间来完成你可以在一两个时间内完成的事情。

无论如何,让我们从文章开始。感谢您的阅读,希望您喜欢。

初步侦察

一旦我把相机从盒子里拿出来,我就开始用我的手机和我的手机 WiFi 配置它很容易。我开始玩它,我很惊讶 30 欧元的 IP 相机有这么多功能。它可以录制视频和音频 SD 卡几乎可以水平移动 360o,垂直移动 90o,音频甚至可以从手机应用程序中实时再现。

我最初的计划是扫描一些端口,试图找到一些漏洞,并检查它们 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/sbn/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扫描中看到的那些开放端口背后的进程,例如uhttpdcet。我特别关注这个uhttpd过程,因为它是服务器背后的那个https(当时我仍然认为它是http)而且我已经非常熟悉http协议了。

uhttpd是由 OpenWRT 制作的 Web 服务器,用于运行此发行版的嵌入式设备。在这一点上,我想知道是否可以获得更多关于它的信息,比如源代码,或者至少是路线。我去了OpenWRT wiki并大致了解了uhttpdOpenWRT。在 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_loginuh_slp_proto_request。稍后我将详细讨论最后一个。

初次接触后,我开始寻找错误。由于我是一个有溢出漏洞的大菜鸟,我做的第一件事就是搜索和system调用,以检查是否有任何我可以轻松利用的命令注入漏洞。哦,男孩,我很幸运。exec``popen

函数exec_and_read_json用于popen执行命令:

exec_and_read_json由 2 个未命名的函数使用,我将其命名为set_languagewifi_connect。它们分别处理语言和 WiFi 配置(显然)。wifi_connect似乎解析单引号('),但是,set_language没有。这意味着如果我们可以控制set_language函数的输入,我们就可以成功地注入我们自己的命令。

函数set_languageuh_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_languagelanguage_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. 如果请求有效(不超过最大长度、使用httphttps取决于服务器配置等),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. 这似乎微不足道,但我无法让它发挥作用。我发现netcatBusyBox 中安装的版本在功能方面非常有限,因此常规的反向 shell 无效。但是,我在PayloadsAllTheThings存储库中找到了我想要的东西(像往常一样),并得到了完美的反向 shell。由于uhttpd以 root 身份运行(感谢 TP-Link),我们只需发送 POST 请求即可获得具有最高权限的 shell。所以,这是最终的 PoC:

标签: 电容rlx能代替rls

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台