Hardcoded BIP39 mnemonic seed phrase used to derive Ethereum private key
A real 12-word BIP39 mnemonic is embedded as a string literal and used to derive an Ethereum wallet's private key inside a request handler.
In routes/checkKeys.ts, the handler embeds a hardcoded BIP39 mnemonic:
const mnemonic = 'purpose betray marriage blame crunch monitor spin slide donate sport lift clutch'
const mnemonicWallet = HDNodeWallet.fromPhrase(mnemonic)
const privateKey = mnemonicWallet.privateKey
const publicKey = mnemonicWallet.publicKey
const address = mnemonicWallet.address
The mnemonic is a valid BIP39 phrase (12 words from the wordlist) that deterministically derives a real Ethereum wallet (address, public key, and private key). It is committed to source control rather than being read from process.env, a secret manager, or any out-of-band configuration. Anyone with access to the repository can reconstruct the wallet and sign transactions / access any funds or NFTs held by that address.
The derived privateKey is then used in a security-relevant comparison (req.body.privateKey === privateKey) to gate a challenge response, confirming it is treated as a sensitive credential by the code itself.
The correct pattern is to load such a seed phrase from an environment variable or secret manager (or, for a demo wallet, generate a throwaway one at process start and never persist it in source).
- Clone the repository and open
routes/checkKeys.ts. - Copy the mnemonic literal:
purpose betray marriage blame crunch monitor spin slide donate sport lift clutch. - In a Node REPL:
const { HDNodeWallet } = require('ethers'); const w = HDNodeWallet.fromPhrase('purpose betray marriage blame crunch monitor spin slide donate sport lift clutch'); console.log(w.privateKey, w.address); - The attacker now controls the wallet — they can sign transactions, transfer any ETH/tokens/NFTs held at that address, and trivially solve the
nftUnlockChallengeby POSTing the derived private key to the endpoint.
Anyone with read access to the repository can derive the full Ethereum private key for the wallet and assume full control of any assets associated with that address (ETH, ERC-20 tokens, NFTs). No authentication is required — the secret is in the source tree. If this wallet holds any value on-chain, those funds are unrecoverable once leaked.
The literal const mnemonic = 'purpose betray marriage blame crunch monitor spin slide donate sport lift clutch' is a valid 12-word BIP39 phrase committed to source, and HDNodeWallet.fromPhrase(mnemonic).privateKey deterministically yields a real Ethereum private key. Anyone with repo read access can reproduce the wallet and sign transactions or drain any assets held by the derived address. The scope explicitly instructs treating Juice Shop findings as production bugs, so the intentional-challenge nature does not mitigate this. The detector's PoC (running HDNodeWallet.fromPhrase on the literal) works exactly as described.
The 12-word BIP39 mnemonic is embedded as a string literal at routes/checkKeys.ts line 10 and is shipped with the source tree; anyone who can read the repository (public Git host, leaked archive, or the deployed bundle) recovers the private key with HDNodeWallet.fromPhrase(...) — no authentication, user interaction, or special conditions required, hence AV:N / AC:L / PR:N / UI:N. Scope is Changed because the leaked key controls an Ethereum wallet on the blockchain — a security authority entirely separate from the Express app where the literal lives — so impact crosses the component boundary. Confidentiality, Integrity, and Availability are all High with respect to that wallet: the attacker fully discloses the private key, can sign arbitrary transactions, and can drain any ETH/ERC-20/NFT assets, rendering them unrecoverable. (Within the app itself the only direct effect is trivially solving nftUnlockChallenge via req.body.privateKey === privateKey, but the Changed scope correctly captures the dominant wallet-takeover impact.)
- CWE-798: Use of Hard-coded Credentials
- CWE-540: Inclusion of Sensitive Information in Source Code
- OWASP A07:2021 - Identification and Authentication Failures