Back to skill

Security audit

Ssh Batch Manager

Security checks for vulnerabilities and agentic risk

Overview

This SSH management skill is mostly purpose-aligned, but it installs a persistent network service and handles SSH credentials and server access with unsafe scoping that users should review carefully.

Install only if you intend to grant this skill administrative control over SSH access on the configured servers. Review the post-install service first, prefer disabling the auto-start web service, bind any UI to loopback only, avoid password-based SSH where possible, verify host keys, and test on non-production targets before using enable-all or disable-all.

Vulnerability Patterns
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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
Findings (6)

T09 · Insecure Skill Coding Practices

Error
Location
ssh-manager.en.html:360
Finding
Shell Command Injection Through Web UI Configuration Serialization<![CDATA[ ## Vulnerability Details **File Location**: `ssh-manager.en.html:360-370` **Equivalent Location**: `ssh-manager.html:360-370` **Vulnerability Type**: Shell command injection **Risk Level**: Critical ### Vulnerable Code ```javascript // Read existing config const configResponse = await window.openclawExec?.('cat ~/.openclaw/credentials/ssh-batch.json'); let config = JSON.parse(configResponse); // Add new server config.servers.push(serverData); // Save config using Python json module (SECURE) const tempFile = `/tmp/ssh_config_${Date.now()}.json`; const jsonContent = JSON.stringify(config, null, 2).replace(/'/g, "'\\''"); await window.openclawExec?.(`python3 -c "import json; f=open('${tempFile}','w'); f.write('${jsonContent}'); f.close()"`); await window.openclawExec?.(`mv ${tempFile} ~/.openclaw/credentials/ssh-batch.json`); await window.openclawExec?.('chmod 600 ~/.openclaw/credentials/ssh-batch.json'); ``` ### Technical Analysis The Web UI serializes configuration containing user-controlled `user`, `host`, and related server values, then interpolates the resulting JSON into a shell command passed to `window.openclawExec`. The attempted escaping only transforms single quotation marks. It does not neutralize shell metacharacters or command substitutions such as `$()` and backticks. Moreover, the command is enclosed in shell double quotes, while serialized JSON itself contains double quotes. The JSON can therefore alter the intended shell quoting context. Using Python inside the generated command does not provide protection because the shell parses and expands the complete command before Python is started. The same unsafe construction is used in the server deletion path at `ssh-manager.en.html:388-397` and its counterpart in `ssh-manager.html`. ### Attack Path 1. An attacker supplies a crafted username or hostname through the server form or causes a crafted value to exist in the configuration. 2. The value is included in `serverData` and th ...[truncated 916 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Eliminate dynamically generated shell commands for configuration updates. 2. Pass configuration data to a dedicated helper through stdin or a structured API. 3. Change `openclawExec` to accept an executable and argument array without invoking a shell. 4. Reuse a hardened helper such as `add-server.py`, but add strict schema validation before writing data. 5. Validate usernames, hostnames, and ports server-side: - Restrict usernames to an explicitly permitted character set. - Accept only validated DNS names or IP addresses for hosts. - Require an integer port in the range 1–65535. 6. Write configuration atomically using Python file APIs and a mode-0600 temporary file created with `tempfile`. 7. Apply the same correction to the deletion path and both language variants of the HTML file. 8. Add regression tests using values containing `$()`, backticks, quotes, newlines, semicolons, and redirection operators. ]]>

T07 · Tool Hijacking and Spoofing

Error
Location
ssh-manager.en.html:174
Finding
Stored Cross-Site Scripting in the Privileged Server List<![CDATA[ ## Vulnerability Details **File Location**: `ssh-manager.en.html:174-203` **Equivalent Location**: `ssh-manager.html:174-203` **Vulnerability Type**: Stored cross-site scripting with command-bridge access **Risk Level**: Critical ### Vulnerable Code ```javascript function renderServerList(servers) { const tbody = document.getElementById('serverList'); const emptyState = document.getElementById('emptyState'); const table = document.getElementById('serverTable'); if (servers.length === 0) { showEmptyState(true); table.style.display = 'none'; return; } showEmptyState(false); table.style.display = 'table'; tbody.innerHTML = servers.map((server, index) => ` <tr> <td><code>${server.user}@${server.host}</code></td> <td>${server.user}</td> <td>${server.host}:${server.port || 22}</td> <td><span class="status-badge ${server.auth === 'key' ? 'status-ready' : 'status-error'}">${server.auth === 'key' ? '🔑 Key' : '🔒 Password'}</span></td> <td><span class="status-badge status-ready">Configured</span></td> <td> <button class="btn btn-success btn-sm" onclick="enableSingle('${server.user}@${server.host}', ${server.port || 22})"> Enable </button> <button class="btn btn-danger btn-sm" onclick="disableSingle('${server.user}@${server.host}', ${server.port || 22})" style="margin-left: 5px;"> Disable </button> <button class="btn btn-secondary btn-sm" onclick="deleteServer(${index})" style="margin-left: 5px;"> Delete </button> </td> </tr> `).join(''); } ``` ### Technical Analysis Configuration values are inserted directly into `innerHTML` and inline event-handler attributes without HTML or JavaScript-context escaping. A crafted `server.user`, ...[truncated 1580 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not use `innerHTML` to render configuration data. 2. Construct table rows with `document.createElement` and assign all untrusted values using `textContent`. 3. Register button handlers with `addEventListener`; do not interpolate values into inline `onclick` attributes. 4. Validate every configuration field before storage and again before rendering. 5. Deploy a strict Content Security Policy that prohibits inline scripts and inline event handlers. 6. Remove the general-purpose `window.openclawExec` interface from page JavaScript. 7. Replace it with a narrowly scoped API exposing only predefined operations and validated structured parameters. 8. Apply the fix to both `ssh-manager.en.html` and `ssh-manager.html`. 9. Add tests using HTML tags, event handlers, quotes, template delimiters, and JavaScript URLs in all server fields. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
serve-ui.py:39
Finding
Web UI Server Exposed on All Network Interfaces Without Authentication<![CDATA[ ## Vulnerability Details **File Location**: `serve-ui.py:39` **Vulnerability Type**: Unauthenticated network exposure **Risk Level**: High ### Vulnerable Code ```python with socketserver.TCPServer(("", PORT), Handler) as httpd: try: httpd.serve_forever() except KeyboardInterrupt: print("\n👋 Service stopped") ``` The handler also serves the entire Skill directory: ```python DIRECTORY = os.path.dirname(os.path.abspath(__file__)) class Handler(http.server.SimpleHTTPRequestHandler): def __init__(self, *args, **kwargs): super().__init__(*args, directory=DIRECTORY, **kwargs) ``` ### Technical Analysis Passing an empty bind address causes the TCP server to listen on all available interfaces, not only loopback. This conflicts with the displayed and documented `http://localhost:8765` access model. The server uses `SimpleHTTPRequestHandler`, provides no authentication or TLS, and serves the entire project directory rather than an isolated directory containing only required Web assets. There are no application-level access controls, host validation, or other network restrictions in this server. The static server alone does not implement `window.openclawExec`; however, broad exposure unnecessarily makes Skill files and the unsafe Web UI reachable to other systems and increases the attack surface around any environment that supplies the bridge. ### Attack Path 1. The Web UI server starts on a machine with a network-reachable interface. 2. Port 8765 binds to all interfaces. 3. A remote client connects to `http://target:8765/`. 4. The client retrieves Web UI assets or other files beneath the Skill directory. 5. Exposed implementation details can be used to identify vulnerable UI flows or support subsequent attacks against users who open the interface in a privileged OpenClaw environment. ### Impact Assessment The direct impact includes: - Unauthorized retrieval of files stored in the Skill directory. - Exposure of ...[truncated 453 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Bind explicitly to loopback: ```python with socketserver.TCPServer(("127.0.0.1", PORT), Handler) as httpd: ``` 2. Serve only a dedicated static-assets directory rather than the project root. 3. Disable directory listing and reject unexpected paths. 4. If remote access is a supported requirement, add authentication, TLS, origin controls, and CSRF protection. 5. Add firewall guidance as defense in depth, but do not use it as a substitute for loopback binding. 6. Update documentation to accurately state the listening interface. 7. Add an automated test verifying that the service cannot be reached through non-loopback interfaces by default. ]]>

T06 · System Persistence

Error
Location
post-install.sh:33
Finding
Automatic Installation of a System-Wide Persistent Web Service<![CDATA[ ## Vulnerability Details **File Location**: `post-install.sh:33-72` **Vulnerability Type**: System service persistence **Risk Level**: High ### Vulnerable Code ```bash # Create service file sudo tee "$SERVICE_FILE" > /dev/null << EOF [Unit] Description=SSH Batch Manager Web UI Documentation=https://gitee.com/subline/onepeace/tree/develop/src/skills/ssh-batch-manager After=network.target [Service] Type=simple User=${USER} Group=${USER} WorkingDirectory=${SKILL_DIR} ExecStart=/usr/bin/python3 ${SKILL_DIR}/serve-ui.py Restart=on-failure RestartSec=5 StandardOutput=journal StandardError=journal SyslogIdentifier=ssh-batch-ui # Security settings NoNewPrivileges=true PrivateTmp=true [Install] WantedBy=multi-user.target EOF # Reload systemd sudo systemctl daemon-reload # Enable service (start on boot) sudo systemctl enable ${SERVICE_NAME} # Start service sudo systemctl start ${SERVICE_NAME} ``` ### Technical Analysis The post-install script uses `sudo` to write a system-wide unit under `/etc/systemd/system`, enables it for startup, and starts it immediately. This makes the Web UI survive reboots and future sessions. The persistence is disclosed in the Skill documentation, so it is not hidden. Nevertheless, it exceeds the minimum privileges required for the core SSH batch-management CLI and is installed as part of the normal installation flow. Its risk is amplified because `serve-ui.py` listens on all interfaces and provides no authentication. `NoNewPrivileges=true` and execution as the installing user reduce some privilege-escalation risk, but they do not remove the persistent network attack surface or the root-level modification of system service configuration. ### Attack Path 1. The user installs the Skill and allows the post-install script to run. 2. The script requests elevated privileges through `sudo`. 3. A unit is written to `/etc/systemd/system/ssh-batch-ui.service`. 4. `systemctl enable` registers the service to start at boot. 5. `syst ...[truncated 709 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove automatic system-wide service installation from the default installation process. 2. Require explicit, informed opt-in before creating any persistent service. 3. Prefer a user-level systemd service under `~/.config/systemd/user/` when persistence is requested. 4. Do not require `sudo` for ordinary Skill installation or operation. 5. Bind the service to `127.0.0.1` before offering any persistent mode. 6. Provide a complete uninstall operation that stops, disables, and removes the unit before reloading systemd. 7. Display the exact unit contents and security implications before requesting confirmation. 8. Add further service hardening, including a restrictive `ProtectSystem`, `ProtectHome`, `RestrictAddressFamilies`, and an explicit read-only asset directory where compatible. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
ssh-batch-manager.py:181
Finding
SSH Host Authentication Disabled During Credentialed Operations<![CDATA[ ## Vulnerability Details **File Location**: `ssh-batch-manager.py:181-213` **Additional Locations**: `ssh-batch-manager.py:227-237`, `342-390`, `568-625` **Vulnerability Type**: Insecure SSH host verification **Risk Level**: High ### Vulnerable Code ```python if password: env['SSHPASS'] = password cmd = ['sshpass', '-e', 'ssh', '-o', 'BatchMode=yes', '-o', 'StrictHostKeyChecking=no', '-o', 'UserKnownHostsFile=/dev/null', '-o', f'ConnectTimeout=5', '-o', f'Port={port}', user_host, 'echo OK'] elif key_path: cmd = ['ssh', '-i', key_path, '-o', 'BatchMode=yes', '-o', 'StrictHostKeyChecking=no', '-o', 'UserKnownHostsFile=/dev/null', '-o', f'ConnectTimeout=5', '-o', f'Port={port}', user_host, 'echo OK'] else: cmd = ['ssh', '-o', 'BatchMode=yes', '-o', 'StrictHostKeyChecking=no', '-o', 'UserKnownHostsFile=/dev/null', '-o', f'ConnectTimeout=5', '-o', f'Port={port}', user_host, 'echo OK'] try: result = subprocess.run(cmd, env=env, capture_output=True, timeout=10) return result.returncode == 0 and 'OK' in result.stdout.decode() except: return False ``` Credentialed key-check and modification commands repeat the same options: ```python cmd = ['sshpass', '-e', 'ssh', '-o', 'StrictHostKeyChecking=no', '-o', 'UserKnownHostsFile=/dev/null', '-o', f'Port={port}', user_host, 'grep', '-F', pub_key_content, '~/.ssh/authorized_keys'] ``` ### Technical Analysis `StrictHostKeyChecking=no` automatically accepts unknown host keys, while `UserKnownHostsFile=/dev/null` prevents persistent host-key trust records from being stored or consulted. Together, these settings remove SSH’s normal server-identity verification. Because password-authenticated SSH commands use `sshpass`, an attacker controlling DNS, rou ...[truncated 1320 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `StrictHostKeyChecking=no` and `UserKnownHostsFile=/dev/null`. 2. Use the normal user `known_hosts` file or a dedicated mode-0600 host-key database. 3. For first use, retrieve and display the host-key fingerprint and require explicit user confirmation. 4. Fail closed when a known host key changes. 5. Allow administrators to pre-provision trusted fingerprints in configuration. 6. Do not silently replace trust records during batch operations. 7. Distinguish connectivity failures from host-verification failures and report the latter clearly. 8. Add tests for unknown, trusted, and changed host-key scenarios. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
ssh-batch-manager.py:782
Finding
Sensitive Passwords and Encryption Keys Exposed Through Process Arguments and Standard Output<![CDATA[ ## Vulnerability Details **File Location**: `ssh-batch-manager.py:782-814` **Vulnerability Type**: Sensitive data exposure **Risk Level**: Medium ### Vulnerable Code ```python # encrypt command encrypt_parser = subparsers.add_parser('encrypt', help='Encrypt a password') encrypt_parser.add_argument('password', help='Password to encrypt') # generate-key command subparsers.add_parser('generate-key', help='Generate encryption key') args = parser.parse_args() if args.command == 'enable-all': enable_all() elif args.command == 'disable-all': disable_all() elif args.command == 'encrypt': # Secure password encryption via argparse (no shell injection) key = load_key() encrypted = encrypt_data(args.password, key) print(encrypted) elif args.command == 'generate-key': from cryptography.fernet import Fernet key = Fernet.generate_key().decode() print(key) elif args.command == 'generate-ed25519': generate_ed25519_key() ``` ### Technical Analysis The `encrypt` command accepts a plaintext password as a positional command-line argument. Command-line arguments can be retained in shell history and may be visible to process-monitoring tools or other same-host observers, depending on operating-system controls. The generated Fernet key is printed directly to stdout. In an Agent context, stdout may be returned to the caller, included in transcripts, or captured by logs. Because this key decrypts all passwords protected with it, its disclosure compromises the confidentiality of the entire stored server credential set. The encrypted password is also returned through stdout. Ciphertext alone is less sensitive than the plaintext password or encryption key, but it should still be treated as a credential artifact and not unnecessarily propagated into logs or conversations. The Base64 encoding used by `encrypt_data` is part of formatting Fernet ciphertext and is not evidence of a covert external exfiltration channel. No external exf ...[truncated 1040 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Read passwords interactively with `getpass.getpass()` or from stdin instead of command-line arguments. 2. Never print the generated master key by default. 3. Generate and atomically write the key directly to `~/.openclaw/credentials/ssh-batch.key` with mode 0600. 4. Refuse to overwrite an existing key unless the user explicitly confirms. 5. Avoid returning credential material through Agent-visible stdout. 6. If ciphertext must be returned, provide an explicit output-file option and warn that it is sensitive. 7. Redact secrets from exceptions, debug output, journals, and command transcripts. 8. Document rotation and recovery procedures for the encryption key. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (191)

Credential Access

High
Category
Privilege Escalation
Content
SOURCE_HOST = subprocess.run(['hostname'], ...).stdout.strip()

source_comment = f" {SOURCE_IDENTIFIER} from {SOURCE_HOST} at {timestamp}"
cmd = f'echo "{pub_key}{source_comment}" >> ~/.ssh/authorized_keys'
```

**效果**:
Confidence
95% confidence
Finding
Appending a key to ~/.ssh/authorized_keys is a credential and persistence action because it modifies who can authenticate to the account. In this context the feature is intentional, but if the wrong key, host, or account is targeted, it silently establishes durable remote access across systems.

YARA rule 'backdoor_persistence': Backdoor persistence with malicious payloads (shell commands, SSH key injection, hidden root users) [malware]

High
Category
YARA Match
Content
�能免密登录,跳过
```

---

### 2️⃣ 来源标识

**问题**: 无法区分公钥是从哪台服务器分发的。

**解决**: 在 authorized_keys 中添加来源标识注释。

**实现**:
```python
SOURCE_IDENTIFIER = "ssh-batch-manager"
SOURCE_HOST = subprocess.run(['hostname'], ...).stdout.strip()

source_comment = f" {SOURCE_IDENTIFIER} from {SOURCE_HOST} at {timestamp}"
cmd = f'echo "{pub_key}{source_comment}" >> ~/.ssh/authorized_keys'
```

**效果**:
```
# authorized_keys 内容
ssh-ed25519 AAAAC3... ssh-batch-manager from mls at 2026-03-03 17:30:00
```

**优势**:
- ✅ 知道是哪台服务器分发的
- ✅ 知道分发时间
- ✅ 便于审计和清理

---

### 3️⃣ 配置清理

**问题**: 测试配置和生产配置混在一起。

**解决**: 删除测试服务器,只保留生产服务器。

**清理的服务器**:
- ❌ root@10.0.0.2 (测试)
- ❌ user1@10.8.8.1 (测试)

**保留的服务器**:
- ✅ 10.8.8.81
- ✅ 10.8.8.85
- ✅ 10.8.8.86
- �
Confidence
96% confidence
Finding
The YARA rule matched because the documented command performs SSH key injection into authorized_keys, a classic persistence mechanism also used by attackers. Even though the apparent product intent is legitimate batch administration, the exact behavior is security-sensitive and especially dangerous here because the changelog shows use on multiple production servers and root accounts.

Credential Access

High
Category
Privilege Escalation
Content
SOURCE_HOST = subprocess.run(['hostname'], ...).stdout.strip()

source_comment = f" {SOURCE_IDENTIFIER} from {SOURCE_HOST} at {timestamp}"
cmd = f'echo "{pub_key}{source_comment}" >> ~/.ssh/authorized_keys'
```

**Effect**:
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SOURCE_HOST = subprocess.run(['hostname'], ...).stdout.strip()

source_comment = f" {SOURCE_IDENTIFIER} from {SOURCE_HOST} at {timestamp}"
cmd = f'echo "{pub_key}{source_comment}" >> ~/.ssh/authorized_keys'
```

**Effect**:
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SOURCE_HOST = subprocess.run(['hostname'], ...).stdout.strip()

source_comment = f" {SOURCE_IDENTIFIER} from {SOURCE_HOST} at {timestamp}"
cmd = f'echo "{pub_key}{source_comment}" >> ~/.ssh/authorized_keys'
```

**Effect**:
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SOURCE_HOST = subprocess.run(['hostname'], ...).stdout.strip()

source_comment = f" {SOURCE_IDENTIFIER} from {SOURCE_HOST} at {timestamp}"
cmd = f'echo "{pub_key}{source_comment}" >> ~/.ssh/authorized_keys'
```

**Effect**:
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SOURCE_HOST = subprocess.run(['hostname'], ...).stdout.strip()

source_comment = f" {SOURCE_IDENTIFIER} from {SOURCE_HOST} at {timestamp}"
cmd = f'echo "{pub_key}{source_comment}" >> ~/.ssh/authorized_keys'
```

**Effect**:
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SOURCE_HOST = subprocess.run(['hostname'], ...).stdout.strip()

source_comment = f" {SOURCE_IDENTIFIER} from {SOURCE_HOST} at {timestamp}"
cmd = f'echo "{pub_key}{source_comment}" >> ~/.ssh/authorized_keys'
```

**Effect**:
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SOURCE_HOST = subprocess.run(['hostname'], ...).stdout.strip()

source_comment = f" {SOURCE_IDENTIFIER} from {SOURCE_HOST} at {timestamp}"
cmd = f'echo "{pub_key}{source_comment}" >> ~/.ssh/authorized_keys'
```

**Effect**:
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SOURCE_HOST = subprocess.run(['hostname'], ...).stdout.strip()

source_comment = f" {SOURCE_IDENTIFIER} from {SOURCE_HOST} at {timestamp}"
cmd = f'echo "{pub_key}{source_comment}" >> ~/.ssh/authorized_keys'
```

**Effect**:
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SOURCE_HOST = subprocess.run(['hostname'], ...).stdout.strip()

source_comment = f" {SOURCE_IDENTIFIER} from {SOURCE_HOST} at {timestamp}"
cmd = f'echo "{pub_key}{source_comment}" >> ~/.ssh/authorized_keys'
```

**Effect**:
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SOURCE_HOST = subprocess.run(['hostname'], ...).stdout.strip()

source_comment = f" {SOURCE_IDENTIFIER} from {SOURCE_HOST} at {timestamp}"
cmd = f'echo "{pub_key}{source_comment}" >> ~/.ssh/authorized_keys'
```

**Effect**:
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SOURCE_HOST = subprocess.run(['hostname'], ...).stdout.strip()

source_comment = f" {SOURCE_IDENTIFIER} from {SOURCE_HOST} at {timestamp}"
cmd = f'echo "{pub_key}{source_comment}" >> ~/.ssh/authorized_keys'
```

**Effect**:
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SOURCE_HOST = subprocess.run(['hostname'], ...).stdout.strip()

source_comment = f" {SOURCE_IDENTIFIER} from {SOURCE_HOST} at {timestamp}"
cmd = f'echo "{pub_key}{source_comment}" >> ~/.ssh/authorized_keys'
```

**Effect**:
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SOURCE_HOST = subprocess.run(['hostname'], ...).stdout.strip()

source_comment = f" {SOURCE_IDENTIFIER} from {SOURCE_HOST} at {timestamp}"
cmd = f'echo "{pub_key}{source_comment}" >> ~/.ssh/authorized_keys'
```

**Effect**:
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SOURCE_HOST = subprocess.run(['hostname'], ...).stdout.strip()

source_comment = f" {SOURCE_IDENTIFIER} from {SOURCE_HOST} at {timestamp}"
cmd = f'echo "{pub_key}{source_comment}" >> ~/.ssh/authorized_keys'
```

**Effect**:
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SOURCE_HOST = subprocess.run(['hostname'], ...).stdout.strip()

source_comment = f" {SOURCE_IDENTIFIER} from {SOURCE_HOST} at {timestamp}"
cmd = f'echo "{pub_key}{source_comment}" >> ~/.ssh/authorized_keys'
```

**Effect**:
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SOURCE_HOST = subprocess.run(['hostname'], ...).stdout.strip()

source_comment = f" {SOURCE_IDENTIFIER} from {SOURCE_HOST} at {timestamp}"
cmd = f'echo "{pub_key}{source_comment}" >> ~/.ssh/authorized_keys'
```

**Effect**:
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SOURCE_HOST = subprocess.run(['hostname'], ...).stdout.strip()

source_comment = f" {SOURCE_IDENTIFIER} from {SOURCE_HOST} at {timestamp}"
cmd = f'echo "{pub_key}{source_comment}" >> ~/.ssh/authorized_keys'
```

**Effect**:
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

YARA rule 'backdoor_persistence': Backdoor persistence with malicious payloads (shell commands, SSH key injection, hidden root users) [malware]

High
Category
YARA Match
Content
g
```

---

#### 2️⃣ Source Identifier

**Problem**: Cannot distinguish which server distributed the public key.

**Solution**: Add source identifier comment to authorized_keys.

**Implementation**:
```python
SOURCE_IDENTIFIER = "ssh-batch-manager"
SOURCE_HOST = subprocess.run(['hostname'], ...).stdout.strip()

source_comment = f" {SOURCE_IDENTIFIER} from {SOURCE_HOST} at {timestamp}"
cmd = f'echo "{pub_key}{source_comment}" >> ~/.ssh/authorized_keys'
```

**Effect**:
```
# authorized_keys content
ssh-ed25519 AAAAC3... ssh-batch-manager from mls at 2026-03-03 17:30:00
```

**Benefits**:
- ✅ Know which server distributed the key
- ✅ Know distribution timestamp
- ✅ Easy to audit and cleanup

---

#### 3️⃣ Configuration Cleanup

**Problem**: Test and production configurations mixed together.

**Solution**: Delete test servers, keep only production servers.

**Deleted servers**:
- ❌ root@10.0.0.2 (test)
- ❌ user1@10.8.8.1 (test)

**Retained servers**:
- ✅ 10.8.8.81
- �
Confidence
75% confidence
Finding
Appending directly to ~/.ssh/authorized_keys is a persistence mechanism and is inherently high risk because it grants or maintains SSH access on remote systems. In this skill's context, that behavior is the advertised purpose, which makes it less indicative of malware intent, but the shell-based construction shown is still dangerous because misuse, mis-targeting, or command injection through unescaped key/comment data could lead to unauthorized access or account compromise across multiple servers.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
If the skill primarily launches a local web UI and does not actually perform multi-server SSH management, connectivity checks, or source tracking, the mismatch is severe because it misrepresents both functionality and risk. Such deception can cause users to expose credentials and install persistence without obtaining the promised security or operational benefit.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
If the skill primarily launches a local web UI and does not actually perform multi-server SSH management, connectivity checks, or source tracking, the mismatch is severe because it misrepresents both functionality and risk. Such deception can cause users to expose credentials and install persistence without obtaining the promised security or operational benefit.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
If the skill primarily launches a local web UI and does not actually perform multi-server SSH management, connectivity checks, or source tracking, the mismatch is severe because it misrepresents both functionality and risk. Such deception can cause users to expose credentials and install persistence without obtaining the promised security or operational benefit.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
If the skill primarily launches a local web UI and does not actually perform multi-server SSH management, connectivity checks, or source tracking, the mismatch is severe because it misrepresents both functionality and risk. Such deception can cause users to expose credentials and install persistence without obtaining the promised security or operational benefit.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
If the skill primarily launches a local web UI and does not actually perform multi-server SSH management, connectivity checks, or source tracking, the mismatch is severe because it misrepresents both functionality and risk. Such deception can cause users to expose credentials and install persistence without obtaining the promised security or operational benefit.

Static analysis

No suspicious patterns detected.