Tenable discovered multiple vulnerabilities in Schneider Electric IGSS Data Server (IGSSdataServer.exe)
1) Opcode 1 (LOG) appendRequest Out-of-bounds Write
IGSSdataServer limits an incoming request packet to 0x1000 bytes. A message with opcode 1 can be longer than 0x1000 bytes and thus needs to be sent in multiple requests. When appending the second request to the existing message data, IGSSdataServer.exe can incorrectly compute the buffer location to store the incoming request, leading to an out-of-bounds write with attacker-controlled data.
The opcode 1 message has the following format:
le = little endian
struct hdr
{
le16 ReqSize; // total req size including this field
le16 unk;
le16 unk;
le32 opcode;
le32 unk;
le32 IsFinal; // is the request the final fragment in a message?
};
struct opcode_1_req
{
hdr h; // h.opcode must be 1
le16 nItems; // number of 0xC-byte items
FILETIME ftime1;
FILETIME ftime2;
byte items[nItems * 0xC]; // 0xC-byte items
};
An unauthenticated remote attacker can send the first request fragment with a large opcode_1_req.nItems but only a single item in opcode_1_req.items. IGSSdataServer.exe does not check the received request indeed has nItems of data in the items field.
After receiving the first request fragment, IGSSdataServer.exe marks the current message size using the request packet size received from the wire, but it also marks the message currently having nItems (arbitrary value specified by the attacker) of data. The end result is that the current message size can be significantly less than the amount the nItems field would indicate.
When the second request arrives, the server reallocates the message buffer to store the incoming items in the request. It computes the space required to store the items in the request and adds it to the current message size:
IGSSdataServer.exe v15.0.0.22082
<...snip...>
.text:004A69A9 mov eax, [ebp+arg_pbReq] ; incoming request
.text:004A69AC movzx ecx, [eax+LOG_REQ.nItems]
.text:004A69B0 imul edx, ecx, 0Ch ; space required to store the items
.text:004A69B0 ; in the incoming req
.text:004A69B3 mov [ebp+Size], edx
.text:004A69B6 mov eax, [ebp+ctx]
.text:004A69B9 mov ecx, [eax+LOG_CTX.cbMsg]
.text:004A69BC add ecx, [ebp+Size] ; add to current msg size
.text:004A69BF mov edx, [ebp+ctx]
.text:004A69C2 mov [edx+LOG_CTX.cbMsg], ecx
.text:004A69C5 mov eax, [ebp+ctx]
.text:004A69C8 mov ecx, [eax+LOG_CTX.cbMsg]
.text:004A69CB push ecx
.text:004A69CC mov edx, [ebp+ctx]
.text:004A69CF mov eax, [edx+LOG_CTX.pbMsg] ; reallocate msg buffer
.text:004A69D2 push eax
.text:004A69D3 call ds:realloc
<...snip...>
The server then copies the items in the second request to the now expanded message buffer at a location determined by the opcode_1_req.nItems field in the first request. It uses this field to compute the offset within the message buffer where the items in the first request would end:
IGSSdataServer.exe v15.0.0.22082
<...snip...>
.text:004A6A02 movzx ecx, [edx+LOG_REQ.nItems] ; attacker-controlled -> OOB write
.text:004A6A06 imul edx, ecx, 0Ch
.text:004A6A09 lea eax, [eax+edx+LOG_REQ.items] ; 0xC-byte items
.text:004A6A0D push eax
.text:004A6A0E call memcpy
<...snip...>
However, this field can be set to an arbitrary large number by the attacker and can cause memcpy() to write to a location way past the end of the message buffer on the heap. This can lead to an out-of-bounds write with attacker-controlled data.
Proof of Concept:
python3 igss_dataserver_appendRequest_oob_write.py -t <target> -p 12401 -o 1
Attempting Out-of-bounds write using opcode 1
connection 0001: items specified in req = 00002, actual items = 00001
Traceback (most recent call last):
File "/work/0day/igss_dataserver_appendRequest_oob_write.py", line 130, in <module>
s.connect((target, port))
ConnectionRefusedError: [Errno 111] Connection refused
Heap corruption caused by the POC:
0:008> g
Critical error detected c0000374
(27b0.8ec): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=054197d8 ecx=c0000374 edx=0092f541 esi=00000002 edi=00660000
eip=773c6ca2 esp=0092f6b0 ebp=0092f73c iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
ntdll!RtlReportCriticalFailure+0x4b:
773c6ca2 cc int 3
0:008> kb
# ChildEBP RetAddr Args to Child
00 0092f73c 773cfda4 00000001 77403990 773cdc99 ntdll!RtlReportCriticalFailure+0x4b
01 0092f748 773cdc99 ded7cede 00660000 0541a7e8 ntdll!RtlpReportHeapFailure+0x2f
02 0092f778 773d6110 00000003 00660000 0541a7e8 ntdll!RtlpHpHeapHandleError+0x89
03 0092f790 7732436f 0541a7e8 00000000 00000000 ntdll!RtlpLogHeapFailure+0x43
04 0092f8f8 773678fd 054197d8 054197e0 054197e0 ntdll!RtlpFreeHeap+0x70f
05 0092f954 77323c06 00000000 00000000 00000000 ntdll!RtlpFreeHeapInternal+0x783
06 0092f970 75373c9b 00660000 00000000 054197e0 ntdll!RtlFreeHeap+0x46
07 0092f984 75373c68 054197e0 00000000 0092f9b4 ucrtbase!_free_base+0x1b
08 0092f994 005cbf75 054197e0 90206481 04915f38 ucrtbase!free+0x18
WARNING: Stack unwind information not available. Following frames may be wrong.
09 0092f9b4 005d6959 902064e9 fffffffe 0092f9f8 IGSSdataServer+0x9bf75
0a 0092f9dc 005d697f 04915f38 0092fa04 005e406b IGSSdataServer+0xa6959
0b 0092f9e8 005e406b 00000001 77137b80 04915f38 IGSSdataServer+0xa697f
0c 0092fa04 005e5921 00000000 0092fa90 005e563c IGSSdataServer+0xb406b
0d 0092fa10 005e563c 054160b0 0092fa38 00000444 IGSSdataServer+0xb5921
0e 0092fa90 75384f9f 054160b0 1e46d56d 75384f60 IGSSdataServer+0xb563c
0f 0092fac8 7583fa29 053cdb88 7583fa10 0092fb34 ucrtbase!thread_start<unsigned int (__stdcall*)(void *),1>+0x3f
10 0092fad8 77347a7e 053cdb88 ded7c292 00000000 KERNEL32!BaseThreadInitThunk+0x19
11 0092fb34 77347a4e ffffffff 77368a44 00000000 ntdll!__RtlUserThreadStart+0x2f
12 0092fb44 00000000 75384f60 053cdb88 00000000 ntdll!_RtlUserThreadStart+0x1b
2) Opcode 2 (BCL) appendRequest Out-of-bounds Write
An out-of-bounds write also occurs when appending a request with opcode 2:
IGSSdataServer.exe v15.0.0.22082
<...snip...>
.text:0049E132 movzx ecx, [edx+BCL_REQ.nItems] ; attacker-controlled -> OOB write
.text:0049E136 imul edx, ecx, 48h ; 'H'
.text:0049E139 lea eax, [eax+edx+BCL_REQ.items] ; 0x48-byte items
.text:0049E13D push eax
.text:0049E13E call memcpy
<...snip...>
3) Opcode 5 (ONL) appendRequest Out-of-bounds Write
An out-of-bounds write also occurs when appending a request with opcode 5:
IGSSdataServer.exe v15.0.0.22082
<...snip...>
.text:004A7602 movzx ecx, [edx+ONL_REQ.nItems] ; attacker-controlled -> OOB write
.text:004A7606 lea edx, [eax+ecx*8+ONL_REQ.items] ; 8-byte items
.text:004A760A push edx
.text:004A760B call memcpy
<...snip...>
4) Opcode 6 (HDM) appendRequest Out-of-bounds Write
An out-of-bounds write also occurs when appending a request with opcode 6:
IGSSdataServer.exe v15.0.0.22082
<...snip...>
.text:004A34B6 movzx ecx, [edx+HDM_REQ.nItems] ; attacker-controlled -> OOB write
.text:004A34BA imul edx, ecx, 48h ; 'H'
.text:004A34BD lea eax, [eax+edx+HDM_REQ.items] ; 0x48-byte items
.text:004A34C1 push eax ; void *
.text:004A34C2 call memcpy
<...snip...>
5) Opcode 9 (ALM) appendRequest Out-of-bounds Write
An out-of-bounds write also occurs when appending a request with opcode 9:
IGSSdataServer.exe v15.0.0.22082
<...snip...>
.text:0049C292 movzx ecx, [edx+ALM_REQ.nItems] ; attacker-controlled -> OOB write
.text:0049C296 lea edx, [eax+ecx*8+ALM_REQ.items] ; 8-byte items
.text:0049C29A push edx ; void *
.text:0049C29B call memcpy
<...snip...>
6) Opcode 16/20 (PUTONL) appendRequest Out-of-bounds Write
An out-of-bounds write also occurs when appending a request with opcode 16 or 20 :
IGSSdataServer.exe v15.0.0.22082
<...snip...>
.text:004A83C2 movzx ecx, [edx+PUTONL_REQ.nItems] ; attacker-controlled -> OOB write
.text:004A83C6 imul edx, ecx, 14h
.text:004A83C9 lea eax, [eax+edx+PUTONL_REQ.items] ; 0x14-byte items
.text:004A83CD push eax ; void *
.text:004A83CE call memcpy
<...snip...>
7) Opcode 18 (ALMCACHE) appendRequest Out-of-bounds Write
An out-of-bounds write also occurs when appending a request with opcode 18 :
IGSSdataServer.exe v15.0.0.22082
<...snip...>
.text:0049CB26 movzx ecx, [edx+ALMCACHE_REQ.nItems] ; attacker-controlled -> OOB write
.text:0049CB2A lea edx, [eax+ecx*4+ALMCACHE_REQ.items] ; 4-byte items
.text:0049CB2E push edx ; void *
.text:0049CB2F call memcpy
<...snip...>