T09 · Insecure Skill Coding Practices
- Location
scripts/powpow-register.mjs:40- Finding
Predictable Shared Temporary State File Enables Local Session-Token Disclosure
- Content
View full analysis
Vulnerability Details
File Location:
scripts/powpow-register.mjs, lines 40–56 and 203–211
Vulnerability Type: Unsafe temporary file handling and local credential disclosure
Risk Level: MediumTechnical Analysis
The script stores workflow state at a fixed, predictable path in the shared operating-system temporary directory:
js const STATE_FILE = join(tmpdir(), 'powpow-register-state.json');The state-loading and state-saving code reopens this path without verifying its ownership, existing permissions, file type, or whether it was created by the current process:
js function loadState() { try { return JSON.parse(readFileSync(STATE_FILE, 'utf8')); } catch { return {}; } } function saveState(patch) { const s = { ...loadState(), ...patch }; writeFileSync(STATE_FILE, JSON.stringify(s, null, 2), { mode: 0o600 }); return s; }Following successful registration, the sensitive PowPow session token is written into this state file:
js const token = r.cookies['powpow_token'] || ''; if (!token) die('注册成功但未捕获到会话 cookie(平台响应异常)'); saveState({ username: r.data?.user?.username || username, nickname: r.data?.user?.nickname || nickname, userId: r.data?.userId, token, step: 'registered-pending', registeredAt: new Date().toISOString(), });The
mode: 0o600option protects a newly created file, but it does not reliably change the permissions of a file that already exists. On a multi-user system, an unprivileged local attacker can pre-create the predictable state file with permissions that allow the victim to overwrite it while preserving attacker read access.The fixed path also permits state collisions between concurrent Skill executions under environments that share the same temporary directory. Moreover,
loadState()accepts preexisting JSON without validating file ownership or provenance.Attack Path
- An unprivi ...[truncated 1596 chars]
- Remediation
View remediation
Remediation Suggestions
- Create a private per-user or per-process state directory with mode
0700, rather than placing the state file directly in a shared temporary directory. - Generate an unpredictable per-session filename with
mkdtempSync()or equivalent secure temporary-file functionality. - Create the state file atomically with exclusive-creation semantics, such as
O_CREAT | O_EXCL, and mode0600. - Reject preexisting files, symbolic links, and non-regular files. Where supported, use
O_NOFOLLOW. - Verify through
fstat()that the opened file is owned by the current user, is a regular file, and has no group or other permissions. - Keep and use the securely opened file descriptor instead of repeatedly reopening a pathname that can be replaced.
- Avoid merging untrusted preexisting state. Authenticate or strictly validate all loaded fields and bind state to the current process or workflow.
- Ensure cleanup securely removes the per-session state and reports deletion failures without exposing the token in logs.
- Consider storing the session token in memory only when workflow resumption is not required. If persistence is necessary, use an operating-system credential store or encrypted per-user storage.
- Create a private per-user or per-process state directory with mode
