In part one, we concentrated on analysing the network communication when one turns the smart power plug on or off from the app. We had noticed some form of encrypted/encoded communication happening on udp port 27431 between the smart plug and the app installed on the phone. We also noticed some form of encrypted/encoded communication on tcp ports 9123 and 5222(xmpp) between the app, smart plug and ikonkek2.com.
Part two: Not so secret keys, some assembly and a reverse engineer
Let’s reverse the apk to see if the code can tell us what type of encryption/encoding was happening. I have a preference for jadx-gui so that’s what I used.
First let’s search for the domains we had seen in the network traffic. Searching for ikonke.us reveals the firmware download url. Note that it is http. This is under the UpdateFirmwareUtil class. About 15 different downloads are listed indicating the app supports multiple devices.
We can also see various variables are mapped to their values in the com.kankunit.smartknorns.commonutil.CommonMap file.
This collaborates our earlier network traffic analysis related to port 9123 and XMPP(5222). We can now also conclude that ikonkek2.com hosts an Apache Mina server listening on ports 9123 and 50003, and an XMPP server listening on port 5222.
We had earlier discovered that the app tries to discover the plug by sending some broadcast messages on port 27431. Let’s see if we can figure out what the exact messages are from the app.
Searching for this in the code reveals the class com.kankunit.smartknorns.service.BaseUdpBroadcastService.
The command sent is:
lan_phone%mac%nopassword%“ + new SimpleDateFormat(DateTransformer.DATE_FORMAT, Locale.ENGLISH).format(new Date(System.currentTimeMillis())) + ”%heart
The sending method is PackageSendData:
byte[] cmd_buf = BaseUdpBroadcastService.this.sj.PackageSendData(cmd, cmd.length());
This method is from the class smartplug.JniC
The method is called from the native library CommunSmartPlug-jni.
The library is packaged with the app in the lib/armeabi folder
We can access the file by unzipping the apk and navigating to the lib/armeabi folder.
Running the file command on it reveals it is a 32-bit ARM binary:
We can also run strings on it to gather more information. Sample output reveals we’re on the right path as we can see the string PackageSendData as part of the output.
Time for Ghidra!
We open the native library in Ghidra and start by looking at the Symbol Tree to locate our function:
Displaying the call graph, we note it calls the function EncryptData256, which in turn calls the functions aes_set_key and aes_encrypt:
Let’s start by analysing the EncryptData256 function:
Before analysis
After analysis and some cleanup
After some more analysis and cleaning up the code, we get to the following code snippet:
We see the use of the the function get_key to fetch the aes key in bytes, the function aes_set_key then sets the encryption key in hex.
The key is then used to encrypt the plaintext as below:
So what we need to find out is what the aes key is, we start by figuring out what the get_key function does:
The function uses switch statements to set a character to each corresponding position in the key.
We can therefore reconstruct the key manually or by using Ghidra’s scripting functionality.
The final key is: fdsl;mewrjope456fds4fbvfnjwaugfo
Let’s confirm the key works!
Remember the messages we’d seen being broadcast by the app on udp port 27431? Let’s try decrypt one.
We first convert our key to hex then try decrypting one of the UDP messages we had captured:
Eureka! The key works!
The decrypted command confirms the format of the UDP messages as earlier discovered:
lan_phone%mac%nopassword%“ + new SimpleDateFormat(DateTransformer.DATE_FORMAT, Locale.ENGLISH).format(new Date(System.currentTimeMillis())) + ”%heart
Let’s try our first attack - Discovering all smarts plug connected to a network and their details.
For this, we’re going to use go code to:
- Format the command and insert the current time as per the message specifications.
- Encrypt the message with the key.
- Send it to the broadcast address on UDP port 27431.
- Listen for responses.
- Decrypt the received messages.
Once we run our custom discovery tool, we get responses from all the smart plugs in the network and various details such as IP, mac address, device name/password, status, hardware and software versions:
We had also seen network communication between the app and the server (ikonkek2.com) on port 9123. Let’s try see if we can figure it out.
We can start with one of the first messages we saw:
1 2 |
|
We can look for this in our reversed apk:
What we see is that the minaEncode method base64 encodes the output from the PackageSendData method.
This is the same method that is used to encrypt the UDP discovery messages. Good thing we already analysed that method and got the encryption key.
By further analysing the reversed apk we see that the message format takes the form of:
1
|
|
This is clearly visible in the following snippet:
Decryption test
Let’s try decryption of some of the messages we had captured in the network traffic.
Get device information
1 2 |
|
Get device status
1 2 |
|
Running remote commands
Let’s try exploit this feature to perform remote query of various details about the smart plug from the ikonkek2.com server. We can do this from anywhere as long as we have an internet connection.
We are again going to write some go code to:
- Create commands and format the messages.
- Encrypt the messages with the key.
- Send the messages to ikonkek2.com on port 9123.
- Listen for responses.
- Decrypt the received messages.
All we need is the mac address of a smart plug
Get device information:
Get device status:
Get email linked to the device:
The project files and code can be found here.
In part three, we will try to remotely control the smart power plug (turn it on and off).