nervous-inhuman / tplink-tapo-c200-re

Reverse Engineering the TP-Link Tapo C200 camera
96 stars 12 forks source link

Reversing web interface #2

Closed hacefresko closed 2 years ago

hacefresko commented 3 years ago

Hi there. I have been following the project since Christmas and some weeks ago I decided to go deeper and tried to get a shell by my own. I succeed, so first, thanks the information provided by all participants.

Now, I'm trying to figure out how the API in port 443 works. I supposed it was made to communicate with the mobile app, so I looked for a script to play with it and I found this one https://github.com/KusoKaihatsuSha/appgotapo. The next step was to look for the actual script running on the backend inside the camera, so I started reading the uhttpd config files. I quickly realized that the lua handler specified in the config files is not present in the camera:

root@SLP:/tmp# 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

Here is some usefull info about the running processes inside the camera:

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

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:/# uci show | grep luci
luci.main=core
luci.main.lang=auto
luci.main.mediaurlbase=/web-static
luci.main.resourcebase=/luci-static/resources
luci.flash_keep=extern
luci.flash_keep.uci=/etc/config/
luci.flash_keep.dropbear=/etc/dropbear/
luci.flash_keep.openvpn=/etc/openvpn/
luci.flash_keep.passwd=/etc/passwd
luci.flash_keep.opkg=/etc/opkg.conf
luci.flash_keep.firewall=/etc/firewall/firewall.user
luci.flash_keep.uploads=/lib/uci/upload/
luci.languages=internal
luci.sauth=internal
luci.sauth.sessionpath=/tmp/luci-sessions
luci.sauth.sessiontime=3600
luci.ccache=internal
luci.ccache.enable=1
luci.themes=internal
luci.themes.TORCHLIGHT=/luci-static/torchlight
ucitrack.@system[0].affects=luci_statistics
uhttpd.main.lua_prefix=/luci
uhttpd.main.lua_handler=/usr/lib/lua/luci/sgi/uhttpd.lua

I have been reading the uhttpd docs but I can't find where is the backend functionality. Any help would be great.

Thanks.

DrmnSamoLiu commented 3 years ago

It's because all of the stuff is handled right inside uhttpd binary, it's not using lua scripts at all. I think the uci entries you see are just leftovers of openwrt projects they used in other of their products. TP-link really love to use openwrt in their recent products.

Edit: I revisited uhttpd after seeing this issue, and found this piece of gem.... It's in a function called upload_conf so it may be useful or may not, I didn't really paid much attention to uhttpd before when we were doing the research🤔

uhttpd

hacefresko commented 3 years ago

So I see the way to go here is jumping into Ghidra and reverse the uhttpd binary, right? Although I have some experience with C programming, I'm new to Ghidra and I'm having issues with the language of the processor. The most reasonable decompilation I have achieved was with MIPS32, little endian, with mips16e, but there were some incorrect function calls (different number of parameters, non-sense returning values, etc.). Which is the language needed for this processor? Thanks so much

DrmnSamoLiu commented 3 years ago

@hacefresko I'm using the same language as yours. Ghidra is never known to have great decompiler, and people even speculate that NSA open sourced it because they can't improve it anymore and need community's help :p So do expect to see a lot of "weird" stuff in the decompiled code. Especially for a stripped binary like the uhttpd in our case.

The best approach is to try to grasp the basic logic of the program via decompiled code and if possible, do dynamic tests on the running device to prove your understanding.

Also, I might need to remind you that AFAIK, the most important interface for this cam is /bin/cet running on port 8800. Most of the APIs used by its mobile app communicates over port 8800.

hacefresko commented 3 years ago

Thank you very much. I have managed to get the login logic and some other interesting things such as the factory status password. I'm starting to get comfortable with Ghidra, which is great. I'll share any other interesting thing I find :D

Edit: I have continued doing some research and found some things that, in the end, I couldn't exploit but I will share anyway:

First, of all, I found what seems the default password for the factory status, although it may have some encryption layers: Screenshot_20210510_110046

It probably has to do something with this:

root@SLP:~# uci show | grep user
OSD_capability.font_info.color_type=auto user_defined
cloud_config.bind.username=hqMcgS4U/bkcM8QZQHT34NGANTSXH7XUV4OlbQeto5s=
function.module_spec.multi_user=0
luci.flash_keep.firewall=/etc/firewall/firewall.user
user_management.root=root
user_management.root.username=admin
user_management.root.passwd=874AAEB45AF77D9E0E0A17619C640F60
user_management.root.ciphertext=CHQTKowKXiEs2HKDRLmvbnFfGik/32xUU/a1LQjOV/cWvTStuHKtEqotDDg3KmvxF4rXeb4tibsyPfAR/2WRibpBm7g8QJjmewkljbMFFJ16B+7o88593eRmqkmgY0+EElGrsWBZoKLWfypF2Cyc8SIOFRVjZ76McS/LPqwn5So=
user_management.root.sharepwd=1604d5590be8e9336f73d641f6da0485
user_management.third_account=third_account
user_management.third_account.username=---
user_management.third_account.passwd=---
user_management.third_account.ciphertext=dl5GoIRk+FMC/JgP5yLjA+r8PynYYSai8DSmdv1Xw7iALNEKxkE5UusQw6BMC4+FlcWv0bCPuw8DSlSk/vkmcTZ/BF/ZY1ENNJqo+uJtiGi2f1zJFjleYhPlDx4YXa3qp7oSNF8EU1BU4mOY9nEtUakFl4oVPsvlLGM3qE/zI2k=
user_management.authentication=authentication
user_management.authentication.basic_enabled=0

But the only thing I found is that the passwd field is the md5 hash for the password.

Then, I found a function exec_and_read_json() used to execute commands in terminal and read the output as JSON. This function is vulnerable to command injection: Screenshot_20210510_110115

exec_and_read_json() is used in two other functions: Screenshot_20210510_110229 Screenshot_20210510_110241

However, although they are used in uh_slp_proto_request(), which looks like one of the main functions, they are called once the request has authenticated, so the input parameters to inject code are not reachable to a non authenticated user :(

Anyway, it was fun to finally have a look into reversing.

hacefresko commented 2 years ago

Hey! Just wanted to share that I ended up finding a critical vulnerability in uhttpd. This is a post I wrote about it in case someone is interested: https://hacefresko.github.io/posts/tp-link-tapo-c200-unauthenticated-rce

nervous-inhuman commented 2 years ago

Hey! Just wanted to share that I ended up finding a critical vulnerability in uhttpd. This is a post I wrote about it in case someone is interested: https://hacefresko.github.io/posts/tp-link-tapo-c200-unauthenticated-rce

You have no idea how happy this makes me!

I'm glad that you found the repo useful, and thanks to all the other people who've contributed. I didn't do much research myself, but it gives me hope for more things in the future.

Thank you!

DrmnSamoLiu commented 2 years ago

Very nice job and congratz for your first CVE! I'm really surprised that calling this method doesn't check for stok in the URL as I thought checking for stok is standard process for openwrt uhttpd. I tried to reproduce this vuln on my C200v1 running 1.0.10 Build 200520 Rel.45325n(5553) FW, but unfortunately did not succeed.

So just to make sure, the json object should look something like this: { "method": "setLanguage", "params": {"payload": ";touch /tmp/pwn.txt;"}} and the http method should be POST,am I correct?

Edit:

Ah ok I see I missed single quotes and now it works, thanks :)

SamDecrock commented 1 year ago

Hi, I wonder do you know how the ciphertext is calculated? If not, I'm gonna decompile the Android app try to figure it out. Nevermind: it looks like you can just re-use any ciphertext.