Script dicom-store-fuzzer

Script types: portrule
Categories: fuzzer, intrusive
Download: https://svn.nmap.org/nmap/scripts/dicom-store-fuzzer.nse

Script Summary

DICOM C-STORE fuzzer for post-association PACS vulnerability testing.

Reads a fuzz corpus generated by dicom_fuzzgen.py and transmits each malformed DICOM file to the target PACS via C-STORE (DIMSE), monitoring for crashes, hangs, unexpected responses, and anomalous behaviour.

The attack surface is post-association but pre-authentication in any application-layer sense -- most PACS accept C-STORE from any associated SCU without further authentication checks.

Test coverage: * Tag value overflow / boundary violations * VR type confusion and mismatch * Sequence nesting attacks (stack overflow, OOM) * Pixel data mutations (buffer size, bit depth, encapsulation) * Transfer syntax abuse (deflate bombs, JPEG corruption, encoding mismatch) * Private and unknown tag injection * File Meta Information attacks * String encoding and character set attacks * DICOM-specific logic bombs * CVE-specific payloads (multiple vendors)

Anomaly classification: SUCCESS -- C-STORE-RSP Status 0x0000 (notable if input was heavily malformed) FAILURE -- C-STORE-RSP Status != 0x0000 (expected for malformed input) REJECT -- A-ASSOCIATE-RJ (SOP class not supported) ABORT -- A-ABORT from target (potential parser panic) TIMEOUT -- No response within timeout (potential crash or infinite loop) RESET -- TCP RST (process likely crashed) CONN_REFUSED -- Cannot connect (target service down / crashed) CORRUPT_RSP -- Response not a valid DICOM PDU (memory corruption indicator)

Health checks run automatically after each configurable interval and after any TIMEOUT or RESET. Three consecutive health-check failures constitute a CRITICAL CRASH; fuzzing halts and the triggering case is reported.

WARNING: This is an intrusive script. It WILL send malformed DICOM data to the target PACS and MAY cause crashes, hangs, data corruption, or exhaustion of service resources. Only use against systems for which you have explicit written authorisation. Medical imaging systems are life-critical infrastructure.

Requires the dicom.lua library (place in nselib/ or same directory).

Script Arguments

dicom-store-fuzzer.output

Output verbosity: "summary","verbose","debug" (default: "summary")

dicom-store-fuzzer.categories

Comma-separated categories to send: "all","cat01","cat03,cat04" (default: "all")

dicom-store-fuzzer.results_file

Write JSON results to this path (default: /tmp/dicom-store-fuzzer-<ip>.json)

dicom-store-fuzzer.delay

Delay between cases in milliseconds (default: 200)

dicom-store-fuzzer.corpus

Path to fuzz_corpus directory containing manifest.json (REQUIRED)

dicom-store-fuzzer.cases

Specific case IDs: "TC0001,TC0042" (default: all)

dicom-store-fuzzer.max_pdu

Max PDU Length to propose in bytes (default: 16384)

dicom-store-fuzzer.called_ae

Target AE Title (default: "ORTHANC")

dicom-store-fuzzer.resume

Resume from this case ID after a previous crash (default: none)

dicom-store-fuzzer.baseline

Send baseline before fuzzing to verify C-STORE works (default: true)

dicom-store-fuzzer.fallback_sc

Use Secondary Capture fallback if SOP class rejected (default: true)

dicom-store-fuzzer.calling_ae

Our AE Title (default: "NMAP-FUZZ")

dicom-store-fuzzer.timeout

C-STORE response timeout in seconds (default: 10)

dicom-store-fuzzer.health_interval

Health check every N cases (default: 10)

dicom-store-fuzzer.batch

Cases per association; 1 = per-case isolation (default: 1)

dicom.called_aet, dicom.calling_aet

See the documentation for the dicom library.

Example Usage

  • nmap -p 104,11112 --script dicom-store-fuzzer \
      --script-args 'dicom-store-fuzzer.corpus=/path/to/fuzz_corpus_v5,dicom-store-fuzzer.called_ae=ORTHANC' <target>
    
  • nmap -p 11112 --script dicom-store-fuzzer \
      --script-args 'dicom-store-fuzzer.corpus=./fuzz_corpus_v5,dicom-store-fuzzer.categories=cat03,cat04,dicom-store-fuzzer.timeout=15' <target>
    
  • nmap -p 104 --script dicom-store-fuzzer \
      --script-args 'dicom-store-fuzzer.corpus=./fuzz_corpus_v5,dicom-store-fuzzer.cases=TC0001,TC0042,TC0300,dicom-store-fuzzer.output=verbose' <target>
    

Script Output

PORT     STATE SERVICE
104/tcp  open  dicom
| dicom-store-fuzzer:
|   Target AE: ORTHANC  Calling AE: NMAP-FUZZ
|   Corpus: /path/to/fuzz_corpus_v5  (1357 cases queued)
|   Baseline: SUCCESS (C-STORE accepted)
|   Cases Sent: 1357 / 1357
|   Results: SUCCESS=898 FAILURE=384 TIMEOUT=4 RESET=2 REJECT=18 ABORT=6 CORRUPT=0
|   CRITICAL FINDINGS:
|     [CRASH]   TC0215 cat03_sequence_nesting/TC0215_depth_500.dcm
|               Sequence depth 500 - TCP RST, target unresponsive for 8s
|     [TIMEOUT] TC0401 cat05_transfer_syntax/TC0401_deflate_bomb.dcm
|               Deflate bomb - No response after 10s, target recovered
|     [ACCEPT]  TC0507 cat06_private_tags/TC0507_inject_shell_dollar.dcm
|               Shell injection in PatientName - Stored successfully
|   Results file: /tmp/dicom-store-fuzzer-192.168.1.50.json
|_

Requires


Author:

  • Paulino Calderon <paulino@calderonpale.com>

License: Same as Nmap -- See https://nmap.org/book/man-legal.html

action

action (host, port)

MAIN ACTION

Parameters

host
 
port