where-secrets-hide

theory

Lesson 1 — Where Secrets Hide

Why Secrets End Up in Firmware

Shipping software is hard. Deadlines exist. Firmware is no different — and embedded developers face constraints that make bad habits worse.

The most common story goes like this: a developer hardcodes a WiFi password to test a cloud integration. The feature works. The password stays. Six months later the device ships to a hundred thousand customers. The password is still there. Nobody removed it because:

  1. CI/CD pipelines commit secrets silently. When credentials are set as environment variables in build scripts, they can end up baked into build artifacts. Automated builds do not warn you.
  2. The "I'll remove it before release" rule does not hold. Code that makes it to QA almost always makes it to production. There is no gate between "working internally" and "shipping."
  3. Testing accounts become production accounts. A backdoor added for factory testing — admin:admin, a hardcoded SSH key, a maintenance API endpoint — is left in because removing it requires changes to test infrastructure that nobody schedules.
  4. Device provisioning is hard. Generating unique credentials per device requires manufacturing-line infrastructure. Hardcoding a shared credential is the easy path, and the easy path gets taken.

The consequence for you as a researcher: the firmware you extract is almost certainly carrying secrets. The question is not whether — the question is where.

The 7 Locations Where Secrets Live

1. Config Files (/etc/config/, *.conf, *.cfg, *.ini)

Configuration files are the most obvious location. Developers put credentials here because it seems "configurable." In practice they are shipped with defaults that are never changed.

Common targets: - /etc/config/wireless — WiFi PSK in OpenWrt-based devices - /etc/wpa_supplicant.conf — WiFi credentials - /etc/mosquitto/mosquitto.conf — MQTT broker credentials - Any *.ini file under /etc/ or /usr/etc/

Look for: password=, psk=, key=, secret=, token=.

2. Init Scripts (/etc/init.d/, S??* startup scripts)

Init scripts run at boot and frequently launch services with credentials passed on the command line or set as environment variables. These are shell scripts — everything is readable.

/etc/init.d/telnet → starts telnetd with a hardcoded password
/etc/rc.d/S50dropbear → may contain an authorized key inline
/etc/init.d/cloud_agent → may export API_KEY= before launching a binary

The S??* naming convention (e.g., S50dropbear, S80cloud) is BusyBox/OpenWrt. These are the scripts that actually run on boot. Check every one.

3. Web Application Files (/www/, *.js, *.php, *.cgi)

Embedded web servers host admin interfaces. Those interfaces are written in PHP, CGI (shell scripts compiled or not), or JavaScript. All of these formats are plaintext.

Common finds: - PHP config files with database passwords - CGI scripts that authenticate by comparing a hardcoded string - JavaScript files that embed API endpoints with authentication tokens - .htpasswd files with hashed credentials (crackable offline)

4. Application Binaries (.data and .rodata sections)

When a developer writes const char *key = "sk-live-abc123"; in C, that string is compiled into the .rodata (read-only data) section of the ELF binary. It is not encrypted. It is not obfuscated. It sits in the binary as a null-terminated ASCII string, readable with strings.

This is the most common location for secrets on devices that do not expose their config files. The binary is where you look when the filesystem search comes up empty.

5. Shared Libraries (*.so files)

Libraries implement authentication, cloud connectivity, and crypto operations. A shared library handling cloud API calls often contains the API endpoint URL and the authentication token burned in at compile time.

/usr/lib/libcloud.so → hardcoded endpoint + auth token
/usr/lib/libwifi.so → WPA supplicant helper with default PSK
/lib/libcrypto_vendor.so → hardcoded certificate or key material

Libraries are frequently overlooked because researchers focus on executables. They are ELF binaries — strings works on them identically.

6. Database Files (*.db, *.sqlite)

SQLite databases are common in embedded Linux devices for storing user accounts, device configuration, and logs. A SQLite file is not a binary format in the sense of compiled code — it is a structured flat file with readable text pages.

strings device.db | grep -iE "pass|user|token"
# or
sqlite3 device.db ".dump" | grep -iE "admin|pass|key"

Find databases with:

find . -name "*.db" -o -name "*.sqlite" -o -name "*.sqlite3"

7. Non-Volatile Storage Formats (NVS, NVRAM)

Devices store persistent configuration in non-volatile storage. On flash-based embedded Linux, this takes several forms:

  • NVRAM (Broadcom): a flat key=value store. On extracted firmware, look for a partition image or a file named nvram, nvram.bin, or similar. Keys like wan_pppoe_password, wl0_wpa_psk are standard Broadcom NVRAM variables.
  • ESP32 NVS: a structured flash format used by Espressif devices. Stores WiFi credentials, device tokens, certificates. Can be parsed with nvs_parser or the esp-idf-nvs-partition-gen tooling.
  • JFFS2 config partitions: on many devices, user config lives in a separate JFFS2 partition appended to the firmware image. jefferson extracts JFFS2.

Types of Secrets to Hunt

Secret type What it looks like Where it hides
WiFi PSK 8–63 printable ASCII characters /etc/config/wireless, NVRAM, binaries
Root password hash $1$ (MD5), $6$ (SHA-512) /etc/shadow
API key 32–64 alphanumeric characters Binaries, JS files, config
OAuth token ya29., Bearer, long base64 Binaries, JS, init scripts
AWS access key AKIA followed by 16 uppercase chars Any text file or binary
Private key -----BEGIN RSA PRIVATE KEY----- PEM files, embedded in binaries
Device certificate -----BEGIN CERTIFICATE----- PEM files, /etc/ssl/
Backdoor account Username/password pair /etc/passwd, /etc/shadow, CGI
MQTT credentials username + password Mosquitto config, init scripts

Real CVEs: This Is Not Theory

CVE-2019-16920 — D-Link multiple routers Unauthenticated command injection through a CGI endpoint. The endpoint called pingtest_action.cgi accepted a target IP with no authentication and executed system commands. The credentials for the admin interface were hardcoded in the CGI binary itself. Affected: DIR-655, DIR-866L, DIR-652, DHP-1565.

CVE-2021-35395 — Realtek Jungle SDK The Realtek SDK, used by dozens of vendors (Netgear, D-Link, Belkin, and others), contained hardcoded credentials in the web management interface. The credentials were embedded in the webs binary and used for HTTP Basic authentication. Because the SDK was shared across vendors, a single CVE affected over 65 device models from multiple manufacturers simultaneously.

Both cases: secrets found by running strings against a firmware binary, confirmed by checking the CGI/binary source references, reported as critical (CVSS 9.8).

Why Extraction Makes This Possible

A SquashFS root filesystem, once extracted with unsquashfs, is a standard directory tree. File permissions are preserved but irrelevant — you are reading the files as the researcher on your own machine, not as a user on the device. There is no access control stopping you from reading /etc/shadow or any binary in /usr/bin/.

Binaries are not encrypted (on almost all consumer devices). The strings they contain are readable. The sections of an ELF binary that hold constant data (.rodata, .data) are there in full. This is why the techniques in this course require no special privileges and no reverse engineering toolchain beyond strings and grep.

The only exception is firmware-level encryption, which is increasingly common on newer, security-conscious devices. When encryption is present, the techniques here do not apply until you recover the key. That is a separate problem. For the majority of deployed IoT devices — and especially older, unpatched ones — encryption is absent.