T01 · Skill Instruction Hijacking
- Location
SKILL.md:45- Finding
Agent Behavior Override Through Highest-Priority Skill Instructions
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill appears to deploy the promised remote browser, but it uses broad root privileges and persistent remote desktop/browser services that need careful review before installation.
Install only on a disposable or dedicated machine where a persistent remote browser is acceptable. Review firewall exposure for port 6080, avoid public internet access unless protected by VPN or another authenticated tunnel, do not grant NOPASSWD: ALL, and prefer running the browser/services as an unprivileged user with pinned dependencies and a clear uninstall path.
SKILL.md:45Agent Behavior Override Through Highest-Priority Skill Instructions
SKILL.md:117Unrestricted Passwordless Root Access Granted to the Deployment User
SKILL.md:67Unnecessary Probing of Cloud Instance Metadata Services
SKILL.md:284Persistent Root-Owned Remote Desktop and Browser Services Enabled Automatically
SKILL.md:220Chromium Runs as Root Without Its Sandbox and With Permissive CDP Origins
SKILL.md:127Unpinned Remote Dependencies Are Installed Globally With Elevated Privileges
Code accesses a cloud instance metadata endpoint (e.g. 169.254.169.254). A single request can return temporary IAM credentials, making this a high-value SSRF target for credential theft.
# 检测是否云服务器(腾讯云/阿里云/AWS metadata)
if curl -s --connect-timeout 2 http://metadata.tencentyun.com/latest/meta-data/ 2>/dev/null | grep -q .; then
echo "ENV_TYPE=CLOUD"
elif curl -s --connect-timeout 2 http://100.100.100.200/latest/meta-data/ 2>/dev/null | grep -q .; then
echo "ENV_TYPE=CLOUD"
elif curl -s --connect-timeout 2 http://169.254.169.254/latest/meta-data/ 2>/dev/null | grep -q .; then
echo "ENV_TYPE=CLOUD"
Code accesses a cloud instance metadata endpoint (e.g. 169.254.169.254). A single request can return temporary IAM credentials, making this a high-value SSRF target for credential theft.
echo "ENV_TYPE=CLOUD"
elif curl -s --connect-timeout 2 http://100.100.100.200/latest/meta-data/ 2>/dev/null | grep -q .; then
echo "ENV_TYPE=CLOUD"
elif curl -s --connect-timeout 2 http://169.254.169.254/latest/meta-data/ 2>/dev/null | grep -q .; then
echo "ENV_TYPE=CLOUD"
# 检测是否 NAS(群晖 Synology / QNAP 威联通)
elif [ -f /etc/synoinfo.conf ]; then
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.
# 权限预检
if [ "$(id -u)" = "0" ]; then
echo "PERMISSION_OK: 当前是 root 用户"
elif command -v sudo >/dev/null 2>&1 && sudo -n true 2>/dev/null; then
echo "PERMISSION_OK: 当前用户可免密 sudo"
else
echo "PERMISSION_DENIED: 当前用户不是 root,且无法免密 sudo。"
The instructions tell the user to grant full passwordless sudo without a clear warning that this is root-equivalent access and can affect the entire system. Normalizing such escalation in a convenience flow materially increases the chance of unsafe deployment.
The skill explicitly instructs granting the current user unrestricted passwordless sudo via NOPASSWD: ALL. That materially exceeds the minimum privilege needed to deploy a browser stack and creates a host-compromise pathway if the agent, user session, or follow-on commands are abused.
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.
权限不足处理:
# 首选:引导用户配置免密 sudo
echo "$(whoami) ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/hermes-deploy
# 部署完成后执行 sudo rm /etc/sudoers.d/hermes-deploy 收回权限
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
# 首选:引导用户配置免密 sudo
echo "$(whoami) ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/hermes-deploy
# 部署完成后执行 sudo rm /etc/sudoers.d/hermes-deploy 收回权限
The skill description markets one-click deployment but does not upfront warn that it exposes a remotely accessible desktop/browser endpoint and may require opening firewall ports. Users can underestimate the network exposure and deploy it in contexts where remote access is unacceptable.
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
**不适合场景:**
- 纯 API 调用任务,不需要浏览器画面
- 对安全要求极高、不能暴露任何远程桌面入口的生产环境
- Linux 模式:无法开放 `6080/tcp` 或无法提供 root/sudo 权限的服务器(Windows 模式无此限制)
**最终效果:**
- Linux 模式:用户访问 `http://<服务器IP>:6080/vnc.html`,Agent 控制 `http://127.0.0.1:9223/json`
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
**不适合场景:**
- 纯 API 调用任务,不需要浏览器画面
- 对安全要求极高、不能暴露任何远程桌面入口的生产环境
- Linux 模式:无法开放 `6080/tcp` 或无法提供 root/sudo 权限的服务器(Windows 模式无此限制)
**最终效果:**
- Linux 模式:用户访问 `http://<服务器IP>:6080/vnc.html`,Agent 控制 `http://127.0.0.1:9223/json`
This is the concrete step that grants passwordless sudo for all commands to the current user. It is a direct privilege-escalation expansion that can be exploited by any subsequent command execution in the session.
权限不足处理:
# 首选:引导用户配置免密 sudo
echo "$(whoami) ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/hermes-deploy
# 部署完成后执行 sudo rm /etc/sudoers.d/hermes-deploy 收回权限
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
权限不足处理:
# 首选:引导用户配置免密 sudo
echo "$(whoami) ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/hermes-deploy
# 部署完成后执行 sudo rm /etc/sudoers.d/hermes-deploy 收回权限
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
权限不足处理:
# 首选:引导用户配置免密 sudo
echo "$(whoami) ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/hermes-deploy
# 部署完成后执行 sudo rm /etc/sudoers.d/hermes-deploy 收回权限
The firewall-opening and port-forwarding steps increase attack surface by exposing a remote desktop/browser service, but the skill does not pair them with a direct warning about internet reachability, credential risks, or recommended network restrictions. This can lead to inadvertent exposure of an interactive browser environment.
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
# 生成 noVNC 访问密码(8 位随机字母+数字)
NOVNC_PASS=$(tr -dc 'A-Za-z0-9' </dev/urandom | head -c 8)
echo -n "$NOVNC_PASS" > /root/.novnc_password
chmod 600 /root/.novnc_password
x11vnc -storepasswd "$NOVNC_PASS" /root/.vncpasswd
chmod 600 /root/.vncpasswd
echo "noVNC 访问密码已生成:$NOVNC_PASS"
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
# 生成 noVNC 访问密码(8 位随机字母+数字)
NOVNC_PASS=$(tr -dc 'A-Za-z0-9' </dev/urandom | head -c 8)
echo -n "$NOVNC_PASS" > /root/.novnc_password
chmod 600 /root/.novnc_password
x11vnc -storepasswd "$NOVNC_PASS" /root/.vncpasswd
chmod 600 /root/.vncpasswd
echo "noVNC 访问密码已生成:$NOVNC_PASS"
The startup flow hardcodes navigation to https://www.baidu.com/, a Chinese-localized site, and later verification instructs opening Baidu as a default confirmation page. This imposes a language/locale choice without offering the user an option or documenting a justified region-specific requirement.
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.
EOF
systemctl daemon-reload systemctl enable --now xvfb.service x11vnc.service novnc.service chromium-remote.service
验证(应全部 `active`):
The skill repeatedly states that the browser should auto-open Baidu and tells the user that Baidu has been opened as part of completion messaging. This is a natural-language locale policy issue because it defaults users into a Chinese-language web experience without offering a language choice or explaining a region-specific necessity.
Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.
import json, time, urllib.request
import websocket
tabs = json.load(urllib.request.urlopen('http://127.0.0.1:9223/json'))
page = next(t for t in tabs if t.get('type') == 'page')
ws = websocket.create_connection(
page['webSocketDebuggerUrl'],
Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.
import json, time, urllib.request
import websocket
tabs = json.load(urllib.request.urlopen('http://127.0.0.1:9223/json'))
page = next(t for t in tabs if t.get('type') == 'page')
ws = websocket.create_connection(
page['webSocketDebuggerUrl'],
Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.
import json, time, urllib.request
import websocket
tabs = json.load(urllib.request.urlopen('http://127.0.0.1:9223/json'))
page = next(t for t in tabs if t.get('type') == 'page')
ws = websocket.create_connection(
page['webSocketDebuggerUrl'],
The skill instructs the agent to read a locally stored secret and disclose it in plain output. Although the password is intended for the user, normalizing plaintext secret extraction/output increases leakage risk through logs, transcripts, shared channels, or unintended recipients.
The reply templates require echoing the access credential back to the user as standard output, which may place the password into conversation logs or third-party messaging systems. For a service exposing a remote desktop endpoint, credential disclosure in routine output meaningfully raises takeover risk.
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.
mkdir -p ~/.openclaw/media/qqbot
cp /tmp/remote-browser.png ~/.openclaw/media/qqbot/remote-browser.png
<qqmedia>/root/.openclaw/media/qqbot/remote-browser.png</qqmedia>Detected: suspicious.exposed_resource_identifier