#!/usr/bin/env python3 """ CVE-2026-3854 - Improper neutralization of push option values in GHE. Attacker with push access achieves RCE on the instance. Affects: GitHub Enterprise Server (all versions before fixes) Fixed in: Fixed in 3.14.25, 3.15.20, 3.16.16, 3.17.13, 3.18.7, 3.19.4 CISA KEV: 2026-03-10 """ import requests, sys, concurrent.futures, json, os requests.packages.urllib3.disable_warnings() UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" BANNER = ''' ╔══════════════════════════════════════════════════╗ ║ CVE-2026-3854 ║ ║ GitHub Enterprise Server Pre-auth RCE via Push Option Injection Scanner║ ╚══════════════════════════════════════════════════╝ ''' def scan_target(target): """Check if target is potentially vulnerable.""" base = f"https://{target}" if not target.startswith("http") else target base = base.rstrip("/") s = requests.Session() s.headers.update({"User-Agent": UA}) s.verify = False results = {"target": target, "vulnerable": False, "details": []} # Add CVE-specific checks here endpoints = ["/api/v1/", "/healthz", "/status", "/"] for ep in endpoints: try: r = s.get(f"{base}{ep}", timeout=8) if r.status_code == 200: results["details"].append(f"Endpoint accessible: {ep} ({r.status_code})") results["vulnerable"] = True except: pass return results def main(): print(BANNER) if len(sys.argv) < 2: print("Usage:") print(f" python3 ghe_rce_scanner.py ") print(f" python3 ghe_rce_scanner.py --info") sys.exit(1) if sys.argv[1] == "--info": print(f"CVE: CVE-2026-3854") print(f"Severity: CRITICAL") print(f"Product: GitHub Enterprise Server") print(f"Type: RCE via Push Option Injection") print(f"Desc: Improper neutralization of push option values in GHE. Attacker with push access achieves RCE on the instance.") print(f"Affected: GitHub Enterprise Server (all versions before fixes)") print(f"Fixed in: Fixed in 3.14.25, 3.15.20, 3.16.16, 3.17.13, 3.18.7, 3.19.4") print(f"CISA KEV: 2026-03-10") return targets = [] if os.path.isfile(sys.argv[1]): with open(sys.argv[1]) as f: targets = [l.strip() for l in f if l.strip()] else: targets = [sys.argv[1]] print(f"[*] Scanning {len(targets)} targets...\n") with concurrent.futures.ThreadPoolExecutor(max_workers=20) as ex: fut = {ex.submit(scan_target, t): t for t in targets} for f in concurrent.futures.as_completed(fut): r = f.result() if r["vulnerable"]: print(f"[!!] {r['target']}") for d in r["details"]: print(f" -> {d}") else: print(f"[ -] {r['target']} - No obvious exposure") print(f"\n[*] Scan complete") print(f"[*] Advisory: https://security.paloaltonetworks.com/CVE20263854") if __name__ == "__main__": main()