Back to skill

Security audit

Kkclaw Server

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a real headless KKClaw client, but it documents a persistent system service and can send API keys, messages, and telemetry over unencrypted HTTP.

Review carefully before installing. Use HTTPS for any non-local gateway, protect or avoid plaintext API keys in config files, and prefer foreground or user-level service operation unless system-wide boot persistence is truly needed. If using systemd, run it as a dedicated least-privilege account, harden the unit, and document how to stop, disable, and remove it.

Vulnerability Patterns
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • 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 (2)

T06 · System Persistence

Error
Location
SKILL.md:91
Finding
Boot-Persistent System Service Installed with Elevated Privileges<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:91-117` **Vulnerability Type**: Boot-enabled systemd service registration **Risk Level**: High ### Vulnerable Code ```ini Create `/etc/systemd/system/kkclaw.service`: [Unit] Description=KKClaw Server After=network.target [Service] Type=simple User=pi WorkingDirectory=/home/pi/kkclaw ExecStart=/usr/bin/node /home/pi/kkclaw/main.js start Restart=always RestartSec=10 [Install] WantedBy=multi-user.target ``` ```bash sudo systemctl daemon-reload sudo systemctl enable kkclaw sudo systemctl start kkclaw ``` ### Technical Analysis The installation instructions direct the user to create a system-wide systemd unit under `/etc/systemd/system`, reload systemd with `sudo`, enable the unit at boot, and start it immediately. The combination of `WantedBy=multi-user.target`, `systemctl enable`, and `Restart=always` provides execution across reboots and automatic relaunch after process termination. Persistent execution is consistent with the advertised always-on server use case, but it is not necessary for the core heartbeat, connection, queue, or model-switching functionality. The application can perform those functions as an ordinary foreground process or through a user-level service. Consequently, system-wide registration using administrative privileges exceeds the minimum privileges necessary for the Skill's core functionality. The service itself runs as `pi`, not as root, which limits direct runtime privileges. However, it executes JavaScript from `/home/pi/kkclaw/main.js`. If that user-writable entry point or its parent directory is subsequently modified, the replacement code will execute automatically at boot and be relaunched by systemd. The unit also lacks standard systemd hardening controls such as `NoNewPrivileges`, `ProtectSystem`, `ProtectHome`, `PrivateTmp`, capability restrictions, and syscall filtering. ### Attack Path 1. The operator follows the documentation and creates the service un ...[truncated 1221 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Make persistent installation explicitly optional rather than part of the default setup. 2. Default to foreground execution with `kkclaw-server start`. 3. Prefer a user-level systemd service installed under `~/.config/systemd/user/` and managed with `systemctl --user`. 4. If a system service is necessary, create a dedicated, non-login service account instead of using a general-purpose `pi` account. 5. Deploy application code into a root-owned, non-user-writable directory such as `/opt/kkclaw`, while keeping only required state directories writable by the service account. 6. Add systemd sandboxing controls, for example: ```ini [Service] User=kkclaw Group=kkclaw NoNewPrivileges=true PrivateTmp=true ProtectSystem=strict ProtectHome=true ProtectKernelTunables=true ProtectKernelModules=true ProtectControlGroups=true RestrictSUIDSGID=true LockPersonality=true CapabilityBoundingSet= AmbientCapabilities= ReadWritePaths=/var/lib/kkclaw ``` 7. Use `Restart=on-failure` with restart-rate limits instead of unconditional `Restart=always`. 8. Document how to stop, disable, and remove the service and how to verify that persistence has been removed. 9. Avoid requiring `sudo` except for an explicitly selected system-wide installation mode. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
main.js:282
Finding
Bearer Credentials, Messages, and Telemetry Can Be Transmitted over Plaintext HTTP<![CDATA[ ## Vulnerability Details **File Location**: `main.js:26-30`, `main.js:212-214`, `main.js:282-316`; `config.json:7-10`; `SKILL.md:54-57` **Vulnerability Type**: Cleartext transmission of sensitive information **Risk Level**: High ### Vulnerable Code Default gateway configuration: ```js gateway: { url: 'http://localhost:18789', apiKey: '' }, ``` Message transmission: ```js const response = await this.apiRequest('/api/message', 'POST', { content: item.message }); ``` HTTP request implementation: ```js async apiRequest(endpoint, method = 'GET', data = null) { return new Promise((resolve, reject) => { const url = new URL(endpoint, this.config.gateway.url); const options = { hostname: url.hostname, port: url.port, path: url.pathname + url.search, method, headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.config.gateway.apiKey}` }, timeout: this.config.heartbeat.timeout }; const req = (url.protocol === 'https:' ? https : http).request(options, (res) => { let body = ''; res.on('data', c => body += c); res.on('end', () => { try { const json = JSON.parse(body); if (res.statusCode >= 200 && res.statusCode < 300) { resolve(json); } else { reject(new Error(`HTTP ${res.statusCode}: ${json.error || body}`)); } } catch (e) { reject(new Error(`Invalid JSON: ${body}`)); } }); }); req.on('error', reject); req.on('timeout', () => { req.destroy(); reject(new Error('Request timeout')); }); if (data) { req.write(JSON.stringify(data)); } req.end(); }); } ``` The shipped configuration also permits plaintext HTTP: ```json "gateway": { "url": "http://localhost:18789", "apiKey": "" } ``` The documentation presents a potentially remote HTTP endpoint: ```json "gatew ...[truncated 2759 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require `https:` for every non-loopback gateway. 2. Reject unsupported URL schemes explicitly before making a request. 3. Permit plaintext HTTP only for explicit development use on loopback addresses such as `127.0.0.1`, `::1`, or `localhost`. 4. Replace the remote HTTP example in `SKILL.md` with an HTTPS URL. 5. Fail closed when a non-loopback HTTP endpoint is configured rather than emitting only a warning. 6. Do not add the `Authorization` header when no API key is configured. 7. Use short-lived, scoped credentials and implement gateway-side token revocation and rotation. 8. Ensure normal TLS certificate and hostname validation remains enabled; do not introduce an option that disables certificate verification. 9. Consider certificate pinning or mutual TLS where the gateway is deployed in a controlled environment. 10. Minimize heartbeat telemetry to fields required for operation and document all transmitted data. Example protocol enforcement: ```js const url = new URL(endpoint, this.config.gateway.url); const loopbackHosts = new Set(['localhost', '127.0.0.1', '::1']); if (url.protocol !== 'https:' && !(url.protocol === 'http:' && loopbackHosts.has(url.hostname))) { throw new Error('HTTPS is required for non-loopback gateways'); } const headers = { 'Content-Type': 'application/json' }; if (this.config.gateway.apiKey) { headers.Authorization = `Bearer ${this.config.gateway.apiKey}`; } ``` ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (9)

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The skill documentation states that it reports status to a gateway and later specifies that heartbeats include status, model, queue length, memory, and uptime. Because this behavior transmits system and operational data off-host, the markdown should disclose the privacy and telemetry implications to users.

Missing User Warnings

Medium
Confidence
83% confidence
Finding
The config example includes an API key field in a user-editable JSON file without any guidance on secure storage, file permissions, or avoiding credential exposure. This can lead users to place long-lived secrets in plaintext configs that may be leaked through backups, logs, screenshots, or overly permissive filesystem access.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
Then:
```bash
sudo systemctl daemon-reload
sudo systemctl enable kkclaw
sudo systemctl start kkclaw
```
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
Then:
```bash
sudo systemctl daemon-reload
sudo systemctl enable kkclaw
sudo systemctl start kkclaw
```
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
Then:
```bash
sudo systemctl daemon-reload
sudo systemctl enable kkclaw
sudo systemctl start kkclaw
```
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Session Persistence

Medium
Category
Rogue Agent
Content
Then:
```bash
sudo systemctl daemon-reload
sudo systemctl enable kkclaw
sudo systemctl start kkclaw
```
Confidence
80% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The heartbeat transmits runtime telemetry including process uptime, memory usage, queue length, model name, and status, and message sending transmits queued content to the configured gateway. While this appears to be intended functionality for a remote server, it still creates a privacy and security risk because sensitive operational metadata and user-provided message content are sent outbound without any visible consent flow, minimization, or redaction, and the default gateway is plain HTTP.

Session Persistence

Medium
Category
Rogue Agent
Content
break;
      
    case 'init':
      // Create config
      const configDir = path.dirname(configPath);
      if (!fs.existsSync(configDir)) {
        fs.mkdirSync(configDir, { recursive: true });
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Missing User Warnings

Low
Confidence
88% confidence
Finding
The init command creates ~/.kkclaw/config.json on disk, which is a file write operation, but there is no preceding disclosure that a persistent config file will be created at that path. Although a success message is printed afterward, the user is not warned before the write occurs.

Static analysis

No suspicious patterns detected.