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

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 卡中,它可以水平移动几乎 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 转换器来连接它:

USB_to_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)] final_device [外链图片转存中…(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扫描中看到的那些开放端口背后的进程,例如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

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

wifi_connect

设置语言

函数set_languageuh_slp_proto_request我之前提到的函数使用,它将从用户接收到的一些解析数据作为输入传递。

main_func_1

main_func_2

要解析用户数据,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_entrypoint

通过猜测并向相机发送几个请求,我们可以检查所使用的数据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:

poc_code

poc

今天的文章TP-Link Tapo c200 未经身份验证的 RCE – Hacefresko分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/61447.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注