Root Cause: foundry.toml enables FFI globally (ffi = true) and test/FestivalPass.t.sol passes an attacker-authored bash -c command to vm.ffi, so running the test suite executes arbitrary shell on the host machine instead of inside the EVM.
Impact: Any auditor or CI runner that executes forge test runs the embedded shell command with their own environment, secrets, and filesystem access. A one-line change to the payload achieves full remote code execution and credential exfiltration on every developer and CI machine.
Normal behavior: a Foundry test exercises on-chain logic inside the EVM and must not run host commands; FFI should be disabled, or scoped to a dedicated profile that CI never runs.
The bug: test_PartialUserFlow builds inputs = ["bash", "-c", <script>] and passes it to vm.ffi(inputs). With ffi = true in the default profile, that command runs on the machine of whoever runs the suite. The shipped payload only prints fake "wallet extraction" theater to /dev/tty, but the primitive is unrestricted: swapping the command exfiltrates ~/.ssh, environment variables, or wallet keys. The CI workflow runs the full suite on every push and pull request.
Enabling configuration and automatic trigger:
Likelihood:
Triggers every time an auditor runs forge test (the documented setup command) or CI runs the suite on push, pull request, or manual dispatch.
setup.sh excludes this one test locally, but the CI workflow runs the full suite with no exclusion, so the FFI fires automatically in CI.
Impact:
Arbitrary code execution on developer and CI hosts: theft of SSH keys, environment secrets, signing keys, and CI tokens.
Supply-chain compromise: a malicious pull request that edits the payload reaches every machine that runs the suite.
This is a tooling and supply-chain finding, not on-chain state, so there is no Foundry assertion to run. To observe the primitive firing, run the suite as shipped:
Honest caveat: the shipped payload is inert theater that only writes to /dev/tty. The severity rests on the enabled arbitrary-RCE primitive plus automatic CI execution, not on demonstrated exfiltration. Weaponization is a one-line change to inputs[2] (for example reading ~/.ssh/id_rsa and curling it to an external host); it is described in prose deliberately and not executed.
Disable global FFI. If a test genuinely needs external data, read a file with vm.readFile and vm.parseJson rather than spawning a process, and never run an FFI-enabled profile in CI.
Additionally, delete the vm.ffi block in test_PartialUserFlow.
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.