All Advisories

Orthanc DICOM Server

GZIP Decompression Bomb via Content-Encoding Header

Orthanc decompresses request bodies sent with Content-Encoding: gzip by reading the gzip ISIZE trailer (last 4 bytes of the stream, attacker-controlled) and pre-allocating an output buffer of that declared size with no upper bound. A small gzip payload claiming ~4 GB triggers a giant allocation per request.

Authored byVolker Schönefeld, Simon WeberDisclosed 2026-04-02Fully disclosed 2026-04-28
SeverityHighCVSS 7.5CVSS 3.1 VectorAV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:HCWECWE-770 (Allocation of Resources Without Limits or Throttling)ProductOrthanc DICOM ServerAffected VersionsOrthanc <= 1.12.10Fixed In1.12.11CVECVE-2026-5438CERT/CCVU#536588.8

Description

When a POST /instances request includes a Content-Encoding: gzip header, Orthanc decompresses the request body before parsing it as DICOM. The decompression entry point lives in the REST handler:

OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp:232-237

if (boost::iequals(call.GetHttpHeader("content-encoding", ""), "gzip"))
{
GzipCompressor compressor;
compressor.Uncompress(dicom, call.GetBodyData(), call.GetBodySize());
}

View source →

GzipCompressor::Uncompress queries the uncompressed size by reading the gzip ISIZE trailer — the last 4 bytes of the compressed stream, an attacker-controlled value — and uses it to size the output buffer:

OrthancFramework/Sources/Compression/GzipCompressor.cpp (GuessUncompressedSize)

uint64_t GzipCompressor::GuessUncompressedSize(const void* compressed,
size_t compressedSize)
{
const uint8_t* p = reinterpret_cast<const uint8_t*>(compressed)
+ compressedSize - 4;
return ((static_cast<uint32_t>(p[0]) << 0) + // attacker-controlled
(static_cast<uint32_t>(p[1]) << 8) + // max ~4 GB (uint32)
(static_cast<uint32_t>(p[2]) << 16) +
(static_cast<uint32_t>(p[3]) << 24));
}

View source →

OrthancFramework/Sources/Compression/GzipCompressor.cpp (Uncompress)

uncompressedSize = GuessUncompressedSize(compressed, compressedSize);
uncompressed.resize(static_cast<size_t>(uncompressedSize)); // no upper bound

View source →

An attacker compresses 64 zero bytes (yielding a 24-byte gzip stream) and patches the trailing ISIZE field to 0xFFFFFFFF (~4 GB). On Linux with default memory overcommit, resize() succeeds optimistically and the zero-initialization commits physical memory page-by-page during fault handling. Twenty concurrent requests collectively allocate ~80 GB and the OOM killer terminates the process with exit code 137. Total attack payload is 480 bytes (20 × 24-byte gzip streams).

The sibling ZlibCompressor path is not reachable from attacker input — every caller in Orthanc passes internally-compressed data with trusted size prefixes. The attack vector is exclusively through GzipCompressor via the HTTP Content-Encoding: gzip header.

Authentication is required: the auth check at HttpServer.cpp:1298 runs before the gzip decompression at OrthancRestApi.cpp:232. This is the same class of trust-the-metadata bug as CVE-2026-5440 (unbounded Content-Length) and CVE-2026-5439 (forged ZIP uncompressed_size).

Impact

  • Denial of service: a burst of ~20 concurrent HTTP requests (each a 24-byte gzip stream with patched ISIZE) terminates the Orthanc process via the Linux OOM killer (exit code 137).
  • Total attack payload is 480 bytes (20 × 24-byte gzip streams). No valid DICOM data is required, only the gzip framing with a patched ISIZE trailer.
  • The server must be restarted after each crash and is immediately vulnerable to the same attack again.

Mitigation

Update Orthanc to version 1.12.11 or later. The fix bounds the resize against a configurable maximum decompressed size and rejects payloads above it with BadFileFormat. As defense in depth, configure your reverse proxy to enforce a hard Content-Length ceiling — typically a few hundred MB — which also caps the unbounded Content-Length allocation in CVE-2026-5440 and the forged-ZIP allocation in CVE-2026-5439.

Defender's Checklist

  • Verify your version.

    curl -u <user>:<pass> http://<orthanc>:8042/system | jq .Version — the patched range begins at 1.12.11.

  • Cap request size at the reverse proxy.

    Configure a Content-Length ceiling appropriate to your DICOM workload (client_max_body_size 512m; in nginx). The gzip bomb triggers a ~4 GB allocation regardless of upload size, but a per-request body cap covers the family of trust-the-metadata bombs (this finding plus CVE-2026-5440 and CVE-2026-5439).

  • Disable Content-Encoding: gzip if unused.

    If your DICOM upload clients do not send gzipped bodies, strip the Content-Encoding header at your reverse proxy. The HTTP path that triggers GzipCompressor is gated entirely by this header.

  • Audit credentials.

    Any authenticated user with POST /instances rights can trigger the bug. Review RegisteredUsers in orthanc.json and any external authentication backend; revoke unused or over-permissioned API tokens.

  • Watch for OOM-restart cycles.

    Alert on repeated SIGKILL exits in your service supervisor (e.g. journalctl -u orthanc | grep -E 'killed|OOM|exit code 137'). A recurring cycle indicates ongoing exploitation rather than a one-off resource event.

Severity Reasoning

AV:NReachable over the network via Orthanc's HTTP REST API on POST /instances with Content-Encoding: gzip.AC:LA single 24-byte gzip stream with a patched ISIZE trailer; no timing or environmental conditions.PR:NThe published vector reflects deployments where Orthanc is exposed without HTTP authentication, which Orthanc permits via configuration. With AuthenticationEnabled set, the auth check runs before the gzip decompression and only an authenticated caller can trigger the allocation.UI:NNo interaction by a second user is needed.S:UThe OOM killer terminates the Orthanc process; the impacted component is the process itself.C:NNo information is read or disclosed.I:NNo state is modified.A:HSustained termination of the Orthanc process; restart returns service but leaves the process vulnerable to immediate re-exploitation.

References

How We Can Help

Who We Are

The security researchers behind this advisory.

Dr. Simon Weber Profile

Dr. rer. nat. Simon Weber

Senior Pentester & MedSec Researcher

I evaluate your SaMD with the same industry-defining security insight I contributed to the BAK MV for the revision of the B3S standard.

  • PhD on Hospital Cybersecurity
  • Critical vulnerabilities found in hospital systems
  • Alumni of THB MedSec Research Group
  • gematik Security Hero
Volker Schönefeld Profile

Dipl.-Inf. Volker Schönefeld

Senior Application Security Expert

As a former CTO and developer turned pentester, I work alongside your team to uncover vulnerabilities and find solutions that fit your architecture.

  • 20+ years as CTO, 50M+ app downloads
  • Architected and secured large-scale IoT fleets
  • Certified Web Exploitation Specialist
  • gematik Security Hero

Looking for a Penetration Test?

Machine Spirits specializes in security assessments for medical devices and healthcare IT. From MDR penetration testing to C5 cloud compliance, we help MedTech companies meet regulatory requirements.