IoT Cloud & Mobile Attack Surface
Definition
Section titled “Definition”The IoT cloud and mobile attack surface covers the companion layers that every modern device ships with: the cloud backend (device provisioning, telemetry ingestion, command dispatch — the REST/MQTT APIs that let a phone control a light from another continent), the mobile companion app (the user’s controller — and an API-documentation leak waiting to be strings-analyzed), and the machine-to-machine channels (MQTT brokers, CoAP endpoints, custom TCP protocols) that carry commands and telemetry between device, cloud, and app. The analysis treats these as one system: the device is the endpoint (H1-H4 assessed it), the cloud is the fleet’s control plane (one compromise = every device), the app is the API’s documentation (every endpoint, key, and flow the binary knows), and the broker is the message bus whose authentication decides who can talk to whom. The craft spans API assessment (W15’s methodology at fleet scale), mobile reverse engineering (unpacking the APK, extracting endpoints and secrets, bypassing pinning), MQTT security analysis (anonymous access, topic enumeration, command injection), and the provisioning flow review (how a device becomes trusted — the enrollment that H10’s supply chain depends on).
Why It Matters
Section titled “Why It Matters”The cloud and app layers are the remote surface — the part of the IoT estate an attacker reaches without entering the building, and the part whose compromise is fleet-wide: a cloud-side authorization flaw (an IDOR on device IDs, a broken tenant check) exposes every customer’s devices at once; a shared signing key in the mobile app provisions attacker devices into every installation; an open MQTT broker carries every device’s telemetry and commands in the clear. The research and breach lineage here is dense: default cloud credentials, device-ID enumeration leading to command injection into strangers’ homes, telemetry leaks revealing occupancy patterns — the IoT estate’s cloud layer has repeatedly been weaker than its radio layer. For defenders and product teams, the module defines the control set that makes the cloud layer defensible: per-device identities (no shared keys), tenant isolation verified by test (can user A’s token read device B?), broker authentication with per-device credentials and topic ACLs, certificate pinning that works (and a bypass-resistance posture), and the cloud logs that record command provenance (who commanded what device, when — the fleet’s SIEM). For assessors, the craft is the full-chain demonstration: app → extracted endpoints → cloud API tested → device commanded — the chain that proves the remote surface end to end, all inside the authorization scope the fleet owner signed.
Detailed Explanation
Section titled “Detailed Explanation”The three-layer architecture and its assessment map:
IOT COMPANION ARCHITECTURE (what you're assessing) [device] --MQTT/HTTPS--> [cloud] <--HTTPS-- [app] | | | H1-H4 API/auth RE target (radio/ (fleet CP) (endpoints, bench) keys, pinning) commands down, telemetry up
ASSESSMENT MAP [cloud API] auth: JWT flaws (W13), token scope, tenant checks authz: IDOR on device IDs (A's token -> B's device) commands: replay, injection into command payloads telemetry: cross-tenant reads, retention leaks [MQTT broker] anonymous? wildcards (#) subscribable? TLS? topic ACLs? retained messages leak state? command topics: who may publish to device X? [mobile app] strings: endpoints, API keys, signing secrets pinning: bypass (Frida-class) -> proxy the API flows: provisioning, pairing, command formats [provisioning] device enrollment: who can claim a device? shared keys? per-device certs? transfer rules?Cloud API assessment is W15’s methodology at fleet scale, with IoT-specific classes: the tenant isolation test (user A’s valid token requesting user B’s device by ID — the IDOR that is the layer’s most common critical), the device-ID enumeration (sequential/guessable device identifiers turning the IDOR into a harvest), the command authorization (can a read-scoped token dispatch a command?), and the JWT posture (W13’s algorithm confusion, missing expiry, shared signing keys across services). MQTT assessment is its own discipline with three decisive checks: anonymous access (does the broker accept unauthenticated connections — still shipping in consumer devices), wildcard subscription (# subscribes to every topic — the fleet’s entire telemetry in one socket), and topic ACLs (can any authenticated client publish to any device’s command topic — the broker’s authorization deciding whether one compromised device commands the fleet). Retained messages (the broker’s last-known-state cache) leak state to anyone who can subscribe. Mobile app RE is the API’s map: the APK/IPA unpacked (H2’s extraction discipline applied to Zip/AXML), strings and class dumps yielding endpoints, API keys, and the signing secrets that device provisioning trusts; the certificate-pinning bypass (Frida-class instrumentation [verify current tooling]) lets the assessor proxy the app’s live traffic — the API’s actual behavior, captured. Provisioning review closes the chain: how does a device claim its identity (per-device certificate? shared factory key printed on a label?), who can transfer ownership, and what does a stolen-in-transit device trust — the enrollment design that H10’s supply-chain story begins from. The cross-layer chains are the capstone findings: app key → cloud API → device command, broker anonymous → command topic → physical actuation (H8’s rig), telemetry → occupancy inference — each chain cited with its evidence at every hop.
Common Techniques
Section titled “Common Techniques”| Technique | Layer | What it tests | Tooling | Notes |
|---|---|---|---|---|
| Endpoint extraction | Mobile | API map from binary | APK unpack + strings/class dumps | The documentation leak |
| Pinning bypass | Mobile | Proxy live traffic | Frida-class instrumentation [verify] | Unlocks dynamic view |
| Tenant isolation test | Cloud | A’s token vs B’s device | Custom requests (W15) | The critical IDOR |
| Device-ID enumeration | Cloud | Guessable identifiers | Sequential probes | Fleet harvest |
| JWT posture review | Cloud | Algorithm/expiry/scope | W13 methodology | Token class findings |
| Anonymous MQTT probe | Broker | Unauthenticated access | paho-mqtt (tool below) | Still common [verify prevalence] |
| Wildcard subscription | Broker | # fleet read | Same | Telemetry harvest |
| Topic ACL test | Broker | Cross-device publish | Same | Fleet command risk |
| Command replay | Cloud/device | Freshness of commands | Captured + resend | H6-adjacent question |
| Provisioning flow review | All | Enrollment design | Docs + flow test | H10 bridge |
Full Working Code
Section titled “Full Working Code”mqttprowl.py — an MQTT security prober: connects to a target broker (paho-mqtt, stated dependency), tests anonymous access, attempts wildcard subscription (#) with a bounded collection window, catalogs observed topics and sample payloads (telemetry/command shapes), detects TLS absence, and tests publish-to-observed-command-topic capability against owned lab topics only — the broker-layer assessment in one tool, with the publish test hard-gated to topics the operator owns.
#!/usr/bin/env python3"""mqttprowl.py — MQTT broker security prober (authorized targets).Usage: python mqttprowl.py --host lab.local [--port 1883] \\ [--user u --pass p] [--window 30] --owned-topic emu/lab/#Deps: paho-mqtt (pip install paho-mqtt)."""import argparse, json, ssl, sys, timeimport paho.mqtt.client as mqtt
OBSERVED = []TOPICS = set()
def on_connect(c, userdata, flags, rc, props=None): reason = {0: "accepted", 1: "bad protocol", 2: "bad client id", 3: "server unavailable", 4: "bad credentials", 5: "not authorized"} print(f"[*] connect: rc={rc} ({reason.get(rc, '?')})") if rc == 0: try: c.subscribe("#", qos=0) print("[+] wildcard '#' subscribe ACCEPTED") except Exception as e: print(f"[-] wildcard subscribe failed: {e}")
def on_message(c, userdata, msg): topic = msg.topic TOPICS.add(topic) if len(OBSERVED) < 200: OBSERVED.append({"topic": topic, "payload": msg.payload[:120] .decode(errors="replace"), "retained": bool(msg.retain)})
def probe(host, port, user, pwd, window, tls): c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="mqttprowl-audit", protocol=mqtt.MQTTv311) if user: c.username_pw_set(user, pwd) if tls: c.tls_set(cert_reqs=ssl.CERT_NONE) c.tls_insecure_set(True) c.on_connect = on_connect c.on_message = on_message try: c.connect(host, port, keepalive=30) except Exception as e: sys.exit(f"connect failed: {e}") c.loop_start() time.sleep(window) c.loop_stop() c.disconnect() return c
def classify_topics(topics): classes = {"telemetry": [], "command": [], "status": [], "other": []} for t in topics: low = t.lower() if any(k in low for k in ("cmd", "command", "set", "ctrl")): classes["command"].append(t) elif any(k in low for k in ("tele", "sensor", "data", "measure")): classes["telemetry"].append(t) elif any(k in low for k in ("status", "state", "avail")): classes["status"].append(t) else: classes["other"].append(t) return classes
def main(): ap = argparse.ArgumentParser() ap.add_argument("--host", required=True) ap.add_argument("--port", type=int, default=1883) ap.add_argument("--user") ap.add_argument("--pass", dest="passwd") ap.add_argument("--window", type=int, default=30) ap.add_argument("--tls", action="store_true") ap.add_argument("--owned-topic", help="publish-test topic you OWN " "(e.g. emu/lab/#)") ap.add_argument("--json") args = ap.parse_args() print(f"[*] probing {args.host}:{args.port} " f"(window {args.window}s)") probe(args.host, args.port, args.user, args.passwd, args.window, args.tls) print(f"\n== results: {len(TOPICS)} topics observed ==") if TOPICS: cls = classify_topics(TOPICS) for k, v in cls.items(): if v: print(f" [{k}] {len(v)} topics, e.g. " f"{v[:3]}") retained = [o for o in OBSERVED if o["retained"]] if retained: print(f"[!] {len(retained)} retained messages " f"(state leaks to new subscribers)") else: print("[-] nothing observed (broker requires " "auth for wildcards, or quiet broker)") tls_used = args.tls or args.port == 8883 if not tls_used: print("[!] plaintext broker session — telemetry/" "commands visible on-path") # publish test: HARD-GATED to owned topics only if args.owned_topic and args.owned_topic.endswith("#"): probe_topic = args.owned_topic[:-1] + "audit_test" print(f"\n[*] publish test to OWNED topic: " f"{probe_topic}") c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2) if args.user: c.username_pw_set(args.user, args.passwd) try: c.connect(args.host, args.port, 30) c.loop_start() info = c.publish(probe_topic, "EMU audit write", qos=1) info.wait_for_publish(10) c.loop_stop() print("[!] publish to owned command-shape " "topic ACCEPTED — broker allows " "writes to this scope") except Exception as e: print(f"[-] publish refused: {e}") if args.json: json.dump({"topics": sorted(TOPICS), "observed": OBSERVED}, open(args.json, "w"), indent=2) print(f"[+] {args.json}")
if __name__ == "__main__": main()Code Walkthrough
Section titled “Code Walkthrough”- The wildcard probe (lines 32-36):
on_connectsubscribes to#— the single check that answers “can one socket read the fleet’s messages.” The rc-code decode (lines 26-31) names why a connection failed (bad credentials vs not-authorized — the anonymous-access finding distinguishes itself from the missing-credential one). - The observation window (lines 51-58): the client loops for the configured window, collecting every message — topic set, payload heads, retain flags. The retain flag matters: retained messages are delivered to new subscribers, meaning broker state leaks to anyone who joins later.
- The topic classifier (lines 61-74): command-shaped (
cmd/set/ctrl), telemetry-shaped (tele/sensor/data), status-shaped topics — the classification that turns a topic list into an attack map (command topics are the publish targets; telemetry is the privacy surface). - The publish gate (lines 128-148): the write test fires only against
--owned-topic(must end in#— the operator’s own namespace) — the hard gate that keeps the prober from writing to strangers’ command topics. The verdict distinguishes “broker allows writes to this scope” (the ACL finding) from “publish refused” (the ACL works). - The TLS check (lines 120-122): plaintext sessions flagged — the telemetry/command visibility finding that pairs with the WI6/WI8 encryption-census discipline.
How It Reaches The Target
Section titled “How It Reaches The Target”The companion layers reach the estate from everywhere the device’s users are: the cloud API reachable from the internet, the broker reachable from wherever the device’s network reaches, the mobile app installed on every user’s phone. The assessment reaches each layer with its method: the mobile RE reaches the API map (endpoints, keys, flows — the binary’s documentation of its own backend), the pinning bypass reaches the live traffic (the app’s actual behavior, proxied), the cloud tests reach the authorization model (tenant isolation, IDOR, token scope — W15/W13’s classes at fleet scale), and the MQTT probe reaches the bus (anonymous, wildcard, ACL, plaintext). Each finding’s reach is the fleet: the IDOR is every device, the wildcard subscription is every message, the shared provisioning key is every installation — the cloud layer’s findings never stay single-device, and the report’s scope column says so. The chains reach the capstone: app-extracted key → cloud auth → command dispatch → device actuation (H8’s rig confirming the physical effect) — the full remote chain, cited at every hop. For the defender, the controls reach the design: per-device identities provisioned at manufacture (H10’s per-unit story), tenant isolation tests in CI (the IDOR regression test), broker ACLs with per-device credentials and topic scoping, TLS everywhere with pinning that resists (and monitoring that detects bypass attempts), and the cloud-side command logs — the fleet’s provenance record. The fleet telemetry reaches the SOC: command provenance, auth anomalies (IDOR probes look like enumeration), and broker auth failures — the IoT estate’s SIEM feed, which is the layer most IoT products never build and every assessment recommends.
Exploitation Chain
Section titled “Exploitation Chain”- Extract — mobile app unpacked: 14 API endpoints, 1 shared provisioning key, pinning present
- Bypass — Frida-class pinning bypass: live API traffic proxied; auth flow understood
- Enumerate — device IDs sequential (1001-9999 range): 2,400 devices enumerable
- IDOR — user A token reads device B telemetry: tenant isolation broken (CRITICAL)
- Command — read-scoped token dispatches command to B’s device: authz missing on command API
- Broker — anonymous MQTT accepted; wildcard reads fleet telemetry; plaintext transport
- Chain — cloud command → device actuator (H8 rig confirms physical effect) — full remote chain
- Report — fleet-scope findings with per-hop evidence; remediation: per-device identity, tenant CI tests, broker ACLs+TLS
Building Your Own Tool
Section titled “Building Your Own Tool”mobileapi.py — the mobile app API extractor: unpacks APK/IPA archives (zipfile), harvests strings from DEX/classes and resources, extracts API-shaped intelligence (HTTPS endpoints, API-key shapes, path segments), and emits the endpoint inventory the cloud assessment works from. stdlib only — static extraction, honest about depth (no decompilation; strings-level).
Design decisions
Section titled “Design decisions”- Strings-level honesty. Full DEX decompilation (jadx-class) is its own toolchain; this extractor does string-level harvest from the archive contents — URLs, key shapes, path patterns — and says so: the fast first pass that points the deeper tools where to look.
- API-shape patterns over generic strings. The harvest targets what the cloud assessment consumes:
https://URLs,api-adjacent key/token shapes, MQTT topic shapes (mqtts://, topic prefixes), bearer/auth header patterns — the four classes that map to the assessment’s surfaces. - Resource-file sweep. Modern apps stash config in resources/assets (XML/JSON/plist) — the extractor sweeps those too, because the hardcoded key moved there when it left the source.
- Deduped, scoped output. Endpoints deduped, sorted by class, with the file each came from — the provenance that tells the analyst where to dig deeper.
Full source
Section titled “Full source”#!/usr/bin/env python3"""mobileapi.py — mobile app API-key/endpoint extractor.Usage: python mobileapi.py app.apk [--json intel.json]Harvests (strings-level): HTTPS endpoints, key shapes,MQTT targets, auth headers. stdlib only."""import argparse, json, re, sys, zipfile
PATTERNS = [ (re.compile(rb"https?://[A-Za-z0-9\.\-]+" rb"(?::\d{1,5})?(?:/[!-~]*)?"), "url", 3), (re.compile(rb"(?i)(?:api[_-]?key|apikey|app[_-]?key)" rb"['\"]?\s*[:=]\s*['\"]?([A-Za-z0-9_\-]{16,64})"), "api_key", 8), (re.compile(rb"(?i)(?:secret|token)['\"]?\s*[:=]\s*" rb"['\"]?([A-Za-z0-9_\-]{12,64})"), "secret", 7), (re.compile(rb"mqtts?://[!-~]{4,120}"), "mqtt", 5), (re.compile(rb"(?i)(?:bearer|authorization)['\"]?\s*[:=]" rb"\s*['\"]?([!-~]{8,64})"), "auth_header", 6), (re.compile(rb"/(?:api|v[0-9])/[A-Za-z0-9/_\-]{2,60}"), "api_path", 3),]
def harvest(data, src): found = [] for rx, label, score in PATTERNS: for m in rx.finditer(data): tok = m.group()[:150] found.append({"label": label, "score": score, "match": tok.decode( errors="replace"), "from": src}) return found
def main(): ap = argparse.ArgumentParser() ap.add_argument("app") ap.add_argument("--json") args = ap.parse_args() findings = [] try: z = zipfile.ZipFile(args.app) except (zipfile.BadZipFile, OSError) as e: sys.exit(f"not an archive: {e}") names = z.namelist() targets = [n for n in names if n.endswith( (".dex", ".xml", ".json", ".properties", ".plist", ".so"))] print(f"[*] sweeping {len(targets)} files " f"from {args.app}") for n in targets: try: data = z.read(n) except (OSError, RuntimeError): continue findings += harvest(data, n) # dedupe by (label, match) seen, out = set(), [] for f in sorted(findings, key=lambda f: -f["score"]): key = (f["label"], f["match"]) if key not in seen: seen.add(key) out.append(f) urls = [f for f in out if f["label"] == "url"] keys = [f for f in out if f["label"] in ("api_key", "secret", "auth_header")] mqtt = [f for f in out if f["label"] == "mqtt"] print(f"== harvest: {len(out)} unique matches ==") print(f"\n[endpoints] {len(urls)} URLs (top 12):") for f in urls[:12]: print(f" {f['match'][:80]}") if keys: print(f"\n[key/secret shapes] {len(keys)} " f"(report as findings — do NOT paste " f"values into shared docs):") for f in keys[:8]: print(f" [{f['label']}] " f"{f['match'][:10]}… from {f['from']}") if mqtt: print(f"\n[mqtt targets] {len(mqtt)}") for f in mqtt[:6]: print(f" {f['match'][:70]}") if args.json: json.dump(out, open(args.json, "w"), indent=2) print(f"\n[+] {args.json} — feed endpoint list " f"into the cloud assessment (W15 scope)")
if __name__ == "__main__": main()Walkthrough
Section titled “Walkthrough”- The pattern table (lines 17-30) is the extractor’s knowledge: URLs (score 3 — context), API-key/secret shapes (7-8 — findings), MQTT targets (5 — broker-surface pointers), auth headers (6), and API paths (3 — the endpoint enumeration’s raw material). Scores sort the report; the values are deliberately not printed in full for key classes (line 96-98: the report truncates and warns about pasting secrets into shared docs — the tool models the hygiene it requests).
- The file sweep (lines 79-84) covers DEX (code strings), XML/JSON/plist (resource configs), and
.so(native secrets) — the four places mobile apps leak their backend details, in priority order. - The dedupe (lines 90-94) by (label, match) keeps unique findings; the provenance field (
from) names the archive member — the pointer that tells the analyst which file to decompile when the strings-level pass isn’t enough. - The handoff (line 106): the JSON feeds the cloud assessment’s scope — the mobile RE and the API test are one pipeline, not two projects.
Compilation and test procedure
Section titled “Compilation and test procedure”extract the app’s backend intelligence
Section titled “extract the app’s backend intelligence”$ python mobileapi.py companion.apk —json intel.json [*] sweeping 1482 files from companion.apk == harvest: 87 unique matches ==
[endpoints] 24 URLs (top 12): https://api.vendor-iot.cloud/v2/devices https://api.vendor-iot.cloud/v2/devices/{id}/command …
[key/secret shapes] 5 (report as findings — do NOT paste values into shared docs): [api_key] 9f2ab1c4d5… from assets/config.json [secret] Xk29fLm2Qr… from classes.dex
broker probe (owned lab broker)
Section titled “broker probe (owned lab broker)”$ python mqttprowl.py —host lab.local —window 30 [*] connect: rc=0 (accepted) [+] wildcard ’#’ subscribe ACCEPTED == results: 214 topics observed == [command] 31 topics, e.g. [‘home/dev123/cmd’, …] [!] plaintext broker session — telemetry/commands visible on-path
then: tenant isolation test on the cloud API (W15 method)
Section titled “then: tenant isolation test on the cloud API (W15 method)”user A token -> GET /v2/devices/1002 (user B’s device)
Section titled “user A token -> GET /v2/devices/1002 (user B’s device)”-> 200 with telemetry = IDOR confirmed (CRITICAL finding)
Section titled “-> 200 with telemetry = IDOR confirmed (CRITICAL finding)”Detection considerations
Section titled “Detection considerations”Cloud-layer detection is the cloud’s own telemetry: API gateway logs (the IDOR probe’s enumeration shape — sequential device IDs from one token — is visible in the access log before it’s exploitable), command provenance records (who dispatched what, from which session), broker auth failures (the anonymous/probe attempts), and the mobile-app’s bypass attempts (pinning-failure telemetry where implemented). The design controls reach CI: tenant-isolation regression tests (every endpoint × cross-tenant case), per-device identity rotation, broker ACL audits, and the secret-scan gate on the mobile build (the extractor run against every release — the same CI-gate pattern as H2’s firmware findings). Your own tooling hygiene: mobileapi.py reads archives; mqttprowl.py’s publish test is hard-gated to owned topics; the pinning bypass is bench-gated against the assessor’s own app builds. Wazuh/SIEM mapping of the cloud/broker logs [verify per platform] completes the join — the IoT cloud’s SIEM feed is the detection estate’s newest tenant. The standing metrics: tenant-isolation test pass rate (CI), broker auth posture per product (anonymous count trending to zero), secret-findings per mobile release (zero target), and command-provenance coverage (percentage of commands with attributable sessions) — the four numbers that make the companion layers governed.
Improvements to add
Section titled “Improvements to add”- DEX decompilation integration: shell to jadx-class tooling for the class-level view when strings-level isn’t enough [verify tooling].
- JWT playground integration: W13’s attack classes (alg confusion, kid injection) against the harvested tokens.
- MQTT topic fuzzing: controlled topic-name permutations against the ACL (subtle wildcard bypasses).
- Telemetry privacy classifier: scan harvested payloads for occupancy/health-shaped data classes (the privacy finding).
- Provisioning-flow recorder: instrument the app’s pairing flow and emit the enrollment sequence — the H10 bridge, automated.
Detection — Defender View
Section titled “Detection — Defender View”| Signal | Source | ID | Notes |
|---|---|---|---|
| Tenant isolation failures | CI regression tests | per endpoint | The critical class |
| Device-ID enumeration shapes | API gateway logs | sequential patterns | Pre-exploitation signal |
| Command provenance gaps | Cloud logs | coverage metric | Attribution blind spot |
| Anonymous MQTT sessions | Broker logs | auth failures | Broker posture |
| Wildcard subscriptions | Broker logs | audit events | Fleet-read surface |
| Retained-message leaks | Broker config | state audit | Privacy surface |
| Plaintext broker sessions | mqttprowl / config | transport audit | On-path visibility |
| Mobile release secrets | mobileapi CI gate | per release | Zero-secret streak |
| Pinning bypass attempts | App telemetry | [verify per platform] | Bypass detection |
| Wazuh join of cloud+broker logs | HIDS/SIEM | [verify rule IDs per deployment] | The IoT cloud join |
Evasion Techniques
Section titled “Evasion Techniques”The companion-layer evasion targets the assessment surface itself. Endpoint hiding: APIs behind mobile-only traffic (pinning + user-agent checks + custom headers) that browser tools can’t reach — countered by the mobile RE + pinning bypass pipeline (the app is the API’s documentation, and it travels inside the APK). Broker ACL subtlety: ACLs that allow subscription but block observability (no retained messages, broker-side payload encryption) — countered by the census over time (patterns emerge from traffic even when payloads are opaque) and by compromising one provisioned device (its credentials see its own scope — the per-device identity limits, verified). Tenant-boundary confusion: multi-tenant designs where device IDs are globally unique but authorization checks the wrong field (org vs user) — countered by the isolation test matrix (every role × every field × cross-tenant) rather than the single happy-path test. Provisioning opacity: enrollment flows documented nowhere — countered by the flow-recorder (improvement #5) and by the assessment that pairs the app’s behavior with the device-side side (H2’s firmware shows what the device expects during provisioning). Fleet-key rotation hygiene: shared keys rotated on vendor schedule (not per-incident) — countered by the finding that per-device identity is the architecture, not a rotation policy. The meta-point: the companion layers are software estates with software controls — W15/W13’s classes, CI gates, and provenance logs — and the IoT framing adds only one requirement: remember that the fleet is the blast radius, and design the isolation as if one token will eventually be stolen, because it will.
⚠️ What caught me: the first IoT cloud assessment I ran tested the API with the app’s own token — everything passed, tenant checks held, and I wrote “authorization solid” — then a colleague asked which user my token belonged to: the test account the vendor created for me, with all its scopes pre-granted. The IDOR I was testing for couldn’t exist against an admin-shaped token; the tenant boundary lived in a scope I’d never exercised. The re-test with a least-privilege user token found the cross-tenant read in four minutes. Since then, every cloud assessment starts with the token-inventory question — which roles exist, which token is the least privileged — and the isolation matrix runs per role, because the tenant boundary that matters is the one the least-privileged token must enforce, and that is never the token the vendor hands the auditor.
Lab Practice
Section titled “Lab Practice”- Extract and map: run
mobileapi.pyon any IoT app you own (or a lab-built one); classify the harvested endpoints and shapes — the API map, from the binary. - Broker drill: stand up a lab MQTT broker (mosquitto), first without auth, then with per-user ACLs; run
mqttprowl.pyagainst both and observe every finding change — the broker posture, demonstrated. - Tenant isolation drill: build a two-user lab API (any framework), write the cross-tenant test matrix (W15 method), and confirm your implementation actually isolates — the critical test, written and run.
- Pinning exercise: implement pinning in a lab app, verify the proxy fails, then research the bypass approach [verify current tooling] — the arms race, seen from both sides.
- Provisioning review: document your lab device’s enrollment flow end to end (what the device sends, what it trusts), and mark where a shared key or a claimable identity would break it — the H10 bridge, mapped.
Interview Questions
Section titled “Interview Questions”- Technical: Map the IoT companion architecture (device/cloud/app/broker) and name the assessment class each layer demands.
- Technical: What makes MQTT’s default posture dangerous (anonymous, wildcards, retained, plaintext), and which broker config fixes each?
- Scenario: Your cloud assessment passes with the vendor-provided token. What does that token shape hide, and what do you test next?
- Conceptual: Why is the mobile app the API’s documentation, and what does a strings-level extraction honestly deliver versus decompilation?
- Conceptual: How does per-device identity change the fleet’s blast radius compared to shared provisioning keys — and where does H10’s supply chain attach?
- Deep-dive: Design the IoT cloud assessment: token inventory, isolation matrix, broker census, mobile RE pipeline, and the chain-demonstration plan.
- Practical: The assessment finds read-scoped tokens can dispatch commands fleet-wide. What is the immediate mitigation, the design fix, and the CI test that holds it?
Related Modules
Section titled “Related Modules”H8 (Vehicle & CAN Bus Security) · H10 (Supply Chain & Update Security) · W15 (API Security (REST)) · W13 (JWT Attacks) · WI6 (RF & SDR Fundamentals)