Back to skill

Security audit

Quack Sdk

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it says, but its quickstart stores and may print sensitive Quack credentials in ways users should review before installing.

Review the quickstart before running it. If you use it, treat the generated Quack credentials as secrets, avoid running it in logged terminals or CI, restrict permissions on ~/.openclaw/credentials/quack.json, and consider removing or separately protecting the stored private key after registration.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (3)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/quickstart.mjs:65
Finding
Credential file is created without explicit restrictive permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/quickstart.mjs`, lines 65–67 **Vulnerability Type**: `T09: Insecure Skill Coding Practices` **Risk Level**: Medium ### Vulnerable Code ```javascript mkdirSync(`${homedir()}/.openclaw/credentials`, { recursive: true }); const creds = { agentId, apiKey: regData.apiKey || regData.api_key, privateKey }; writeFileSync(CREDS_PATH, JSON.stringify(creds, null, 2)); ``` ### Technical Analysis The script stores a bearer API key and an RSA private key in a plaintext JSON file but does not explicitly apply owner-only permissions to either the credentials directory or the file. The resulting permissions depend on the process umask and the permissions of any pre-existing directory or file. On systems with a permissive umask or an improperly configured `~/.openclaw/credentials` directory, other local users may be able to read the credentials. Additionally, `writeFileSync` follows an existing symbolic link. If an attacker with local access can prepare the destination path before the script runs, the write could be redirected to another file accessible to the victim account. ### Attack Path 1. An attacker obtains local access sufficient to inspect or manipulate the victim's credential directory. 2. The attacker either: - waits for the script to create `quack.json` under permissive filesystem permissions; or - places a symbolic link at `~/.openclaw/credentials/quack.json` before registration. 3. The victim runs the quick-start script. 4. The script writes the API key and private key without validating the destination or enforcing owner-only access. 5. The attacker reads the credentials or causes the write to affect the symbolic-link target. 6. The attacker reuses the exposed API key or private key to impersonate the registered agent. ### Impact Assessment Successful exploitation may expose the Quack bearer API key and RSA private key to another local user or compromised process. The attacker could perf ...[truncated 367 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Create the credentials directory with owner-only permissions: ```javascript mkdirSync(credentialsDir, { recursive: true, mode: 0o700 }); ``` - Create the credential file with mode `0o600`. - Use exclusive creation where appropriate to avoid silently overwriting an attacker-prepared file. - Validate the destination with `lstatSync` and reject symbolic links. - Correct permissions on pre-existing directories and files rather than assuming the requested creation mode was applied. - Prefer an atomic write process using a securely created temporary file in the same protected directory, followed by a rename. - Consider using an operating-system credential store instead of a plaintext JSON file. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/quickstart.mjs:39
Finding
RSA private key is persistently retained beyond the implemented workflow's needs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/quickstart.mjs`, lines 39–67 **Vulnerability Type**: `T05: Unauthorized Access and Privilege Escalation` **Risk Level**: Medium ### Vulnerable Code ```javascript const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' }, }); console.log('Fetching declaration challenge...'); const challengeRes = await fetch(`${API}/api/v1/auth/challenge`); const challenge = await challengeRes.json(); const declaration = challenge.declaration || challenge.text || JSON.stringify(challenge); console.log('Signing declaration...'); const sign = crypto.createSign('SHA256'); sign.update(declaration); const signature = sign.sign(privateKey, 'base64'); console.log('Registering...'); const regRes = await fetch(`${API}/api/v1/auth/register`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ agentId, displayName, platform: 'openclaw', publicKey, signature }), }); const regData = await regRes.json(); console.log('Registration response:', JSON.stringify(regData, null, 2)); if (regData.apiKey || regData.api_key) { mkdirSync(`${homedir()}/.openclaw/credentials`, { recursive: true }); const creds = { agentId, apiKey: regData.apiKey || regData.api_key, privateKey }; writeFileSync(CREDS_PATH, JSON.stringify(creds, null, 2)); console.log(`Credentials saved to ${CREDS_PATH}`); } ``` ### Technical Analysis The private key is required to sign the declaration during initial registration. After registration, however, the implemented repeat-run workflow reads only `agentId` and `apiKey` from the credential file to send a test message. It does not use the stored private key. Persisting the unencrypted private key therefore exceeds the minimum data retention required by the functionality implemented in this script. It unnecessarily expands th ...[truncated 1229 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not include `privateKey` in the persisted credentials when it is not required after registration: ```javascript const creds = { agentId, apiKey: regData.apiKey || regData.api_key, }; ``` - Keep the private key in memory only for the duration of the registration operation. - If future documented functionality genuinely requires long-term private-key access, store it in an operating-system keychain, hardware-backed key store, or encrypted keystore. - Separate bearer-token storage from long-term signing-key storage to reduce the effect of a single-file compromise. - Document the key lifecycle, including creation, retention, rotation, revocation, and deletion. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/quickstart.mjs:59
Finding
Registration response may disclose the bearer API key through logs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/quickstart.mjs`, lines 59–62 **Vulnerability Type**: `T09: Insecure Skill Coding Practices` **Risk Level**: Medium ### Vulnerable Code ```javascript const regData = await regRes.json(); console.log('Registration response:', JSON.stringify(regData, null, 2)); if (regData.apiKey || regData.api_key) { ``` ### Technical Analysis The script logs the complete registration response before checking that response for `apiKey` or `api_key`. This establishes that the logged object may contain a bearer credential. Console output can be captured by terminal logging, CI/CD systems, process supervisors, remote execution platforms, agent transcripts, or centralized observability services. Bearer tokens do not require proof of possession of a separate secret, so anyone who obtains the logged value may be able to authenticate as the agent. ### Attack Path 1. The user runs the registration script in a logged terminal, CI job, hosted agent environment, or process supervisor. 2. The Quack registration endpoint returns an API key in the response. 3. The script serializes and prints the complete response, including the API key. 4. The output is retained in a log or transcript accessible to another user, operator, or compromised logging service. 5. The attacker extracts the bearer token from the recorded response. 6. The attacker submits authenticated requests to the Quack API as the registered agent. ### Impact Assessment An attacker with access to the captured output may impersonate the registered agent for any API operation authorized by the exposed token. This can include sending messages and other authenticated actions supported by the Quack service. The precise server-side privilege scope and token lifetime are not defined in the audited project, so broader service compromise cannot be asserted. The private key is not transmitted or directly printed by this statement. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Never print the complete registration response. - Log an explicit allowlist of non-sensitive fields, such as the registered agent identifier or status. - Redact fields whose names contain `apiKey`, `api_key`, `token`, `secret`, `credential`, or private-key material. - Keep sensitive values out of exceptions, debug traces, telemetry, and structured logs. - Add automated tests that verify credentials never appear in standard output or standard error. - If a token may already have been logged, revoke or rotate it and remove retained copies from accessible logging systems. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (4)

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill includes explicit network-oriented functionality, external URLs, and example code that performs registration and message sending, but it does not declare any tool scope or allowed-tools metadata. This creates a mismatch between documented behavior and permission transparency, increasing the risk that an agent or operator invokes network-capable actions without clear policy review or least-privilege controls.

External Transmission

Medium
Category
Data Exfiltration
Content
### Send a Message

```javascript
await fetch('https://quack.us.com/api/send', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` },
  body: JSON.stringify({ from: 'myagent/main', to: 'other/main', task: 'Hello!' })
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The script stores sensitive credentials to disk, including both the API key and the unencrypted RSA private key, without warning the user beforehand or setting restrictive file permissions. In a developer quickstart context this is risky because users may unknowingly persist long-lived secrets in a predictable location where other local users, backup systems, or malware could access them.

Missing User Warnings

Low
Confidence
82% confidence
Finding
This markdown file describes an endpoint for reading an agent inbox, which affects potentially sensitive user or agent message data, but it provides no user-facing warning or disclosure about privacy implications or access sensitivity. Under the markdown variant of SQP-2, descriptions of behaviors that affect user data or privacy should include a warning.

Static analysis

No suspicious patterns detected.