Yeelight, the Bluetooth LED Bedside Lamp from Xiaomi that Spies on You, Part One
UPDATE (12/21/2017): The audio recording failures I noticed may actually be a result of Android trying to listen for “Ok Google”, and the emulator not having microphone capabilities. It looks like this is the case and it is not the Yeelight application attempting to record audio as soon as it is installed. I need to confirm this and I will research this more in Part Two.
NOTE: Before we get started here, I don’t have a rooted device in my possession today while working on this. As a result the issues I discovered were/are limited. For example, this application starts recording audio as soon as it is installed, but I’m unable to look at that data. If you have a rooted device in your possession, please go ahead and take a look. In fact, I encourage everyone to take a look at this application because of the permissions the application requests.
A bit of history: my wife wanted a bedside LED lamp that she could control from her phone, something she could turn off without having to get out of bed. Amazon recommends Yeelight from Xiaomi as adequate competitor of Philips Hue, the latter being the maker of the smart LED bulbs that you can use with Alexa.
In fact, a lot of the Yeelight products have great reviews on Amazon. My wife purchased the following device: https://www.amazon.com/YEELIGHT-XMCTD01YL-Dimmable-Changing-Bluetooth/dp/B01J9H0V30
Considering that I am a security researcher by day, I downloaded the apk to take a look. I have audited hundreds of Android applications over the last few years, and this is the one that I will always remember because of how sketchy it is. Remember, this is an Android application for a Bluethooth LED bedside lamp. There’s multiple tools one can use when auditing this type of application:
jadx-gui to decompille the apk. This generates java source code and reveals a lot of other useful information. It’s not perfect so you’ll end up with some classes that aren’t descriptive with names like a,b,c which make understanding the code a little difficult, and some code won’t decompile at all but this is a fantastic resource.
MobSF & qark are two great tools for auditing the apk and finding issues like exported activities with no permissions.
BurpSuite’s proxy to look at the network traffic of the application.
Android Studio to run the application on an emulator. Some applications have protection in place to prevent them running on an emulator, and I have had to edit the .smali code in the past to get around this. Thankfully this application didn’t have any such protection. It also didn’t enforce SSL pinning which meant I didn’t even need to use Frida to look at the SSL traffic.
When I investigate decompiled code with jadx-gui, the first file I look for is strings.xml, which can often yield some interesting information such as stored API keys the apk uses to communicate with online services, such as Google Maps, etc.
The above string definition name piqued my interest. Why would the android application for Bluetooth LED lamp need to scan wifi?
The AndroidManifest.xml defines the permissions, activities and intents of an apk. This “little” android application has 108 defined activities, thankfully only 4 of these are exported (qark found 1 that MobSF missed — this is why I use both tools). This means another application or process can invoke/call them, which is a pretty basic security issue. I’m not too concerned with this for now but I will come back to it when looking for vulnerabilities here in Part Two.
The permissions this application asks for are extremely sketchy, given its supposed function. Some of these permissions were removed from Android in version 6.0 and above, and unfortunately Android’s documentation on old permissions that they removed is severely lacking.
It might be a bit difficult to read each entry in the above image, and I just want to focus on the worst offenders. MobSF, one of the tools I listed above, flags all of these permissions and attempts to give a brief summary of each.
I like MobSF, but it can be a bit hyperbolic; it will flag the Internet permission as dangerous. Most applications need to communicate on the Internet for something these days. However, I would agree with the MobSF classification of some of these permissions. It’s always best to consult the Android definition to see exactly what the permission is used for.
The permissions ACCESS_WIFI_STATE, ACCESS_NETWORK_STATE are harmless enough and could be needed before determining if the application has internet access before attempting to reach out to an endpoint. No big deal there. CHANGE_WIFI_STATE concerns me because I did not see any controls regarding wireless networks or connecting to any wireless networks with the GUI. This is another area to look at later.
AUTHENTICATE_ACCOUNTS concerns me as well because of the following MobSF definition: Allows an application to use the account authenticator capabilities of the Account Manager, including creating accounts as well as obtaining and setting their passwords.
DOWNLOAD_WITHOUT_NOTIFICATION needs no explanation and is concerning.
READ_PHONE_STATE Allows the application to access the phone features of the device. An application with this permission can determine the phone number and serial number of this phone, whether a call is active, the number that call is connected to and so on.
ACCESS_COARSE_LOCATION: Access coarse location sources, such as the mobile network database, to determine an approximate phone location, where available. Malicious applications can use this to determine approximately where you are.
ACCESS_FINE_LOCATION: Access fine location sources, such as the Global Positioning System on the phone, where available. Malicious applications can use this to determine where you are and may consume additional battery power.
READ_SETTINGS & WRITE_SETTINGS: Allows an application to read & write the System settings.
KILL_BACKGROUND_PROCESSES: Allows the application to kill the processes of other applications. Obviously something that stands out as well.
GET_TASKS: Allows application to retrieve information about currently and recently running tasks. May allow malicious applications to discover private information about other applications.
RECORD_AUDIO: Self explanatory — what the fuck is that needed for?
A lot of applications these days take your IMEI, wifi mac address, wifi ssid, and a bunch of other info and send it to data collection services such as Logly. Facebook, GoodReads, even Speedtest.net does it. It’s become normalized. If that’s all this application was doing I wouldn’t be as concerned as I am now.
In the following source code we can see it building a JSONArray with the ssid and mac address. It sends this information to an endpoint in China.
In fact, there’s lots of Chinese endpoints being used by this application, but Yeelight is a Chinese company so that’s what you would kind of expect. However, you have to ask yourself are you comfortable with Yeelight sending this kind of information to China?
The following code checks the type of network we are using. If it’s not a wifi network, it checks if it’s a cmwap, 3gwap or uniwap newtork. If it is any of these it returns an Apache HttpHost object pointing to 10.0.0.172 on port 80. If the wifi network is ctwap, it does the same with 10.0.0.200 on port 80., it tries to connect to internal network ips: 10.0.0.172 & 10.0.0.200.
cmwap is a Chinese specific WAP network, which can only be used to access resources on the 10.* network range.
The following code below is building up a org.json.JSONObject, recording the newtorkId, ssid, bssid (mac address), and the password. Given that this code is using json, I have to assume that this is sent over the Internet to one of the hosts/IPs in China above. It doesn’t make sense to use json unless it’s for communication between different hosts. If that’s the case, it looks like Xiaomi could be recording your network information and the password to connect to it.
There is a WifiDeviceScan class and it appears to be looking for “Mi Devices”, which I assume are part of Xiaomi’s Wi-Fi offerings: https://xiaomi-mi.com/wifi-routers/
I find it interesting that it is using the SSID to determine if it’s one of Xiaomi’s devices because customers can change their SSID. Surely it should be looking at the MAC addresses.
I also discovered a class called XMPushService, which is one of the main reporting services to Xiaomi.
As soon as the application is installed, and before a user has a chance to create an account, the phone tries to connect to the above hosts on port 5222. A lot of Xiaomi services call home to these endpoints.
There’s a mipush sdk which sends a bunch of wifi related information and the latitude and longitude coordinates I mentioned earlier back to Xiaomi as well. You can see these classes under the package:
The MiiLocalAPI class is a udp asynchronous service that sends information about the Mi devices via UDP, including the type of encryption the device is using (TKIP, for example).
The Yeelight Android application uses some native code via the following:
Native code is written in C or C++ and is a great area to look at when trying to find vulnerabilities.
MobSF was able to identify the following URLs, a lot of which are not using https. The application has code to accept self signed certificates anyway so it’s vulnerable to man-in-the-middle attacks as well.
I also saw a lot of code relating to the AudioManager, which is probably why the RECORD_AUDIO permission was requested.
Moving along to dynamic analysis. I installed the Yeelight application on an Android Emulator, but ran into issues. The device appeared to fill up the emulator’s disk fairly quickly, and when it tried to invoke Bluetooth scanning it crashed. As a result I needed to look at this thing on my phone. Prior to that though, here’s a few things I noticed from logcat on the emulator last night.
I mentioned earlier that the application calls home as soon as it starts up. The below request is an attempt at doing so:
12–19 00:03:57.512 4868 4885 D MIOT_LOG: SMACK: Could not connect to 184.108.40.206:5222 port:5222 failed to connect to
/220.127.116.11 (port 5222) from /10.0.2.15 (port 56977) after 8000ms
I initially thought the Yeelight application was attempting to record audio as soon as it was installed, however, it appears that Android is trying to listen for “OK Google” and is encountering issues because the emulator does not have microphone capabilities.
12–19 00:04:01.971 2145 2145 E AudioFlinger: not enough memory for AudioTrack size=131296
12–19 00:04:17.088 2863 5665 E android.media.AudioRecord: Error code -20 when initializing native AudioRecord object.
As stated, the application started crashing on the emulator soon after this:
12–19 00:04:20.947 2863 5476 W ErrorProcessor: Caused by: com.google.android.apps.gsa.shared.exception.GsaIOException:
Error code: 393238 | Buffer overflow, no available space.
I tried increasing the space on the emulator which worked, but it crashed again when it tried to scan for Bluetooth devices.
As soon as the application was installed, and before I even opened it, here’s what it was doing:
The above network activity didn’t appear in logcat until after the application was installed. It repeated quite often in the logs, so this application is constantly looking at the networks in range & recording them. It also looks at every saved wireless network your device as connected to.
One last point of interest before I wrap up Part One, is the LogCollectionService class. This is one of the exported services with no permissions. From looking at the source code it appears that this service saves the output of logcat on the device to a log file and then uploads this to Xiaomi.
At this point I can safely say that the only information being sent via https to China was my network ssid, and my mac address for the wireless network I was connected to.
I did not see a HTTP POST with the log files being sent while I looked at the application, but that is not to say it isn’t happening. It could easily be a scheduled task that takes place nightly, for example.
This is extremely worrying because logcat shows a lot of debug information and as a customer I don’t feel comfortable sending my logcat information to a server in China.
I have no idea if any of the other information we looked at is being sent using the XMPP service on port 5222, or via UDP as there is a bunch of UDP Datagram classes as well, or by some other method.
Unfortunately the device I have here isn’t rooted so I can’t see much more than this. I plan to put this application on a rooted device so that I can see the log files it generates to see what kind of information is in them, and to also look at the database files it stores locally, and I would like to run tcpdump on the rooted device to see exactly what is going happening on the newtork.
This application does not do SSL pinning so you don’t even need to install the Burp SSL certificate to see the traffic, which means this application is vulnerable to man-in-the-middle attacks and can be sniffed pretty easily if you’re on a public wireless network.
The Yeelight application claims to control a Bluetooth LED lamp. I couldn’t get the application to work with the lamp at all; it couldn’t find it. It was essentially useless. However, the application scans and records wireless networks that are in range, appears to try to connect to some of them, scans Bluetooth devices which I would expect given its supposed functionality, records every wireless network you have saved on your device. The above “fuctionality” is repeated continuously while the application is installed. It also appears to send the contents of logcat to Xiaomi as well.
In Part Two I will look at the following:
- Tcpdump traffic
- The native code in libTelinkCrypto.so, libblecipher.so, liblight_mix.so and libmiio.so
- The exported activities, intents, broadcast receivers and the content provider to see if we can find some vulnerabilities there