T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/setup.mjs:52
- Finding
- Generated Wallet Private Key Is Not Protected by Enforced File Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup.mjs:52-55` **Vulnerability Type**: Plaintext credential storage with insecure default permissions **Risk Level**: High ### Vulnerable Code ```js const pk = generatePrivateKey(); const account = privateKeyToAccount(pk); _mkdirSync(_envDir, { recursive: true }); _appendFileSync(_envFile, `\nPRIVATE_KEY=${pk}\n`); ``` ### Technical Analysis The setup script generates a secp256k1 private key and stores it in plaintext at `~/.openclaw/.env`. Neither the directory nor the file is created with an explicit restrictive mode. When `appendFileSync` creates a file, its effective permissions ordinarily derive from a default mode such as `0666`, filtered through the process umask. Under a common `022` umask, the resulting file may be `0644`, making the wallet key readable by other local users. Likewise, `mkdirSync` does not explicitly require mode `0700`. This conflicts with the security guidance in `SKILL.md:55`, which advises users to apply `chmod 600`, but the setup routine does not enforce that protection. Because the same key may control real Base mainnet assets, reliance on the caller's umask is not an adequate security boundary. The use of `appendFileSync` also lacks explicit checks against a pre-existing symbolic link. If an attacker who can manipulate the target path prepares an appropriate symlink, the secret may be written to an unintended file. Practical exploitation depends on the attacker's existing filesystem access and ownership constraints. ### Attack Path 1. A user runs: ```bash node scripts/setup.mjs --generate ``` 2. The script creates `~/.openclaw/.env` using permissions inherited from the runtime environment and current umask. 3. Under a permissive or typical configuration, another local account or process reads the file. 4. The attacker extracts the `PRIVATE_KEY` value. 5. The attacker imports the key into a wallet and signs arbitrary transactions. 6. Any toke ...[truncated 1126 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Create the wallet directory with owner-only access: ```js mkdirSync(envDir, { recursive: true, mode: 0o700 }); chmodSync(envDir, 0o700); ``` 2. Create the key file atomically with exclusive, owner-only permissions rather than using `appendFileSync`: ```js const fd = openSync(envFile, "wx", 0o600); try { writeFileSync(fd, `PRIVATE_KEY=${pk}\n`, { encoding: "utf8" }); } finally { closeSync(fd); } ``` 3. If an existing `.env` file must be supported, inspect it using `lstatSync`, reject symbolic links and non-regular files, verify that it is owned by the current user, and set its mode to `0600` before writing. 4. Avoid storing the key in a shared configuration file. Prefer a dedicated wallet file with a narrowly scoped parser, an encrypted keystore protected by a user-supplied password, or an operating-system credential store. 5. Verify permissions after creation and fail closed if the directory or file remains accessible to group or other users. 6. Continue to avoid printing or transmitting the raw key, and retain the recommendation to use a dedicated low-value wallet. ]]>
