
Docker lab for validating CVE-2026-84383 in libheif through Discourse
An isolated Docker lab for independently validating the security boundary of CVE-2026-84383 / GHSA-g89c-p67h-r497 in libheif 1.23.1 and the 1.23.2 fix through Discourse's real authenticated upload and ImageMagick conversion path.
The lab performs four HTTP checks:
/tmp/cve-2026-84383-rce-marker owned by uid 1000 (discourse).The file contains one fixed command:
/usr/bin/id > /tmp/cve-2026-84383-rce-marker
This is a deterministic, mitigation-relaxed proof, not a production-ready exploit. Only the ImageMagick child runs with ASLR disabled and the Discourse image's bundled jemalloc preloaded. The ordinary ASLR-enabled/glibc path was confirmed vulnerable to memory corruption but was not shown to execute code. See Technical boundary.
internal.Do not deploy these images or run the fixture against systems you do not own or have explicit authorization to test.
The first build and Discourse database migration can take 10–30 minutes.
git clone https://github.com/dinosn/libheif-cve-2026-84383-lab.git
cd libheif-cve-2026-84383-lab
export LAB_ACK=I_UNDERSTAND_THIS_IS_AN_ISOLATED_LAB
./scripts/verify.sh
Expected terminal gates:
[cve-2026-84383] vulnerable HTTP run 1 status=200
[cve-2026-84383] vulnerable HTTP run 2 status=200
[cve-2026-84383] patched exploit control status=422 marker=absent
[cve-2026-84383] patched benign control status=200 marker=absent extension=jpeg
[cve-2026-84383] PASS: all marker, patched, and benign HTTP gates passed
[cve-2026-84383] containers, volumes, temporary key, inputs, and marker removed
HTTP 200 alone is not the execution oracle. The verifier removes the marker before every request and then checks its content and uid. The successful exploit response is intentionally incomplete because the controlled ROP tail exits the converter after writing the marker.
Force an image rebuild with LAB_REBUILD=1 ./scripts/verify.sh.
| Component | Identity |
|---|---|
| Discourse base | discourse/discourse@sha256:709c40fed5ea6fc8d2945aff57fd9bd4b11629195d1bda03eb3ec55b92e3e44a |
| Vulnerable libheif source | 1.23.1, SHA-256 0de0327f60fcd47de90d5654c6fe152232738d60d84fe084ec3e0f35e03b166a |
| Patched libheif source | 1.23.2, SHA-256 8bd5d41d19dc84536d118b04774709f244df6104ef66d623dad5fa4650143405 |
| Marker fixture | SHA-256 130c89eff9b8dae1765f22326eaf8222e95c7c8c8201203f01ad04c4aeb5ef5e |
| Benign fixture | SHA-256 dcffc5fb99c6a34b27ff1bc451ccf091c2854c7bcbf2c7fdc1445286c917c6a9 |
SHA256SUMS is checked before Docker is started. The Docker builds also reject
unexpected upstream source archives and unexpected critical runtime binaries.
Upgrade libheif to 1.23.2 or later. Keep converter sandboxing, ASLR, seccomp, least privilege, upload validation, and process isolation as defense in depth. The libheif 1.23.2 release describes the security fix.
Dockerfile vulnerable libheif 1.23.1 application image
Dockerfile.patched causal 1.23.2 control
Dockerfile.noaslr deterministic child-process wrapper
compose.yaml internal-only Discourse, PostgreSQL, and Redis
compose.noaslr.yaml isolated seccomp override for setarch(1)
fixtures/ fixed marker-only and benign HEIF inputs
scripts/preflight.sh architecture, network, and integrity gates
scripts/verify.sh complete positive/control/cleanup workflow
docs/ technical boundary and expected evidence