SNARKeling Treasure Hunt

First Flight #59
Beginner FriendlyGameFiFoundry
100 EXP
Submission Details
Impact: low
Likelihood: high

[L-06] Deployment script depends on plaintext `PRIVATE_KEY` env variable instead of Foundry keystore flow #

Author Revealed upon completion

Root + Impact

Description

The deployment script currently reads the signer from a plaintext environment variable:

// contracts/scripts/Deploy.s.sol
uint256 deployerKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerKey);

This workflow encourages handling raw private key material in shell/session environment and CI logs/process context. Even when .env is gitignored, plaintext env-based key handling is an avoidable secret-exposure surface.

Foundry already supports encrypted keystores, which avoid keeping raw keys in plaintext:

  • cast wallet import account_name --interactive

  • use deployment with --account <keystore_name> (password-gated decrypt at use time)

Risk

Likelihood:

  • Environment-based secrets are commonly exposed via shell history, process inspection, misconfigured CI logging, or accidental debugging output.

  • Current script path makes env key usage the default pattern for operators.

Impact:

  • Private key compromise enables full signer takeover and malicious deployments/transactions.

  • Incident response is operationally costly (key rotation, redeploy, trust and config migration).

Severity rationale:

  • Classified as Low for this codebase because this is deployment operational security hardening rather than an on-chain logic flaw.

Proof of Concept

Written reproduction flow:

  1. Operator exports raw key and runs deployment:

    • export PRIVATE_KEY=0x...

    • forge script contracts/scripts/Deploy.s.sol:Deploy --broadcast

  2. Any local process/telemetry path that captures env/process context can recover the key.

  3. Recovered key can be used to sign arbitrary transactions as deployer.

# Current plaintext-key workflow (discouraged)
export PRIVATE_KEY=0xabc123...
forge script contracts/scripts/Deploy.s.sol:Deploy --rpc-url <RPC> --broadcast
# Recommended encrypted keystore workflow
cast wallet import deployer --interactive
forge script contracts/scripts/Deploy.s.sol:Deploy --rpc-url <RPC> --account deployer --broadcast

Recommended Mitigation

Use Foundry keystore-based signing as the primary workflow and remove PRIVATE_KEY dependency from script logic/docs.

- uint256 deployerKey = vm.envUint("PRIVATE_KEY");
- address deployer = vm.addr(deployerKey);
- vm.startBroadcast(deployerKey);
+ // Use Foundry keystore signer from CLI:
+ // forge script ... --account <keystore_name> --broadcast
+ vm.startBroadcast();

Operational guidance:

  • Import keys with cast wallet import <name> --interactive.

  • Prefer --account <name> over plaintext env keys.

  • Avoid storing raw private keys in files inside working directories.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!