# MONAILabel Server Crash and Silent Inference Corruption via Crafted DICOM (pydicom DS/IS Validation Bypass)

Unauthenticated attacker uploads a crafted DICOM with NaN/inf in DS tags to MONAILabel's REST API, crashing inference and silently corrupting segmentation geometry.

- Payout: Research
- Severity: critical
- Program: MONAILabel
- Found by: cldrn
- Disclosed: 2026-10-27
- CVE: CVE-2026-48705
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-6hp3-vr39-rqw8
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-6hp3-vr39-rqw8
- Tags: area:ai domain:ai domain:api impact:dos impact:integrity kind:research platform:ghsa sev:critical tech:python tech:rest technique:source-review vuln:dos

**Target:** MONAILabel REST API /datastore/ and /infer/ endpoints processing DICOM via pydicom and MONAI transforms
**Root cause:** pydicom's DSfloat/IS accept NaN, inf and scientific notation because is_valid_ds only runs in RAISE mode (default WARN), and MONAI's PydicomReader._get_affine builds the affine matrix from PixelSpacing/IPP/IOP with no isfinite checks, so NaN propagates through Spacingd into the model and writer.
**Steps:**
1. Start MONAILabel server on port 8000 with the radiology sample app and verify via `curl http://localhost:8000/info/`.
2. Confirm baseline: `curl -X POST "http://localhost:8000/infer/segmentation?image=spleen_40"` returns HTTP 200 with a valid mask.
3. Craft DICOM files (~132 KB) with `NaN`/`inf` in PixelSpacing and ImagePositionPatient DS fields.
4. Upload each via `PUT /datastore/` — accepted with HTTP 200, no auth.
5. Trigger `POST /infer/segmentation` on the uploaded image — HTTP 500 RuntimeError from itkGDCMImageIO after ~16.7s of compute; the poisoned file persists in the datastore.
**How found:** Reporter tested pydicom's DSfloat/IS constructors directly and observed NaN/inf/scientific notation accepted silently, then traced how those values flow through MONAI's PydicomReader._get_affine into LoadImaged/Spacingd and the inference writer, confirming 17/17 test vectors against MONAI 1.5.2 and pydicom 3.0.2.
**Exploitation:** No auth needed on default deployment; attacker with network access to port 8000 uploads a single small DICOM with non-finite DS values, which is stored permanently. Any inference on that image ID re-runs the full ML pipeline (~16s) and crashes with RuntimeError, aborting all active inference requests from legitimate users; if the writer does not crash, a geometrically wrong mask is returned with HTTP 200 and no warning.
**Escalation:** Persistence: the poisoned DICOM stays in the datastore across restarts, so a single upload yields repeatable DoS on that image ID; repeatable indefinitely with minimal bandwidth.
**Insight:** DICOM VR validators that only enforce in RAISE mode leave NaN/inf flowing into geometry math; when auditing medical imaging pipelines, feed non-finite values into spatial metadata tags (PixelSpacing, IPP, IOP) and follow them through affine construction and resampling rather than just the parser.

## Proof of concept (copied exactly from the original report)

**pydicom DSfloat accepts NaN and inf**

```
>>> from pydicom.valuerep import DSfloat
>>> DSfloat("NaN")
nan    # Accepted silently — should be rejected
>>> DSfloat("inf")
inf    # Accepted silently — should be rejected
```

**Attack commands: upload and trigger inference**

```
curl -X POST "http://localhost:8000/infer/segmentation?image=spleen_40" \
     -o baseline.nii.gz -w "HTTP %{http_code} - %{size_download} bytes"
# Result: HTTP 200, ~223,628 bytes valid segmentation mask

**Step 2 — Run the attack (NaN DICOM upload + inference):**
pip install pydicom numpy requests
python poc_monailabel_ds_bypass.py --server http://localhost:8000
```

