Files
2026-09-12 01:29:25 +00:00

3.7 KiB

Baked config & OPSEC

cmd/build compiles implant configuration straight into the binary, so a field deployment is one artifact instead of an env-var bundle. This note covers how that config is protected on disk and on the wire, what it does and does not guarantee, and how to verify it.

The problem it fixes

Earlier builds injected each value as its own plaintext symbol:

-ldflags "-X main.bakedC2URLs=https://c2.example:8443 \
          -X main.bakedBotSecret=s3cret \
          -X main.bakedBotKey=<implant-priv-hex>"

-ldflags -X assigns a Go string variable, and string data lives verbatim in the binary. A single strings pass recovered the C2 endpoints, the shared bot secret and the implant private key from the shipped artifact — no reverse engineering required. That is convenience, not OPSEC.

What we do now

All baked values are packed into one opaque token (internal/bakepack) and injected as a single string, main.bakedBundle:

Mode Token Mechanism Guarantee
obfuscated (default) b1.<base64url> salt(16) ‖ json XOR SHA-256(seed‖salt‖ctr) secrets are not present as readable strings in the binary
sealed (-seal PASS) b2.<base64url> salt(16) ‖ nonce(12) ‖ AES-256-GCM(json), key = PBKDF2-HMAC-SHA256(PASS, salt, 210k) authenticated encryption: the binary alone discloses nothing and is inert without the passphrase

Obfuscated (b1)

Defeats casual triage, strings/grep, AV/EDR static string scanners and config hunters. It is obfuscation, not encryption: someone who reverses the token format can recover the values. Use it when you only need the artifact to not hand over its config on a plate.

Sealed (b2)

Real AEAD under an operator passphrase that is supplied out-of-band at runtime via SWIZ_UNLOCK. The binary embeds only ciphertext + KDF salt, so a recovered artifact is useless without the passphrase. A wrong or missing SWIZ_UNLOCK fails authentication — the implant does not silently fall back to a baked secret it could not decrypt; it logs and continues on env/defaults. This is the mode to use for real OPSEC.

Note: no self-contained artifact can hide a secret from someone who holds both the binary and the runtime secret. b2 moves the secret out of the binary and into operator custody; it does not put it nowhere.

Precedence

env (SWIZ_*)  >  baked (token)  >  built-in default

Env always wins, so an operator can override any baked value without a rebuild. A sealed bundle that cannot be unlocked simply contributes nothing.

Building

# obfuscated (default)
go run ./cmd/build -c2-urls https://c2.example:8443 \
  -bot-secret s3cret -bot-key <implant-hex> -c2-pubkey <op-hex>

# sealed -> zero-leak; set SWIZ_UNLOCK at runtime
go run ./cmd/build -seal 'correct horse battery staple' \
  -c2-urls https://c2.example:8443 \
  -bot-secret s3cret -bot-key <implant-hex> -c2-pubkey <op-hex>

Baking secrets without -seal prints a warning.

Verifying

make leakscan (or scripts/leakscan.sh) builds the implant three ways and asserts the canary secrets are absent from the binary in both bake modes, using a deliberate plaintext negative control to prove the scanner works.

$ make leakscan
==> 1/4 negative control ... PASS control: control canary detected (scan works)
==> 2/4 obfuscated bake (b1) ... PASS obfuscated: no plaintext canaries in binary
==> 3/4 sealed bake (b2) ... PASS sealed: no plaintext canaries in binary
==> 4/4 env still overrides baked ... PASS
ALL LEAK CHECKS PASSED

Unit coverage lives in internal/bakepack/bakepack_test.go: round-trip for both modes, passphrase required/wrong/tamper rejection, and a plaintext-absence regression guard.