SNARKeling Treasure Hunt

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

Potential Private Key Leakage via Environment Variable Usage

Author Revealed upon completion

Root + Impact

Description

The script programmatically accesses a sensitive private key stored in the system's environment variables.

There are three primary ways this leads to theft:

  1. Shell History: If the key is set via export PRIVATE_KEY=..., it is saved in plaintext in ~/.bash_history or ~/.zsh_history.

  2. Version Control Leakage: If the key is stored in a .env file and that file is accidentally committed to a public repository (e.g., forgetting to update .gitignore), bots will scrape and drain the wallet instantly.

  3. Process Inspection: Environment variables can sometimes be read by other processes running on the same machine, increasing the attack surface for malware.

// Root cause in the codebase
function run() external {
@> uint256 deployerKey = vm.envUint("PRIVATE_KEY"); // Key loaded into memory from environment
// ...
vm.startBroadcast(deployerKey);
}

Risk

Likelihood: Medium

  • Common "copy-paste" deployment commands often lead to keys being left in terminal history.

  • Accidental .env uploads are one of the most frequent causes of lost funds in web3 development.

Impact: Critical

  • Total loss of all assets held by the deployer address.

  • Immediate compromise of the TreasureHunt contract's admin privileges.

Proof of Concept

  1. Developer runs export PRIVATE_KEY=0x123... in their terminal.

  2. Developer runs the forge script.

  3. An attacker gains access to the machine (or a public repo where the history was leaked) and runs cat ~/.bash_history.

  4. The attacker finds the export command, imports the key, and drains the account.

Recommended Mitigation

Use Foundry Keystores instead of environment variables. Keystores store the key in an encrypted file on your disk that requires a password to unlock during the broadcast phase.

# 1. Import key securely (one-time setup)
cast wallet import myDeployerAccount --interactive
# 2. Run script using the account name (removes need for PRIVATE_KEY in script)
forge script script/Deploy.s.sol --rpc-url <URL> --account myDeployerAccount --sender <ADDRESS> --broadcast

Update the script to remove the envUint requirement:

- uint256 deployerKey = vm.envUint("PRIVATE_KEY");
- vm.startBroadcast(deployerKey);
+ vm.startBroadcast(); // Uses the account provided in the CLI command

Support

FAQs

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

Give us feedback!