Skip to content

CVE-2026-28470 — OpenClaw versions prior to 2026.2.2 contain an exec approvals (must be enabled) allowlist…

Key facts

CVE-2026-28470 is a Critical (9.8) vulnerability: OpenClaw versions prior to 2026.2.2 contain an exec approvals (must be enabled) allowlist…. This entry ships ready-to-deploy detection and prevention rules (Snort, YARA, Nginx, Apache, Sigma) plus a detection-only PoC and a Docker test lab.

CVE CVE-2026-28470
Severity Critical (9.8)
Detection / prevention Snort, YARA, Nginx, Apache, Sigma
PoC poc.py
Source 2026/CVE-2026-28470/

Details

CVE-2026-28470 is a Critical (9.8) vulnerability: OpenClaw versions prior to 2026.2.2 contain an exec approvals (must be enabled) allowlist…. This entry ships a detection-only proof-of-concept (PoC), a Docker test lab, and ready-to-deploy detection and prevention rules (Snort, YARA, Nginx, Apache) — see Detection and prevention below. Source: NVD record.

English | 한국어

Description

OpenClaw versions prior to 2026.2.2 contain an exec approvals (must be enabled) allowlist bypass vulnerability that allows attackers to execute arbitrary commands by injecting command substitution syntax. Attackers can bypass the allowlist protection by embedding unescaped $() or backticks inside double-quoted strings to execute unauthorized commands.

Vulnerability Details

Property Value
CVE ID CVE-2026-28470
Type RCE
CVSS Score 9.8

Proof of Concept

Download

# Download PoC script
curl -O https://raw.githubusercontent.com/fankh/vulnerability-poc/main/2026/CVE-2026-28470/poc.py

Usage

# Run PoC against target
python3 poc.py --target <TARGET_URL>

# With authentication
python3 poc.py --target <TARGET_URL> --username <USER> --password <PASS>

⚠️ Warning: This PoC is for authorized security testing only. It uses detection-only techniques and does not exploit or damage target systems.

Test Lab

This repository includes a Docker-based test environment with both vulnerable and patched applications.

Quick Start

# Clone and navigate to CVE directory
cd CVE-2026-28470

# Start both applications
docker-compose up -d

# Run tests
./run-tests.sh    # Linux/Mac
./run-tests.ps1   # Windows PowerShell

Application Endpoints

Application URL Status
Vulnerable App http://localhost:8080 Shows vulnerability
Patched App http://localhost:8081 Demonstrates fix

Directory Structure

├── README.md           # English documentation
├── README_KO.md        # Korean documentation
├── poc.py              # PoC detection script
├── docker-compose.yml  # Container orchestration
├── run-tests.sh        # Linux/Mac test runner
├── run-tests.ps1       # Windows test runner
├── vulnerable-app/     # Vulnerable version
│   ├── app.py
│   ├── Dockerfile
│   └── requirements.txt
└── patched-app/        # Fixed version
    ├── app.py
    ├── Dockerfile
    └── requirements.txt

References

  • https://github.com/openclaw/openclaw/commit/d1ecb46076145deb188abcba8f0699709ea17198
  • https://github.com/openclaw/openclaw/security/advisories/GHSA-3hcm-ggvf-rch5
  • https://www.vulncheck.com/advisories/openclaw-exec-allowlist-bypass-via-command-substitution-in-double-quotes

Generated by PoC Generator on 2026-04-10T16:24:23.094188812

Detection and prevention

Ready-to-deploy defensive rules for CVE-2026-28470, for authorized testing and blue-team detection engineering:

References: NVD — CVE-2026-28470 · full write-up on the searchable site

Detection & prevention rules

alert tcp any any -> any any (msg:"CVE-2026-28470 OpenClaw RCE Command Injection via Dollar Parentheses"; flow:established,to_server; content:"OpenClaw"; nocase; content:"$("; distance:0; content:")"; distance:0; within:1000; classtype:attempted-admin; sid:1000000; rev:1; priority:1;)

alert tcp any any -> any any (msg:"CVE-2026-28470 OpenClaw RCE Command Injection via Backticks"; flow:established,to_server; content:"OpenClaw"; nocase; content:"`"; distance:0; content:"`"; distance:1; within:1000; classtype:attempted-admin; sid:1000001; rev:1; priority:1;)

alert tcp any any -> any any (msg:"CVE-2026-28470 OpenClaw RCE Command Substitution in Double Quotes"; flow:established,to_server; content:"OpenClaw"; nocase; content:"|22|"; content:"$("; distance:0; within:500; content:")|22|"; distance:0; within:500; classtype:attempted-admin; sid:1000002; rev:1; priority:1;)

alert tcp any any -> any any (msg:"CVE-2026-28470 OpenClaw RCE Backtick Substitution in Double Quotes"; flow:established,to_server; content:"OpenClaw"; nocase; content:"|22|"; content:"`"; distance:0; within:500; content:"`"; distance:1; within:500; content:"|22|"; distance:0; within:500; classtype:attempted-admin; sid:1000003; rev:1; priority:1;)

alert tcp any any -> any any (msg:"CVE-2026-28470 OpenClaw RCE Exec Approval Bypass Attempt"; flow:established,to_server; content:"exec"; nocase; content:"approval"; nocase; distance:0; within:100; pcre:"/(\$\([^)]+\)|`[^`]+`)/i"; classtype:attempted-admin; sid:1000004; rev:1; priority:1;)

:material-file-download: prevention/snort.rules

rule CVE_2026_28470_OpenClaw_RCE_Command_Injection {
    meta:
        author = "Security Engineer"
        description = "Detects OpenClaw RCE vulnerability CVE-2026-28470 command injection attempts"
        cve = "CVE-2026-28470"
        date = "2024-01-01"
        severity = "high"
        category = "exploit"

    strings:
        $cmd_subst_1 = "\"$(" ascii nocase
        $cmd_subst_2 = "$()\"" ascii nocase
        $cmd_subst_3 = "\"`" ascii nocase
        $cmd_subst_4 = "`\"" ascii nocase

        $pattern_1 = /"\$\([^)]*\)"/ ascii
        $pattern_2 = /"`[^`]*`"/ ascii
        $pattern_3 = /"[^"]*\$\([^)]*\)[^"]*"/ ascii
        $pattern_4 = /"[^"]*`[^`]*`[^"]*"/ ascii

        $openclaw_exec = "openclaw" ascii nocase
        $exec_approval = "exec" ascii nocase
        $allowlist = "allowlist" ascii nocase

        $malicious_cmd_1 = "$(cat /etc/passwd)" ascii nocase
        $malicious_cmd_2 = "$(whoami)" ascii nocase
        $malicious_cmd_3 = "$(id)" ascii nocase
        $malicious_cmd_4 = "`cat /etc/passwd`" ascii nocase
        $malicious_cmd_5 = "`whoami`" ascii nocase
        $malicious_cmd_6 = "$(wget" ascii nocase
        $malicious_cmd_7 = "$(curl" ascii nocase
        $malicious_cmd_8 = "`wget" ascii nocase
        $malicious_cmd_9 = "`curl" ascii nocase

    condition:
        any of ($cmd_subst_*) or 
        any of ($pattern_*) or
        (any of ($malicious_cmd_*) and ($openclaw_exec or $exec_approval))
}

rule CVE_2026_28470_OpenClaw_Exploit_Artifacts {
    meta:
        author = "Security Engineer"
        description = "Detects exploitation artifacts for CVE-2026-28470 OpenClaw command injection"
        cve = "CVE-2026-28470"
        date = "2024-01-01"
        severity = "high"
        category = "artifact"

    strings:
        $hex_pattern_1 = { 22 24 28 }  // "$( in hex
        $hex_pattern_2 = { 29 22 }     // )" in hex
        $hex_pattern_3 = { 22 60 }     // "` in hex
        $hex_pattern_4 = { 60 22 }     // `" in hex

        $log_pattern_1 = "exec approval" ascii nocase
        $log_pattern_2 = "allowlist bypass" ascii nocase
        $log_pattern_3 = "command substitution" ascii nocase
        $log_pattern_4 = "openclaw" ascii nocase

        $exploit_string_1 = "\"$(echo" ascii nocase
        $exploit_string_2 = "echo)\"" ascii nocase
        $exploit_string_3 = "\"`echo" ascii nocase
        $exploit_string_4 = "echo`\"" ascii nocase
        $exploit_string_5 = "\"$(ls" ascii nocase
        $exploit_string_6 = "\"$(ps" ascii nocase

    condition:
        (any of ($hex_pattern_*) and any of ($log_pattern_*)) or
        any of ($exploit_string_*)
}

rule CVE_2026_28470_OpenClaw_Memory_Indicators {
    meta:
        author = "Security Engineer"
        description = "Memory-based indicators for CVE-2026-28470 OpenClaw exploitation"
        cve = "CVE-2026-28470"
        date = "2024-01-01"
        severity = "medium"
        category = "memory"

    strings:
        $memory_1 = "openclaw.exe" ascii nocase
        $memory_2 = "exec_approval" ascii nocase
        $memory_3 = "allowlist_check" ascii nocase

        $shell_exec_1 = "/bin/sh" ascii
        $shell_exec_2 = "/bin/bash" ascii
        $shell_exec_3 = "cmd.exe" ascii nocase
        $shell_exec_4 = "powershell" ascii nocase

        $injection_marker_1 = { 24 28 [1-50] 29 }  // $(...) pattern
        $injection_marker_2 = { 60 [1-50] 60 }     // `...` pattern

    condition:
        any of ($memory_*) and 
        (any of ($shell_exec_*) or any of ($injection_marker_*))
}

:material-file-download: prevention/yara.yar

# CVE-2026-28470: OpenClaw RCE Prevention Rules
# Block command substitution syntax in exec approvals bypass attempts

# Block requests containing command substitution patterns
location / {
    # Block $() command substitution syntax
    if ($request_uri ~* "\$\([^)]*\)") {
        return 403;
    }

    # Block backtick command substitution
    if ($request_uri ~* "`[^`]*`") {
        return 403;
    }

    # Block encoded command substitution patterns
    if ($request_uri ~* "(%24|%5C%24)\([^)]*\)") {
        return 403;
    }

    # Block encoded backticks
    if ($request_uri ~* "(%60|%5C%60)[^%60]*(%60|%5C%60)") {
        return 403;
    }
}

# Block command substitution in request body
location /api/ {
    # Check request body for command substitution
    lua_need_request_body on;
    access_by_lua_block {
        local body = ngx.var.request_body
        if body then
            # Check for $() patterns
            if string.match(body, "%$%([^%)]*%)") then
                ngx.status = 403
                ngx.say("Blocked: Command substitution detected")
                ngx.exit(403)
            end

            # Check for backtick patterns
            if string.match(body, "`[^`]*`") then
                ngx.status = 403
                ngx.say("Blocked: Command substitution detected")
                ngx.exit(403)
            end
        end
    }
}

# Block suspicious headers that might contain command injection
location / {
    # Block command substitution in User-Agent
    if ($http_user_agent ~* "\$\([^)]*\)|`[^`]*`") {
        return 403;
    }

    # Block command substitution in Referer
    if ($http_referer ~* "\$\([^)]*\)|`[^`]*`") {
        return 403;
    }

    # Block command substitution in custom headers
    if ($http_x_forwarded_for ~* "\$\([^)]*\)|`[^`]*`") {
        return 403;
    }
}

# Specific protection for OpenClaw exec approval endpoints
location ~* ^/(exec|execute|approval|command)/ {
    # Strict filtering for exec-related endpoints
    if ($request_method !~ ^(GET|POST)$) {
        return 405;
    }

    # Block any shell metacharacters
    if ($request_uri ~* "[\$`;&|><\\\n\r]") {
        return 403;
    }

    # Block double-quoted strings containing command substitution
    if ($request_uri ~* "\"[^\"]*(\$\([^)]*\)|`[^`]*`)[^\"]*\"") {
        return 403;
    }

    # Add security headers
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options DENY;
    add_header X-XSS-Protection "1; mode=block";
}

# Block query parameters with command substitution
map $args $block_args {
    default 0;
    "~*\$\([^)]*\)" 1;
    "~*`[^`]*`" 1;
    "~*(%24|%5C%24)\([^)]*\)" 1;
    "~*(%60|%5C%60)[^%60]*(%60|%5C%60)" 1;
}

location / {
    if ($block_args = 1) {
        return 403;
    }
}

# Rate limiting for exec-related endpoints
limit_req_zone $binary_remote_addr zone=exec_limit:10m rate=5r/m;

location ~* ^/(exec|execute|approval|command)/ {
    limit_req zone=exec_limit burst=2 nodelay;
    limit_req_status 429;
}

:material-file-download: prevention/nginx.conf

# ModSecurity rules for CVE-2026-28470 - OpenClaw RCE via command substitution bypass
# Protects against command injection through $() and backtick substitution in double-quoted strings

# Rule 1000000: Block command substitution with $() syntax
SecRule REQUEST_BODY "@detectSQLi" \
    "id:1000000,\
    phase:2,\
    block,\
    msg:'CVE-2026-28470: Command substitution attack detected - $() syntax',\
    logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}',\
    severity:CRITICAL,\
    tag:'attack-injection',\
    tag:'CVE-2026-28470',\
    tag:'OWASP_CRS',\
    ver:'CVE-2026-28470/1.0',\
    maturity:1,\
    accuracy:8,\
    t:none,\
    ctl:auditLogParts=+E,\
    setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'"

SecRule ARGS "@rx \$\([^)]*\)" \
    "id:1000001,\
    phase:2,\
    block,\
    msg:'CVE-2026-28470: Command substitution attack detected - $() in parameters',\
    logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}',\
    severity:CRITICAL,\
    tag:'attack-injection',\
    tag:'CVE-2026-28470',\
    tag:'OWASP_CRS',\
    ver:'CVE-2026-28470/1.0',\
    maturity:1,\
    accuracy:9,\
    t:none,\
    ctl:auditLogParts=+E,\
    setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'"

# Rule 1000002: Block backtick command substitution
SecRule ARGS "@rx `[^`]*`" \
    "id:1000002,\
    phase:2,\
    block,\
    msg:'CVE-2026-28470: Backtick command substitution attack detected',\
    logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}',\
    severity:CRITICAL,\
    tag:'attack-injection',\
    tag:'CVE-2026-28470',\
    tag:'OWASP_CRS',\
    ver:'CVE-2026-28470/1.0',\
    maturity:1,\
    accuracy:9,\
    t:none,\
    ctl:auditLogParts=+E,\
    setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'"

# Rule 1000003: Block command substitution in double-quoted strings
SecRule ARGS "@rx \"[^\"]*\$\([^)]*\)[^\"]*\"" \
    "id:1000003,\
    phase:2,\
    block,\
    msg:'CVE-2026-28470: Command substitution in double-quoted string detected',\
    logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}',\
    severity:CRITICAL,\
    tag:'attack-injection',\
    tag:'CVE-2026-28470',\
    tag:'OWASP_CRS',\
    ver:'CVE-2026-28470/1.0',\
    maturity:1,\
    accuracy:9,\
    t:none,\
    ctl:auditLogParts=+E,\
    setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'"

# Rule 1000004: Block backtick substitution in double-quoted strings
SecRule ARGS "@rx \"[^\"]*`[^`]*`[^\"]*\"" \
    "id:1000004,\
    phase:2,\
    block,\
    msg:'CVE-2026-28470: Backtick substitution in double-quoted string detected',\
    logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}',\
    severity:CRITICAL,\
    tag:'attack-injection',\
    tag:'CVE-2026-28470',\
    tag:'OWASP_CRS',\
    ver:'CVE-2026-28470/1.0',\
    maturity:1,\
    accuracy:9,\
    t:none,\
    ctl:auditLogParts=+E,\
    setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'"

# Rule 1000005: Block command substitution in request body
SecRule REQUEST_BODY "@rx \$\([^)]*\)" \
    "id:1000005,\
    phase:2,\
    block,\
    msg:'CVE-2026-28470: Command substitution attack in request body',\
    logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}',\
    severity:CRITICAL,\
    tag:'attack-injection',\
    tag:'CVE-2026-28470',\
    tag:'OWASP_CRS',\
    ver:'CVE-2026-28470/1.0',\
    maturity:1,\
    accuracy:8,\
    t:none,\
    ctl:auditLogParts=+E,\
    setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'"

# Rule 1000006: Block backtick substitution in request body
SecRule REQUEST_BODY "@rx `[^`]*`" \
    "id:1000006,\
    phase:2,\
    block,\
    msg:'CVE-2026-28470: Backtick substitution attack in request body',\
    logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}',\
    severity:CRITICAL,\
    tag:'attack-injection',\
    tag:'CVE-2026-28470',\
    tag:'OWASP_CRS',\
    ver:'CVE-2026-28470/1.0',\
    maturity:1,\
    accuracy:8,\
    t:none,\
    ctl:auditLogParts=+E,\
    setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'"

# Rule 1000007: Block command substitution in HTTP headers
SecRule REQUEST_HEADERS "@rx \$\([^)]*\)" \
    "id:1000007,\
    phase:1,\
    block,\
    msg:'CVE-2026-28470: Command substitution attack in HTTP headers',\
    logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}',\
    severity:CRITICAL,\
    tag:'attack-injection',\
    tag:'CVE-2026-28470',\
    tag:'OWASP_CRS',\
    ver:'CVE-2026-28470/1.0',\
    maturity:1,\
    accuracy:8,\
    t:none,\
    ctl:auditLogParts=+E,\
    setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'"

# Rule 1000008: Block backtick substitution in HTTP headers
SecRule REQUEST_HEADERS "@rx `[^`]*`" \
    "id:1000008,\
    phase:1,\
    block,\
    msg:'CVE-2026-28470: Backtick substitution attack in HTTP headers',\
    logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}',\
    severity:CRITICAL,\
    tag:'attack-injection',\
    tag:'CVE-2026-28470',\
    tag:'OWASP_CRS',\
    ver:'CVE-2026-28470/1.0',\
    maturity:1,\
    accuracy:8,\
    t:none,\
    ctl:auditLogParts=+E,\
    setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'"

:material-file-download: prevention/httpd.conf

Frequently asked questions

What is CVE-2026-28470?

CVE-2026-28470 is a Critical (9.8) vulnerability: OpenClaw versions prior to 2026.2.2 contain an exec approvals (must be enabled) allowlist…. This entry ships ready-to-deploy detection and prevention rules (Snort, YARA, Nginx, Apache, Sigma) plus a detection-only PoC and a Docker test lab.

How do I detect CVE-2026-28470?

Deploy the Snort and YARA detection rule included with this entry (see Detection & prevention rules below), then validate it against the detection-only PoC in the Docker test lab.

How do I block or mitigate CVE-2026-28470?

Apply the Nginx and Apache mitigation config included with this entry, and upgrade to a fixed version per the vendor advisory (see the NVD record for affected versions).


For authorized security testing and research only. See the full source and NVD record.