Hardcoded API Key in Smart Home App, Account Creation, S3 Takeover, and Mass Lockout
The Risk
A single key hidden inside a smart home camera platform's Android app gave anyone who extracted it the ability to create real production accounts at will, send official password reset emails to any customer's inbox, and lock every customer out of their own account. The same key also opened a production cloud storage bucket for full read, write, and delete access. Evidence in the bucket showed it had been reachable for at least two years, with web shells and probe payloads from earlier outsiders already sitting inside.
The Vulnerability
The Android app of a smart home camera platform shipped a hardcoded API key inside a native library. The key, combined with hardcoded service account credentials also extracted from the same library, unlocked a chain of production-impacting operations on the company's API gateway with no end-user authentication required.
Root cause
A configuration loader in the app loaded the native library at startup. A native function returned a set of base64-encoded credentials decoded by a companion class. The decoded API key was then sent as an HTTP header on every backend request. The same key on the backend gateway granted access to over thirty PHP services that operated on the production user database, plus full read, write, and delete access to a production cloud storage bucket, all without an end-user session.
Five distinct primitives unlocked by one key
- Arbitrary account creation on the production platform with a usable access token returned and a real welcome email delivered
- Full read, write, and delete access on a production cloud storage bucket
- Mass account lockout: three failed authentication requests against any email address moved that account into a locked state
- Unauthenticated password-reset email triggering: real reset emails delivered to any address with a working production reset link
- Production infrastructure disclosure including cluster names, IAM role names, hostname patterns, and the names of 29 internal applications
The Attack
Reproduced end to end on a rooted Android device, automated through a single script that exercised each step.
Extract the key from the native library
The native library was hooked at startup with a runtime instrumentation tool. The credential class constructor was intercepted and all decoded fields dumped, including the API key, the API base URL, and the service account username and password.
Register a real account
Using the API key plus a service-account session token, a registration call returned HTTP 200 with a fully authenticated access token, a user ID, and an "auth completed" flag. A welcome email was delivered to the catch-all inbox confirming the account was live on the production platform.
Lock any account with three requests
Three failed authentication attempts against a target email address transitioned the account from "password not correct" to "account locked". The legitimate user could not log back in until the lockout expired or an admin intervened. At scale, an attacker could lock every customer simultaneously.
Full CRUD on the production bucket
Files could be created, overwritten, read, and deleted in the production storage bucket through the API gateway. Overwrite was confirmed by writing a file, overwriting it, reading it back to verify the new content, and deleting it.
Read the bucket contents
The bucket contained 192 files. Notable items:
- An ECS cloud-init script revealing the production cluster name, hostname pattern, and Docker config
- A file listing the production IAM role name attached to the API gateway tasks
- An expired set of cached AWS access keys from an EC2 instance metadata snapshot
- Web shells uploaded by earlier researchers in 2024 (one-line PHP shells executing arbitrary commands)
- Out-of-band callback payloads from automated scanners dated 2024 and 2025
The web shells and old probe payloads confirmed the same attack surface had been reachable to outsiders for over two years.
Trigger official password reset emails
A request to the password reset endpoint with only the API key in the headers and the target email in the body returned HTTP 200 and delivered a real reset email containing a working production reset link. Sequential requests produced unique random codes, so codes were not predictable, but the endpoint allowed unlimited triggering of official-looking emails to any address.
Enumerate the deployment
A status endpoint returned the gateway version, license tier, last admin login timestamp, and resource counts: 29 applications, 22 admins, 16 users, 124 services, 22 roles. An environment endpoint returned the names of all 29 internal applications.
The Impact
| Finding | Proven | Impact |
|---|---|---|
| Arbitrary account creation | HTTP 200 + access token + welcome email | Unlimited fake accounts at scale |
| Production bucket overwrite and delete | HTTP 201 write, read-back confirmed, HTTP 200 delete | Tamper with or destroy production data, supply-chain risk |
| Account lockout in three requests | Error code transition from invalid password to locked | Mass denial of service against the entire customer base |
| Password reset email triggering | HTTP 200 + real email delivered with valid reset link | Phish any customer with official emails at scale |
| Infrastructure disclosure | Cluster, IAM role, hostname patterns, 29 internal apps | Reconnaissance for further cloud attacks |
| Prior exploitation evidence | Web shells and scanner payloads in bucket from 2024 to 2025 | Attack surface exposed for 2+ years |
Remediation
- Rotate the hardcoded API key immediately. The current key is embedded in every copy of the app on every customer device
- Remove or restrict the registration endpoint so it cannot be called with only an API key and a service-account session
- Rate-limit the authentication endpoint so failed attempts from a single API key cannot lock accounts. Do not lock accounts based on failed attempts originating from unauthenticated, API-key-only callers
- Restrict the storage bucket permissions through gateway role configuration. The API key role should not have write or delete access
- Purge the storage bucket of web shells, scanner payloads, and any researcher PoC files
- Rotate all third-party service account credentials exposed in the same native library
- Move credentials out of the app entirely. Use a server-side token exchange or runtime credential fetch tied to the user's session, not a static key shipped to every device