SNARKeling Treasure Hunt

First Flight #59
Beginner FriendlyGameFiFoundry
100 EXP
View results
Submission Details
Severity: high
Valid

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

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.

Updates

Lead Judging Commences

s3mvl4d Lead Judge 18 days ago
Submission Judgement Published
Validated
Assigned finding tags:

plaintext private key stored in .env

The deployment workflow pulls a raw EOA private key directly from the environment with `vm.envUint("PRIVATE_KEY")` and then uses it in `vm.startBroadcast(deployerKey)`, meaning the signing key is handled as an unencrypted scalar rather than through a safer account abstraction or keystore-backed signer. It is vulnerable to accidental commit, shell/session leakage, workstation compromise, or exposure to any process that can read the environment, turning compromise of the deploy machine into immediate compromise of the deployer account itself.

Support

FAQs

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

Give us feedback!