一、 背景概述与预警通报

OpenClaw 是一款开源的本地 AI Agent 框架。与传统的云端大模型助手不同,它支持通过自然语言直接操控宿主机,能够自主完成本地文件读写、网页浏览、Shell 命令执行等高阶操作。

然而,正是这种自动化与高权限的特性,使其在缺乏访问控制的企业内网中成为了一把双刃剑。2026年2月,工业和信息化部网络安全威胁和漏洞信息共享平台(NVDB)发布了关于防范 OpenClaw 开源 AI 智能体安全风险的紧急预警,明确指出:由于 OpenClaw 在部署时“信任边界模糊”,且具备自身持续运行、自主决策、调用系统和外部资源等特性,在缺乏有效权限控制的情况下,极易引发网络攻击、信息泄露、系统受控等一系列安全问题。

img

工信部网络安全威胁和漏洞信息共享平台(NVDB)发布的安全预警提示

二、 核心安全风险剖析

与普通办公软件不同,OpenClaw 这类 AI Agent 在企业环境中的风险,主要集中在其运行机制与生态开放性上:

2.1 高权限的“可执行代理”

OpenClaw 并不是一个简单的对话框,而是一个具备系统操作能力的执行节点。为了完成自动化任务,它默认需要访问本地文件、调用系统终端,甚至会接触到开发者的APIKey、云平台访问令牌及本地缓存数据。如果它运行在高权限账户下,一旦出现误操作或被恶意利用,将直接成为攻击者在内网收集信息与横向移动的跳板。

2.2 开放生态引发的供应链投毒

OpenClaw 支持通过 Skills(技能)、插件和扩展模块不断增强能力。这一机制虽然提升了灵活性,但也引入了严重的供应链安全隐患。 在近期的真实安全事件中,已出现名为 “GhostClaw” 的攻击案例。攻击者将包含恶意 RAT(远程访问木马)的组件伪装成 OpenClaw 安装包发布在 npm 平台上。用户一旦下载安装,程序在后台便会窃取浏览器凭证、SSH 密钥及加密钱包等核心敏感数据,实现了从“辅助工具”到“窃密后门”的转变。

img

Cyber Security News针对GhostClaw事件的描述

2.3 部署配置不当导致的网络暴露

OpenClaw 的 Gateway 默认监听 18789 端口,Control Service 等组件也可能占用 8000 或 9090 端口。在默认配置文档中(如 ~/.openclaw/openclaw.json),为了方便远程调用或测试,部分研发人员会将绑定地址从本地回环(127.0.0.1)修改为局域网或公网(0.0.0.0),且未配置任何身份认证。这种行为会直接将一个具备系统级控制权的接口暴露在内网甚至公网中。

img

openclaw.json配置文件示例

三、 传统排查方式的盲区

在收到预警后,许多企业的安全团队开始使用端口扫描脚本在内网寻找 OpenClaw。但大量反馈表明,单纯依赖“默认端口扫描”极易产生漏报,主要原因在于以下两个盲区:

3.1 仅监听本地回环地址

如果使用者遵循了最基础的安全习惯,将应用仅绑定在本地回环地址(127.0.0.1):

"controlUI": {
    "allowedOrigins": [
        "http://localhost:18789",
        "[http://127.0.0.1:18789](http://127.0.0.1:18789)"
    ]
}

此时,该服务不会对局域网开放,网络侧扫描工具完全无法探测到该端口的存活,但程序依然在员工的终端上高权限运行。

3.2 规避默认端口

具备一定安全意识或为避免端口冲突的开发者,会主动修改默认的 18789 端口。如果检测脚本仅对单一固定端口进行探测,同样会直接漏检。

四、 企业级多维自查与检测方案

鉴于上述盲区,Solar 应急响应团队建议企业采用“终端核查 + 网络扫描 + 流量审计”相结合的联动排查思路,从易到难、从面到点进行全面治理。

4.1 终端合规检测(精准度最高,适用于 EDR/MDM 下发)

如果你拥有域控、Jamf 或 EDR 设备的统一下发权限,直接在主机侧进行排查是最彻底的方式。此方案不受网络端口绑定状态的影响。

目前社区已有开源的检测脚本(如 GitHub 上的 knostic/openclaw-detect),提供了适用于多平台的检测逻辑。

  • 核心检测路径:扫描是否存在 ~/.openclaw/openclaw.json 或 ~/.config/openclaw 等默认配置目录。

  • 进程特征:检测是否存在包含 openclawclawdbotmoltbot 参数的 node 进程。

  • 利弊分析:精准度极高,无视网络隔离;缺点是执行速度相对较慢,需要管理员权限。注:在执行第三方脚本前,务必先进行代码审计,防范二次供应链投毒。

# 检测脚本地址
https://github.com/knostic/openclaw-detect

4.2 网络端主动扫描(适用于大网段快速摸排)

对于尚未部署完善终端管理体系的环境,主动扫描仍是快速收敛暴露面的首选方案。

方法 A:使用 Nmap 快速探测 利用 Nmap 对内网存活资产的 187898000 等高危端口进行探测。

# 扫描 192.168.146.0/24 网段的 18789 端口,仅输出开放状态的主机
nmap -p 18789 --open -T4 192.168.146.0/24 -oG - | grep "/open"
nmap -p 18789 --open -T4 192.168.146.0/24 -oG - | findstr "18789/open" # Windows命令执行

img

使用 Nmap 探测发现内网主机 192.168.146.154 开放了 18789 默认端口

方法 B:Python 深度特征验证脚本 为了降低 Nmap 端口扫描带来的误报(其他业务恰好使用了该端口),可以使用结合了 HTTP 探针验证的 Python 扫描脚本。 该脚本的逻辑分为两步:首先验证端口是否开放,随后向目标发送探测请求(如 /health/v1/agents/v1/models),并解析 HTTP Body 中是否包含 <openclaw-app> 或 status: ok 等特有指纹。

# -*- coding: utf-8 -*-
"""
OpenClaw(小龙虾)内网探测工具 - 端口扫描 + HTTP 特征双重验证
仅限企业内部授权资产自测与安全审查,严禁未经授权使用。

版本号:V1.0
授权给:Solar应急响应团队
"""

LOGO = r"""
              YYYYYYXXXYYYYYYYYYYYYYYYYYYYYYYYYYXQ
           XYYYXXXYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXYYYYJ
  ~X XXYYYYYXzXYYYY                          JYYXzzYYYYYYQ $$
  YYYYYYXzzXYYYU  |\|                      \\| 8JYYYzczYYYYXYr
  YXXXXYYYYYJX(|\\\\\/\/)              t\\\\\\\\|" JYYYYYXXXYY
  UXYXYY   ||\\\|\\\\||\\\\\       b\\\\\||\\\||\\\|    YXYYYY
  UXYXYY   \||\\\\\\\\\\\||/\/    \\\||\\\\\\||\\\\/c   YXYYXU
  UXYXYY   //\\\\///||\\\\\\(/    /(\\\\\||\\\\\/       YXYYXU
  YYYXYU   (/\\\\\/\\\/\||\\|/    /|\||\\///j   \\|/    YXYYXU
  XYXYYY   b/|\\\\\   )\\\\\(/    /(//\/\   |\\\\\(/    YXYYYX
  mYXYXY    /|\\\\\\\\\   |\|/    \||   \\/\\\||\\|\   cYXYXYU
   YXYXYJ   /\\\\\\\||\\\\\         /\\\\\||\\\\\\\/   YYYYXY
   YYXYYY   //\||\\\\\\||\\\\\    /\\\||\\\\\\\\\|/   JYXYXYY
    YXYXYU   \\\\\\||\\\\\\|(\    /(\\\\\\\\|\\\\\/   YXYYXY
    YYXYXY       /\\\\\||\\\|\    /|\\\\||/\//\\|/   UYXYXYY
     XYXYXY    f/t   |\\\\\|(/    /(||\\\\\  /(|/b  UYXYXYX
      YYXYYY   j/\\\/    \/\|/    /x  t\\\\\(\/   YXXYXYU
       YYXYYYJ   /\(\\\//    |    /|\\/   }\/\|/}  YYXYXYY
        UYXXXYY   /\\((\\\\\f     J\\\\\||(\\/  zYYXXYYc
         zYYXXXYX   /\\|\||\|/    /|||\\|\\/   YYXXXYU                      
           YYXXXYYX   \\\|\\(/    /|\||\\\   YYYXXXYX                      
            mYYXXXYYY   \\\\(/    /(\\\\  CYYYzXYYX                        
              JYYXzXYYYJ  |\|/    \||   XYYXzXYYY
                0YYYXzXYYYJ         UYYYYzzYXYU
                   YYYYzzXYYYYXQYYYYYXcXYYYX
                      YYYYYXzXYYYXzXYYYYY
                         XXXYYYYYYYYYY
"""

import socket
import ipaddress
import concurrent.futures
import time
import json
import re
import argparse

try:
    import urllib.request
    import urllib.error
except ImportError:
    urllib = None

# ---------------- 配置区 ----------------
TARGET_PORT = 18789           # OpenClaw 默认端口
TIMEOUT = 0.5                 # 端口扫描超时(秒)
HTTP_TIMEOUT = 2              # HTTP 请求超时(秒)
MAX_THREADS = 100             # 端口扫描并发线程数
HTTP_WORKERS = 20             # HTTP 验证并发数
# ----------------------------------------

# HTTP 探测路径,按优先级排序(先试开销小的)
HTTP_PROBE_PATHS = [
    "/health",        # 健康检查,通常无需认证
    "/v1/agents",     # OpenClaw 独有接口
    "/v1/models",     # OpenAI 兼容,可检测 openclaw 相关 model id
]


defcheck_port_open(ip):
    """
    仅检测端口是否开放,不建立完整连接后等待。
    成功返回 IP 字符串,失败返回 None。
    """
    try:
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.settimeout(TIMEOUT)
            result = s.connect_ex((str(ip), TARGET_PORT))
            if result == 0:
                return str(ip)
    except Exception:
        pass
    returnNone


defhttp_probe(ip, path="/health"):
    """
    对指定 IP 发送 GET 请求,返回 (status_code, body_text, headers_dict) 或 (None, None, None)。
    """
    url = f"http://{ip}:{TARGET_PORT}{path}"
    try:
        req = urllib.request.Request(url, method="GET")
        req.add_header("User-Agent", "OpenClaw-Scanner/1.0")
        with urllib.request.urlopen(req, timeout=HTTP_TIMEOUT) as resp:
            body = resp.read().decode("utf-8", errors="ignore")
            headers = dict(resp.headers)
            return resp.status, body, headers
    except urllib.error.HTTPError as e:
        # 401/403 等也说明该端口有 HTTP 服务在响应
        try:
            body = e.read().decode("utf-8", errors="ignore")
        except Exception:
            body = ""
        return e.code, body, dict(e.headers) if e.headers else {}
    except (urllib.error.URLError, OSError, socket.timeout, Exception):
        returnNone, None, None


defis_openclaw_response(status, body, path):
    """
    根据 HTTP 响应判断是否为 OpenClaw 实例。
    返回 (是否匹配, 匹配原因描述)。
    """
    if body isNoneor body == "":
        returnFalse, ""

    body_lower = body.lower()
    # 常见 OpenClaw 相关关键词
    openclaw_indicators = [
        "openclaw",
        "openclaw-agent-id",
        r'"status":\s*"ok"',           # /health 常见返回
        '"agents"',                   # /v1/agents 返回
        r'"data":\s*\[.*"openclaw',    # /v1/models 中可能包含 openclaw:main 等
        "invalid_request_error",      # 文档中的错误类型
    ]

    for indicator in openclaw_indicators:
        if re.search(indicator, body_lower, re.IGNORECASE | re.DOTALL):
            returnTrue, f"响应含特征: {indicator[:30]}"

    # /health 进一步特征判断
    if path == "/health":
        # 1) body 中直接包含 <openclaw-app>
        if"<openclaw-app>"in body:
            returnTrue, "health 响应中包含 <openclaw-app>"

        # 2) status 为 200 且 JSON 结构中带 status 字段
        if status == 200:
            try:
                obj = json.loads(body)
                if isinstance(obj, dict) and"status"in obj:
                    returnTrue, f"health 返回 status 字段: {obj.get('status')}"
            except json.JSONDecodeError:
                pass

    returnFalse, ""


defverify_openclaw(ip):
    """
    对单个 IP 进行 HTTP 特征验证。
    返回 (是否确认为 OpenClaw, 详细信息)。
    """
    for path in HTTP_PROBE_PATHS:
        status, body, headers = http_probe(ip, path)
        if status isNone:
            continue

        is_match, reason = is_openclaw_response(status, body, path)
        if is_match:
            detail = f"path={path} status={status} -> {reason}"
            returnTrue, detail

        # 可选:检查 Server 头
        server = (headers or {}).get("Server", "")
        if server and"openclaw"in server.lower():
            returnTrue, f"Server 头: {server}"

    returnFalse, ""


defscan_network(subnet):
    """
    扫描指定网段:先端口扫描,再对开放端口进行 HTTP 验证。
    """
    print(f"[*] 扫描网段: {subnet}")
    print(f"[*] 目标端口: {TARGET_PORT}")
    print(f"[*] 端口扫描线程: {MAX_THREADS},HTTP 验证并发: {HTTP_WORKERS}\n")

    port_open_ips = []
    confirmed_openclaw = []

    try:
        network = ipaddress.IPv4Network(subnet, strict=False)
    except ValueError as e:
        print(f"[!] 网段格式错误: {e}")
        print("[!] 请使用 CIDR 格式,例如: 192.168.1.0/24")
        return confirmed_openclaw

    # ---------- 阶段一:端口扫描 ----------
    start_time = time.time()
    with concurrent.futures.ThreadPoolExecutor(max_workers=MAX_THREADS) as executor:
        future_to_ip = {executor.submit(check_port_open, ip): ip for ip in network.hosts()}
        for future in concurrent.futures.as_completed(future_to_ip):
            ip = future_to_ip[future]
            try:
                result = future.result()
                if result:
                    port_open_ips.append(result)
                    print(f"[+] 端口开放: {result}:{TARGET_PORT}")
            except Exception:
                pass

    port_time = time.time() - start_time

    ifnot port_open_ips:
        print("\n[*] 未发现开放端口,无需 HTTP 验证。")
        print("=" * 50)
        print(f"耗时: {port_time:.2f} 秒")
        return confirmed_openclaw

    # ---------- 阶段二:HTTP 特征验证 ----------
    print(f"\n[*] 对 {len(port_open_ips)} 个主机进行 HTTP 特征验证...")
    http_start = time.time()

    with concurrent.futures.ThreadPoolExecutor(max_workers=HTTP_WORKERS) as executor:
        future_to_ip = {executor.submit(verify_openclaw, ip): ip for ip in port_open_ips}
        for future in concurrent.futures.as_completed(future_to_ip):
            ip = future_to_ip[future]
            try:
                is_openclaw, detail = future.result()
                if is_openclaw:
                    confirmed_openclaw.append(ip)
                    print(f"[!] 确认 OpenClaw: {ip}:{TARGET_PORT} | {detail}")
                else:
                    print(f"[-] 仅端口开放(非 OpenClaw): {ip}:{TARGET_PORT}")
            except Exception as e:
                print(f"[-] 验证异常 {ip}: {e}")

    total_time = time.time() - start_time

    print("\n" + "=" * 50)
    print(f"端口扫描耗时: {port_time:.2f} 秒")
    print(f"HTTP 验证耗时: {time.time() - http_start:.2f} 秒")
    print(f"总耗时: {total_time:.2f} 秒")
    print(f"端口开放: {len(port_open_ips)} 个")
    print(f"确认为 OpenClaw: {len(confirmed_openclaw)} 个")
    print("=" * 50)

    return confirmed_openclaw


defmain():
    parser = argparse.ArgumentParser(
        description="OpenClaw(小龙虾)内网探测工具 - 端口扫描 + HTTP 特征验证",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
示例:
  python scan_openclaw.py 192.168.1.0/24
  python scan_openclaw.py 10.0.0.0/16
  python scan_openclaw.py 172.16.0.0/24
        """
    )
    parser.add_argument(
        "subnet",
        nargs="?",
        help="目标网段,CIDR 格式,如 192.168.1.0/24"
    )
    args = parser.parse_args()

    target_subnet = (args.subnet or"").strip()
    ifnot target_subnet:
        print("版本号:V1.0")
        print("授权给:Solar应急响应团队")
        print(LOGO)
        print("""
      🦞 OpenClaw(小龙虾)内网探测工具 🦞
    ---------------------------------------------
    端口扫描 + HTTP 特征双重验证
    声明:仅限企业内部授权资产自测,严禁未经授权使用。
        """)
        target_subnet = input("请输入需要扫描的内网网段 (如 192.168.1.0/24): ").strip()
        ifnot target_subnet:
            print("未输入有效网段,程序退出。")
            return
    else:
        print("版本号:V1.0")
        print("授权给:Solar应急响应团队")
        print(LOGO)
        print(f"""
      🦞 OpenClaw(小龙虾)内网探测工具 🦞
    ---------------------------------------------
    目标网段: {target_subnet}
        """)

    if urllib isNone:
        print("[!] 需要 urllib(Python 标准库)以进行 HTTP 验证。")
        return

    scan_network(target_subnet)


if __name__ == "__main__":
    main()

img

基于python编写的内网探测OpenClaw资产脚本

  • 利弊分析:通过“端口存活+协议特征”双重验证,极大降低了误报率,结果高度可信;但弊端依然存在,它无法探测到绑定在 127.0.0.1 或修改了非常规端口的实例。

4.3 全流量审计与被动监测(应对深度隐藏,适用于 NDR/流量分析设备)

无论使用者如何隐藏进程或修改端口,只要 OpenClaw 产生交互,网络流量就会留下痕迹。结合科来等专业全流量分析厂商的研究,企业可以在流量审计设备上配置以下特征规则:

  • API请求与协议头特征:深度解析 HTTP 请求,关注 User-Agent 是否包含特定客户端标识,或是否存在 X-Stainless-Retry-CountX-Stainless-OS 等大模型调用框架专属 Header。

  • 通信交互上下文:监控访问 open.feishu.cn 等开放平台接口的流量,若该流量与外部大模型域名的 API 请求存在紧密的时序关联,可判定为智能体正在进行指令交互。

  • 加密隧道异常:部分实例可能通过 FRP 或 Ngrok 建立反向隧道隐藏通信。需结合数据包长度分布、时序特征等元数据识别异常的隐蔽外联。

img

img

img

以上流量特征提取思路参考自科来

内网藏“暗爪”?OpenClaw检测与治理全攻略》一文

五、 防御建议与安全加固

面对具备自主执行能力的 AI 智能体,传统的基于特征码的防御策略已显被动。对于确有研发和测试需求的企业,我们建议:

1.物理与逻辑隔离:严禁在涉及核心业务数据、生产环境的终端上直接运行 OpenClaw。所有测试必须在隔离的沙箱、虚拟机或独立的容器环境中进行,严格控制宿主机权限。

2.细粒度网络阻断:利用防火墙和微隔离策略,对非必须的网关端口及异常的跨网段横向请求进行阻断;限制测试环境向外联络的域名白名单。

3.插件准入机制:建立严格的第三方 AI 插件和 Skills 审计流程,禁止开发人员随意拉取来源不明的执行脚本,从源头切断供应链投毒风险。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐