T05 · Unauthorized Access and Privilege Escalation
- Location
server.py:600- Finding
Unauthenticated Socket.IO Events Provide Arbitrary Host Shell Access
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill is a disclosed OpenClaw approvals dashboard, but it exposes high-impact pairing data and a host terminal through under-protected web/socket paths.
Review carefully before installing. Only run this on a trusted machine and do not expose the port beyond localhost. Change all defaults first, but note that changing passwords alone does not fix the unauthenticated Socket.IO terminal/data exposure, token-in-page exposure, or XSS-prone rendering. The terminal feature and gateway-token copy flow should be removed or strongly reworked before use with real OpenClaw credentials.
server.py:600Unauthenticated Socket.IO Events Provide Arbitrary Host Shell Access
server.py:569Unauthenticated Socket.IO Connections Receive Sensitive Pairing and Device Records
templates/channel_approvals.html:463Stored Cross-Site Scripting Through Pairing and Device Metadata
server.py:126Known Default Credentials and API Password Exposed in Frontend Source
templates/device_pairings.html:507Gateway Authentication Token Is Embedded Unsafely in Page JavaScript
templates/terminal.html:7Privileged Pages Load Unpinned Third-Party Dependencies Without Integrity Verification
The skill includes a fully interactive shell capability that is unrelated to the observable pairing-management purpose. That mismatch is a strong indicator of hidden remote administration/backdoor functionality, and exploitation would provide arbitrary code execution on the host.
Connected clients can obtain an interactive shell without any explicit disclosure that the application enables arbitrary command execution on the server. In context, this is far beyond pairing management and effectively acts as a concealed remote administration channel.
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.
master_fd, slave_fd = pty.openpty()
# Use subprocess instead of fork to avoid "multi-threaded fork" warnings
env = os.environ.copy()
env['TERM'] = 'xterm-256color'
env['COLORTERM'] = 'truecolor'
The client-side approval action embeds a hard-coded password in JavaScript and sends it with every POST to /approve, which exposes the secret to any user who can load the page, inspect source, or intercept browser traffic. The same function performs a sensitive state-changing action immediately on button click without an explicit confirmation step, increasing the risk of accidental or unauthorized approvals if the page is accessed by the wrong user or abused via surrounding application weaknesses.
The client-side code embeds a hardcoded approval password and uses it to perform a privileged pairing approval action from the browser. Any user who can load or inspect this page can recover the credential and invoke the same approval flow directly, defeating access control for a sensitive operation.
A sensitive approval credential is sent from browser code in every approval request, making the secret fully exposed to end users, browser tooling, source viewers, and potentially logs or intermediaries. In the context of a pairing dashboard, this directly enables unauthorized approval of device/account pairings and undermines trust in the approval workflow.
Allowing Socket.IO connections from any origin weakens browser-based trust boundaries and can enable cross-origin interaction with authenticated users' sessions, especially when paired with powerful socket actions like terminal access. In this application, broad CORS materially increases the blast radius of the shell and approval features.
# Force polling transport only — Werkzeug's dev server doesn't support WebSocket
# with threading async_mode, and the failed upgrade spams ugly 500 errors in the console.
socketio = SocketIO(app, cors_allowed_origins="*", async_mode=async_mode, transports=['polling'])
# In-memory storage for pairings (for simplicity)
# In a real app, use a database or more persistent storage.
The code defines one authentication mechanism via AUTH_PASSWORD but the login flow actually uses different hardcoded credentials, creating inconsistent and misleading security boundaries. This confusion weakens operator assumptions and leaves sensitive routes protected by guessable embedded credentials rather than a unified secure auth system.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
cmd.append('--notify')
print(f"Running command: {' '.join(cmd)}")
result = subprocess.run(
cmd,
capture_output=True,
text=True,
The gateway token is read from local state and injected into a rendered page, exposing a sensitive credential to the browser. Any XSS, browser compromise, malicious extension, or unintended client-side script access could leak that token and allow unauthorized gateway actions.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
cmd.append(request_id)
print(f"Running device approve command: {' '.join(cmd)}")
result = subprocess.run(
cmd,
capture_output=True,
text=True,
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
cmd.append(request_id)
print(f"Running device approve command: {' '.join(cmd)}")
result = subprocess.run(
cmd,
capture_output=True,
text=True,
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def approve_latest_device_pairing():
"""Quick endpoint to approve the most recent pending device pairing."""
try:
result = subprocess.run(
['openclaw', 'devices', 'approve', '--latest', '--json'],
capture_output=True,
text=True,
The SSH helper introduces remote terminal access with hardcoded connection parameters despite being unjustified by the stated purpose of managing pairings. Hidden remote-access code in an unrelated skill is suspicious and could be used to pivot into another machine or maintain covert access.
The SSH helper contains hardcoded remote credentials and uses AutoAddPolicy, combining credential exposure with disabled host authenticity checks. If used, this enables credential theft from source, unsafe connections to untrusted hosts, and unauthorized remote access.
This code spawns an interactive local shell for a connected client, effectively granting remote command execution on the host through the web interface. In this skill's pairing-management context, an embedded terminal is unrelated and dramatically increases risk because any compromise of the web session becomes full host compromise.
env['TERM'] = 'xterm-256color'
env['COLORTERM'] = 'truecolor'
process = subprocess.Popen(
[shell],
stdin=slave_fd,
stdout=slave_fd,
The shell executable is selected from the SHELL environment variable and then executed, so process behavior depends on ambient environment state instead of a fixed trusted path. While this is usually less exploitable than direct user input, it still creates an avoidable execution sink whose impact is amplified because the spawned process is a remote interactive shell.
env['TERM'] = 'xterm-256color'
env['COLORTERM'] = 'truecolor'
process = subprocess.Popen(
[shell],
stdin=slave_fd,
stdout=slave_fd,
The approve action sends a POST request that authorizes a device pairing, which is a security-relevant operation affecting access control. Unlike rejection, which uses a confirmation dialog, approval proceeds immediately with only a transient status message after initiation and no prior warning or confirmation.
This action approves the most recent pending request via a POST call without confirming which device is being approved or warning that access will be granted. Because the operation changes authorization state and may be triggered quickly from the UI, the absence of disclosure increases the risk of unintended approval.
This code handles a sensitive credential by reading the gateway token into client-side JavaScript and copying it to the user's clipboard, but it provides no warning, confirmation, or explanatory text about the sensitivity of that token. Clipboard placement of credentials can increase accidental disclosure risk because other applications or users may access pasted contents.
This HTML/JavaScript file implements an interactive terminal and emits every terminal input event to the server via socket.emit('terminal_input', { data: data }). There is no visible warning in the UI, confirmation step, or explanatory text telling users that their keystrokes will be transmitted to a backend shell session, which is a safety-relevant behavior affecting privacy and system interaction.
The inline comment states 'Remove the pairing from the list', but the implementation only disables the button, marks the item approved, and removes it two seconds later. This is a direct mismatch between the comment and actual behavior, even though the divergence is minor.
No suspicious patterns detected.