Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Out-of-Bound Read in /src/libiec61850/src/goose/goose_receiver.c:665:9 in parseGoosePayload #509

Open
gnbon opened this issue Jun 11, 2024 · 0 comments

Comments

@gnbon
Copy link

gnbon commented Jun 11, 2024

Summary:

An Out-of-Bound Read vulnerability was discovered in the parseGooseMessage and parseGoosePayload functions of the goose_receiver.c file in the libiec61850 library through fuzzing. This vulnerability occurs when the size of the input data is 22 bytes.

Detailed Description:

At the beginning of the parseGooseMessage function, there is a check to return immediately if numbytes is less than 22.

parseGooseMessage(GooseReceiver self, uint8_t* buffer, int numbytes)
{
int bufPos;
bool subscriberFound = false;
if (numbytes < 22)
return;

However, if numbytes is 22 or larger, this check is passed, and the code proceeds to parse the contents of the buffer using bufPos.

if (buffer[bufPos++] != 0x88)
return;
if (buffer[bufPos++] != 0xb8)
return;
uint8_t srcMac[6];
memcpy(srcMac,&buffer[6],6);
uint8_t dstMac[6];
memcpy(dstMac,buffer,6);
uint16_t appId;
appId = buffer[bufPos++] * 0x100;
appId += buffer[bufPos++];
uint16_t length;
length = buffer[bufPos++] * 0x100;
length += buffer[bufPos++];
/* skip reserved fields */
bufPos += 4;

For example, if numbytes is exactly 22, bufPos can reach the exact boundary of the buffer. When bufPos becomes 22, buffer + bufPos could refer to an invalid memory location.

if (buffer[bufPos++] != 0x88)
return;
if (buffer[bufPos++] != 0xb8)
return;
uint8_t srcMac[6];
memcpy(srcMac,&buffer[6],6);
uint8_t dstMac[6];
memcpy(dstMac,buffer,6);
uint16_t appId;
appId = buffer[bufPos++] * 0x100;
appId += buffer[bufPos++];
uint16_t length;
length = buffer[bufPos++] * 0x100;
length += buffer[bufPos++];
/* skip reserved fields */
bufPos += 4;

At the end of parseGooseMessage, the memory is passed to parseGoosePayload with an incorrect bound.

if (subscriberFound)
parseGoosePayload(self, buffer + bufPos, apduLength);

This leads to out-of-bounds read when referencing buffer[bufPos++], causing the program to behave abnormally.

if (buffer[bufPos++] == 0x61) {

Root Cause:

The vulnerability arises due to insufficient boundary checks when the input data size is exactly 22 bytes. The code fails to properly validate and handle the case when bufPos reaches the end of the buffer, leading to out-of-bounds memory access.

Impact:

An attacker can use this vulnerability to leak data by sending deliberately crafted input data to trigger abnormal behavior in the program. This can potentially lead to information disclosure or other unintended consequences.

Recommendation:

To mitigate this vulnerability, it is crucial to perform thorough boundary checks regardless of the size of the input data. The code should be modified to ensure that bufPos never exceeds the valid range of the buffer. Additionally, proper input validation and error handling should be implemented to gracefully handle cases where the input data size is unexpected or malformed.

Proof of Concept (PoC):

A proof-of-concept exploit has been provided in the attached poc.zip file. This PoC demonstrates how the vulnerability can be triggered by sending specially crafted input data to the affected functions.

AddressSanitizer

The AddressSanitizer (ASan) log shows:

./fuzz_goose_parse poc-oobr-001.goose                                  
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 1941454991
INFO: Loaded 1 modules   (1694 inline 8-bit counters): 1694 [0x556316da7760, 0x556316da7dfe), 
INFO: Loaded 1 PC tables (1694 PCs): 1694 [0x556316da7e00,0x556316dae7e0), 
./fuzz_goose_parse: Running 1 inputs 1 time(s) each.
Running: poc-oobr-001
=================================================================
==56280==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x503000000176 at pc 0x556316d94425 bp 0x7ffca8eec5b0 sp 0x7ffca8eec5a8
READ of size 1 at 0x503000000176 thread T0
    #0 0x556316d94424 in parseGoosePayload /src/libiec61850/src/goose/goose_receiver.c:665:9
    #1 0x556316d94424 in parseGooseMessage /src/libiec61850/src/goose/goose_receiver.c:1002:9
    #2 0x556316d73e84 in LLVMFuzzerTestOneInput /src/libiec61850/build/../fuzz/fuzz_goose_parse.c:14:5
    #3 0x556316c24cd0 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:614:13
    #4 0x556316c0e814 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:327:6
    #5 0x556316c142aa in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:862:9
    #6 0x556316c412f2 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
    #7 0x7f0fed7d8d8f  (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f) (BuildId: 962015aa9d133c6cbcfb31ec300596d7f44d3348)
    #8 0x7f0fed7d8e3f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e3f) (BuildId: 962015aa9d133c6cbcfb31ec300596d7f44d3348)
    #9 0x556316c052ed in _start (/home/user/oss-fuzz/build/out/libiec61850/fuzz_goose_parse+0xa92ed)

0x503000000176 is located 0 bytes after 22-byte region [0x503000000160,0x503000000176)
allocated by thread T0 here:
    #0 0x556316d3345e in malloc /src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:69:3
    #1 0x556316c42c96 in operator new(unsigned long) cxa_noexception.cpp
    #2 0x556316c0e814 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:327:6
    #3 0x556316c142aa in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:862:9
    #4 0x556316c412f2 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
    #5 0x7f0fed7d8d8f  (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f) (BuildId: 962015aa9d133c6cbcfb31ec300596d7f44d3348)

SUMMARY: AddressSanitizer: heap-buffer-overflow /src/libiec61850/src/goose/goose_receiver.c:665:9 in parseGoosePayload
Shadow bytes around the buggy address:
  0x502ffffffe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x502fffffff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x502fffffff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x503000000000: fa fa 00 00 00 fa fa fa 00 00 00 fa fa fa 00 00
  0x503000000080: 00 fa fa fa 00 00 00 fa fa fa 00 00 00 fa fa fa
=>0x503000000100: 00 00 00 00 fa fa 00 00 06 fa fa fa 00 00[06]fa
  0x503000000180: fa fa 00 00 05 fa fa fa fa fa fa fa fa fa fa fa
  0x503000000200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x503000000280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x503000000300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x503000000380: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==56280==ABORTING

CVE Assignment Request:

I kindly request the assignment of a Common Vulnerabilities and Exposures (CVE) identifier for the Out-of-Bound Read vulnerability

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant