5.6 KiB
5.6 KiB
Bridge Firmware Analysis — firmware.bin
Static analysis of the LOQED bridge firmware dump (firmware/bridge/firmware.bin,
4 MB, MD5 847d4795ee64714d3b02e2bdb3d20266).
Method: analyzed with local Binwalk + strings/dd + a partition-table parser (the
same tools REA's firmware route wraps — the REA MCP server was not registered in that session).
All findings are static (nothing was executed). Offsets below are byte offsets into
firmware.bin.
Identity & build
- Project
LoqedBridge, ESP-IDF v4.4.1, ESP32-D0WD (single-core) + 64 Mbit PSRAM. - Git commit baked in:
33beadfc1ba5d4d3b0cb6a6ee673535a1d12972c, version33beadf-dirty. The-dirtysuffix means it was built from an uncommitted working tree. - The 4 MB image is essentially two copies of the same app (factory @
0x20000and ota_0 @0x160000), which is why every string appears twice (e.g. CA store at0x372faand0x166b55).
Flash layout (partition table @ 0x8000)
| Label | Type | Subtype | Offset | Size |
|---|---|---|---|---|
nvs |
data | 0x02 | 0x009000 |
16K |
nvs_key |
data | 0x04 | 0x00d000 |
4K |
otadata |
data | 0x00 | 0x00e000 |
8K |
phy_init |
data | 0x01 | 0x010000 |
4K |
factory |
app | 0x00 | 0x020000 |
1280K |
ota_0 |
app | 0x10 | 0x160000 |
1280K |
ota_1 |
app | 0x11 | 0x2a0000 |
1280K |
coredump |
data | 0x03 | 0x3e0000 |
64K |
The nvs_key partition (subtype 0x04) means NVS encryption is provisioned — consistent with
the boot log's secure_boot_v1.
Keys & crypto — correcting an earlier finding
- No plaintext keys or certificates are embedded. The
report.txtclaim of "embedded RSA and EC Private Key structural markers" is a false positive: the PEM lines at0x39439+are mbedtls's null-terminated label table (contiguous-----BEGIN/END ...-----\0strings preceded by ASN.1 OID bytes2a 86 48 86 f7 0d 01 05 0d), i.e. library format constants — not a key body. - What is bundled: the standard ESP/mbedtls CA trust store (Entrust, XRamp, Trustwave, Starfield, COMODO, Certum, Actalis, China Financial…). These are public CA certs for TLS server validation, not secrets.
- Crypto primitives referenced: mbedtls with SHA-1/SHA-256 (Binwalk flagged SHA256 constants at
0x2801c/0x167fc4), AES, RSA, EC; TLS-PSK code paths exist (gated behind menuconfig). - Real device secrets (device identity, auth/key tokens, WiFi credentials) live in the
encrypted
nvs/nvs_keypartitions, not in the code image. - ⚠️ Security observation: the firmware logs credentials in cleartext to the UART console —
format strings
id: %llu, token: %s, auth_token: %s,key token: %s,token: %s, andSetting wifi credentials to:. Anyone with UART access could harvest live tokens at runtime.
Endpoints (string literals)
mqtts://mqtt.production.loqed.com:8883andmqtts://production.loqed.com:8883(MQTT/TLS)https://gateway.production.loqed.com:443,https://production.loqed.com:8080- OTA:
https://firmware.loqed.com/firmware_bridge_…and…/firmware_lock_… - Core-dump exfil:
https://app.loqed.com/Bridge-core-dump/bridge-core-dump2.php(uploads crash core dumps to LOQED as multipart, with"attachment"/"message"fields)
Functionality
- Role: the bridge is the BLE central/client — full of
esp_ble_gattc_*(scan, open, search_service, read/write char, register_for_notify, MTU). The lock is the BLE peripheral. Pairing/bonding viaESP_GAP_BLE_AUTH_CMPL_EVT. - Cloud transport: MQTT/TLS. Topic fragments:
locks/,/to_backend,/to_lock,/state,/status→ structure is effectivelylocks/<id>/to_lockandlocks/<id>/to_backend. MQTT auth errors handled ("bad username or password", "not authorized"). - Command protocol (bridge relays Backend ↔ Lock):
Backend->Bridge: REQUEST_STATUS_INFO_BRIDGEBackend->Bridge: COMMAND_BRIDGE_FIRMWARE_UPGRADE/COMMAND_LOCK_FIRMWARE_UPGRADECOMMAND_ELECTRONIC_DOOR/COMMAND_ELECTRONIC_DOOR_REBOOT(the actual door actuation)Lock->Bridge: REQUEST_TIME_FROM_BRIDGE,RESPONSE_START_NEW_FIRMWARE_DOWNLOAD,RESPONSE_SEND_PART_FIRMWARE→ the bridge downloads lock firmware over HTTPS and streams it to the lock over BLE in parts.- Payloads are binary/custom, not JSON (only the core-dump HTTP upload uses JSON keys).
- Lock state machine (
GO_TO_STATE_*):TWIST_ASSIST_{OPEN,LOCK,LATCH},INSTANTOPEN_{OPEN,LATCH},TOUCH_TO_LOCK,MANUAL_{LOCK,UNLOCK}_{BLE,REMOTE}_{OPEN,LATCH,NIGHT_LOCK},MANUAL_UNLOCK_VIA_OUTSIDE_MODULE_{BUTTON,PIN}; status/errorsSTATE_CHANGED_{OPEN,LATCH,NIGHT_LOCK,UNKNOWN},MOTOR_STALL. The three positions are open / latch / night-lock, reachable via twist-assist, touch, outside PIN/button, BLE, or remote. - OTA: HTTPS OTA (
esp_https_ota_*) with A/Bota_0/ota_1+ rollback, validates image magic0xE9, for both bridge and lock. - WiFi provisioning:
ap.authmode/ap.passwd, "Set WiFi credentials (4)" — provisioned (likely over BLE) and stored in NVS. - Developer path leak persists:
/home/pierre/bridge/components/button_driver/iot_button.cand/root/bridge/....
Coverage & limits
- Static strings/structure only; encrypted NVS contents (the actual secrets) were not decrypted — that needs the device's flash-encryption key, which is not in this image.
- Payload semantics (byte layout of the BLE/MQTT commands) are not recoverable from strings alone — that needs disassembly of the Xtensa code (Ghidra/IDA) around the command-handler functions.