⏱️ Lectura: 11 min
A security researcher extracted the firmware of a Hanwha Vision camera and found, duplicated across nearly 30 different files, a GitHub token with admin permissions over hundreds of the company’s private repositories.
📑 En este artículo
- TL;DR
- Introduction
- What Happened: The GitHub Token in the Firmware
- Context and Background
- Technical Details and Impact
- How to Start Auditing Your Own Firmware
- Impact and Analysis
- What’s Next
- Frequently Asked Questions
- What is trufflehog and why do security researchers use it?
- Why did the GitHub token have admin permissions over hundreds of repositories?
- How do I prevent Vite from exposing the full process.env in the final bundle?
- Did Hanwha Vision respond publicly to the finding?
- What real relationship does Hanwha Vision have with the U.S. Department of Defense?
- How do I protect my CI/CD pipeline’s secrets so they don’t end up in a frontend build?
- References
The discovery didn’t come from a sophisticated attack: the token was embedded in the JavaScript bundle of the admin interface that the camera itself serves to anyone who reaches its configuration login screen. Anyone with access to that login page could have unknowingly received the secret.
TL;DR
- A researcher extracted the firmware of a Hanwha Vision camera and found a GitHub token duplicated in about 30 files.
- The token had admin permissions over hundreds of private repositories belonging to Hanwha’s GitHub organization.
- Root cause: the Vite build dumped the entire process.env, including the token, into the camera’s admin interface JS bundle.
- To reach the bundle, the researcher had to deobfuscate an AES-256-CBC encryption layer inside the fwupgrader binary.
- The researcher scanned nearly 500 different Hanwha firmware images to confirm the issue wasn’t an isolated case.
- The firmware also revealed environment variables with IPs assigned to the U.S. Department of Defense, without official explanation.
- Hanwha Vision started out as Samsung Techwin and is part of Hanwha Group, alongside defense-sector siblings like Hanwha Aerospace.
Introduction
Security cameras running embedded Linux stopped being isolated appliances a while ago. Manufacturers like Axis push toward every camera running full Linux applications, turning them into serious targets inside a corporate network: they need vulnerability and credential management just like any server. A researcher who had been following this trend came across Hanwha Vision, a brand with publicly downloadable firmware for each camera model, and decided to look inside.
What Happened: The GitHub Token in the Firmware
The researcher downloaded the firmware image and ran it through binwalk, the standard tool for extracting embedded filesystems. Inside appeared a separate tarball with AI components for the camera and a fwimage.tgz file that binwalk flagged as encrypted.
Prior research by another security researcher, Matt Brown, had already documented that the encryption password for these cameras follows the pattern HTW plus the model number (for example HTWXNP-9300RW). That first layer opened without trouble. But inside was a second fwimage.tgz, encrypted with a different scheme that no longer responded to Brown’s technique.
The missing piece was in a binary called fwupgrader, responsible for applying the update. There, Hanwha had added an obfuscation layer: the AES key was split against a static table inside the binary and reconstructed at runtime (the IV, on the other hand, traveled in plain text). fwupgrader itself didn’t implement the encryption by hand: it invoked the openssl CLI, and even the command fragments were obfuscated with the same XOR trick.
With the command reconstructed and the key rebuilt, the researcher was able to decrypt the second fwimage.tgz and finally reach a complete rootfs. The first step there was running trufflehog over the filesystem to look for obvious secrets. A GitHub token turned up, repeated across nearly 30 files. Checking that token’s permissions against the GitHub API confirmed it had admin privileges over hundreds of repositories in Hanwha’s organization.
Context and Background
This wasn’t the first time the researcher had found a leaked GitHub token in someone else’s firmware, though this time what stood out was the why. The token wasn’t sitting in a single config file: it appeared repeated because the camera’s admin interface is built with Vite, and someone had configured the build to dump the entire process.env object into a bundle variable. That means everything present in the CI job’s environment (not just variables intended for the client) ended up copied, file by file, into the JavaScript that the camera serves to anyone who reaches its login page.
Among those variables was also a handful of IPs assigned to the U.S. Department of Defense, in addresses resembling internal SWARM_MASTER_NFS_ADDRESS or OTEL_ELASTIC_URL values. It could be a coincidence (companies reserving internal IP ranges that will never collide with real traffic is a more common practice than it should be), or it could reflect that Hanwha Vision’s CI infrastructure is shared with other units of the group. Hanwha Vision started out as Samsung Techwin and is today a subsidiary of Hanwha Group, a South Korean conglomerate whose other companies include manufacturers of self-propelled artillery and the armed sentry robot SGR-A1. The researcher himself flagged this part of the analysis as speculation: there’s no evidence that Hanwha Vision has direct operational ties to the Pentagon, only the coincidence of those IP addresses in its CI’s environment variables.
📌 Note: the finding of IPs associated with the DoD is circumstantial. The author of the original research himself labeled it as speculation, not proof of a direct link.
Technical Details and Impact
The underlying problem isn’t exotic: it’s a known misconfiguration pattern in Vite frontend builds. When a project uses define to expose variables to client code, it’s easy to end up copying the entire process.env object instead of listing only what’s needed.
// vite.config.js (INSECURE)
import { defineConfig } from 'vite'
export default defineConfig({
define: {
'process.env': JSON.stringify(process.env)
}
})
With that configuration, any variable present in the CI job’s environment (npm tokens, deployment credentials, third-party keys) ends up serialized inside the JavaScript file served to the browser (or, in this case, to the web interface embedded in the camera). The code doesn’t even need to use it: it’s enough that it exists in the process where vite build runs.
// vite.config.js (safe)
import { defineConfig } from 'vite'
export default defineConfig({
define: {
__APP_VERSION__: JSON.stringify(process.env.npm_package_version),
__BUILD_DATE__: JSON.stringify(new Date().toISOString())
}
})
Vite’s recommended alternative is its own prefixed environment variable mechanism using VITE_: only those variables reach import.meta.env on the client, and everything else stays out of the bundle by default.
| Vite method | What ends up in the client bundle | Risk |
|---|---|---|
define: { 'process.env': JSON.stringify(process.env) } | All CI environment variables, including tokens | High: any secret from the job ends up in public JS |
VITE_ prefix + import.meta.env | Only variables starting with VITE_ | Low: exposes only what the team explicitly marked as public |
define with specific keys | Only manually listed values (version, build date, etc.) | Low: explicit control, no accidental dump |
The diagram below summarizes the path the secret took: from a CI pipeline environment variable to a public file served by every camera sold.
flowchart TD
A["Manufacturer's CI/CD"] --> B["Environment variable: GITHUB_NPM_TOKEN"]
B --> C["vite build dumps entire process.env"]
C --> D["Admin interface JS bundle"]
D --> E["Camera firmware"]
E --> F["Admin panel served by the camera"]
F --> G["Whoever accesses the panel receives the token"]
How to Start Auditing Your Own Firmware
You don’t need a Hanwha camera on hand to apply the same technique to your own build artifacts. The basic recipe: extract, decode if needed, and scan with a secrets detection tool.
Installing trufflehog
# Linux (official script)
curl -sSfL https://raw.githubusercontent.com/trufflesecurity/trufflehog/main/scripts/install.sh | sh -s -- -b /usr/local/bin
# macOS (Homebrew)
brew install trufflehog
# Windows (with Scoop, from PowerShell)
scoop install trufflehog
With the binary installed, the minimal scan over an extracted directory is straightforward:
trufflehog filesystem ./extracted-firmware --results=verified,unverified
That command looks for known credential patterns (GitHub tokens, AWS keys, npm credentials, among dozens of formats) and, when possible, verifies against the service’s API whether the secret is still active. To audit your own repository before each build, it’s worth running it as part of the CI pipeline too, not just manually.
💡 Tip: run the secrets scan on the final build artifact (the bundle, the container image, the firmware), not just on the source code. Many leaks of this kind aren’t in the repo: they only show up at the packaging step.
If you work with Vite, a quick and free check is searching the generated bundle for the full process.env string appearing instead of specific variable names:
npm run build
grep -c "process.env" dist/assets/*.js
A high result from that grep is a sign it’s worth reviewing vite.config.js before publishing.
Impact and Analysis
The researcher didn’t stop at a single case: to confirm it wasn’t a fluke, he scraped Hanwha’s site and downloaded nearly 500 different firmware images, covering most of the brand’s camera lineup. The root cause (the Vite configuration dumping the entire process.env) is the kind of bug that, once introduced into a build template shared across models, replicates automatically in every new release without anyone noticing.
The real risk isn’t just that someone sees the token: it’s what they can do with admin permissions over hundreds of private repositories. That potentially includes pushing changes to those repos, rotating collaborators, or inserting malicious code into a future build, exactly the kind of access relevant to a supply chain attack. The researcher noted this wasn’t the first time he’d found a leaked GitHub token in a company’s firmware, suggesting the pattern (CI variables dumped unfiltered into a client build) is more common than the IoT industry would like to admit.
What’s Next
The expected outcome after a finding like this is immediate rotation of the exposed token and an audit of what was done with it while it was active, something only Hanwha can confirm internally. For the rest of the industry, the case is another reminder that IoT device security doesn’t end at the hardware: the pipeline that compiles and signs the firmware is part of the attack surface, and any CI job environment variable can end up, unbidden, inside the final product.
📖 Summary on Telegram: View summary
Try it yourself: run trufflehog filesystem . on your latest build’s bundle before publishing it and confirm it doesn’t carry along any of your CI variables.
Frequently Asked Questions
What is trufflehog and why do security researchers use it?
It’s an open source secrets scanner that looks for credential patterns (tokens, API keys, passwords) in source code, filesystems, and repositories, and also tries to verify against the corresponding service’s API whether a found secret is still active.
Why did the GitHub token have admin permissions over hundreds of repositories?
The original article doesn’t detail Hanwha’s internal policy for generating the token, but the broad scope (admin over hundreds of repos) is consistent with using a single organization-wide token for multiple CI pipelines, instead of short-lived tokens with per-repository permissions.
How do I prevent Vite from exposing the full process.env in the final bundle?
Avoid using define: { 'process.env': JSON.stringify(process.env) }. Instead, use variables with the VITE_ prefix, which Vite explicitly exposes through import.meta.env, or manually list the specific keys you need to inject.
Did Hanwha Vision respond publicly to the finding?
The original article, published on hhh.hn, doesn’t report a public response from Hanwha Vision as of this article’s publication.
What real relationship does Hanwha Vision have with the U.S. Department of Defense?
None confirmed. The finding of IPs associated with DoD ranges in its CI environment variables is circumstantial, and the researcher himself marked it as speculation, not evidence of an operational link.
How do I protect my CI/CD pipeline’s secrets so they don’t end up in a frontend build?
Use minimally scoped CI variables (one token per pipeline, not one shared across the whole organization), rotate them periodically, and add a secrets scan like trufflehog or gitleaks as a mandatory step before publishing any artifact, whether it’s a web bundle or a firmware image.
References
- hhh.hn: My security camera shipped a GitHub admin token in its login page: the researcher’s original analysis, with the full firmware extraction and deobfuscation process.
- GitHub: trufflesecurity/trufflehog: repository of the secrets scanner used to detect the leaked token.
- Vite: Env Variables and Modes: official documentation on the VITE_ prefix and how Vite decides which variables to expose to the client.
- Wikipedia: Hanwha Vision: the company’s history, formerly Samsung Techwin, and its relationship to Hanwha Group.
- OWASP Secrets Management Cheat Sheet: reference guide for preventing CI secrets from ending up in build artifacts.
📱 Enjoy this content? Follow @programacion on Telegram for daily tech content in Spanish: quick summaries, fresh content every day. @programacion
Imagen destacada: Foto de Harrison Broadbent en Unsplash
0 Comments