Skip to content

Heap-Buffer-Overflow in ModbusLayer::getLength() #2155

@TYGLS

Description

@TYGLS

Bug description

Describe the bug

A heap-buffer-overflow read vulnerability exists in the Modbus protocol parsing module of PcapPlusPlus. The vulnerability occurs when parsing malformed, truncated Modbus packets with insufficient payload length.

Several getter functions in ModbusLayer.cpp directly access Modbus header fields without validating the actual packet data length. A specially crafted short packet can trigger an out-of-bounds memory read, causing program crash (DoS).

File: Packet++/src/ModbusLayer.cpp

Function: ModbusLayer::getLength() (Line 45)

Cause

The standard Modbus TCP header requires at least 6 bytes to read the length field. However, the current implementation does not check m_DataLen before accessing header fields:

  • getTransactionId() requires 2 bytes
  • getProtocolId() requires 4 bytes
  • getLength() requires 6 bytes

Feeding a truncated packet shorter than 6 bytes leads to heap out-of-bounds read and ASan abort.

To Reproduce
Steps to reproduce the behavior:

  1. Clone the pcapplusplus repository and build it refer to oss-fuzz.
export CC=clang \
    CXX=clang++ \
    CFLAGS='-fsanitize=address -O0 -g' \
    CXXFLAGS='-fsanitize=address -O0 -g' \
    LIB_FUZZING_ENGINE='-fsanitize=fuzzer'
  1. Run the PoC using FuzzTarget:

poc.zip

The PoC is provided as a zip archive. After extracting, run:

./FuzzTarget ./poc

ASAN Report

~/fuzz$ ./fuzzers/FuzzTarget ./crashes/poc 
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 2210324020
./fuzzers/FuzzTarget: Running 1 inputs 1 time(s) each.
Running: ./crashes/poc
Read 0 packets successfully and 0 packets could not be read
=================================================================
==230375==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x50700000030e at pc 0x55cabead5d47 bp 0x7ffc977568d0 sp 0x7ffc977568c8
READ of size 2 at 0x50700000030e thread T0
    #0 0x55cabead5d46 in pcpp::ModbusLayer::getLength() const /home/hexijie/fuzz/project/PcapPlusPlus/Packet++/src/ModbusLayer.cpp:45:10
    #1 0x55cabead5d46 in pcpp::ModbusLayer::toString[abi:cxx11]() const /home/hexijie/fuzz/project/PcapPlusPlus/Packet++/src/ModbusLayer.cpp:98:94
    #2 0x55cabe939f70 in pcpp::Packet::toStringList(std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>>&, bool) const /home/hexijie/fuzz/project/PcapPlusPlus/Packet++/src/Packet.cpp:942:31
    #3 0x55cabe93922b in pcpp::Packet::toString[abi:cxx11](bool) const /home/hexijie/fuzz/project/PcapPlusPlus/Packet++/src/Packet.cpp:930:3
    #4 0x55cabe8696f6 in LLVMFuzzerTestOneInput /home/hexijie/fuzz/project/PcapPlusPlus/Tests/Fuzzers/FuzzTarget.cpp:60:17
    #5 0x55cabe7740c4 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/hexijie/fuzz/fuzzers/FuzzTarget+0x1160c4) (BuildId: a4a415ba67789b2d8c736acb1b373274c76440c3)
    #6 0x55cabe75d1f6 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) (/home/hexijie/fuzz/fuzzers/FuzzTarget+0xff1f6) (BuildId: a4a415ba67789b2d8c736acb1b373274c76440c3)
    #7 0x55cabe762caa in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/hexijie/fuzz/fuzzers/FuzzTarget+0x104caa) (BuildId: a4a415ba67789b2d8c736acb1b373274c76440c3)
    #8 0x55cabe78d466 in main (/home/hexijie/fuzz/fuzzers/FuzzTarget+0x12f466) (BuildId: a4a415ba67789b2d8c736acb1b373274c76440c3)
    #9 0x7f3fa422a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #10 0x7f3fa422a28a in __libc_start_main csu/../csu/libc-start.c:360:3
    #11 0x55cabe757dc4 in _start (/home/hexijie/fuzz/fuzzers/FuzzTarget+0xf9dc4) (BuildId: a4a415ba67789b2d8c736acb1b373274c76440c3)

0x50700000030e is located 0 bytes after 78-byte region [0x5070000002c0,0x50700000030e)
allocated by thread T0 here:
    #0 0x55cabe866911 in operator new[](unsigned long) (/home/hexijie/fuzz/fuzzers/FuzzTarget+0x208911) (BuildId: a4a415ba67789b2d8c736acb1b373274c76440c3)
    #1 0x55cabe876002 in std::__detail::_MakeUniq<unsigned char []>::__array std::make_unique<unsigned char []>(unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/unique_ptr.h:1085:30
    #2 0x55cabe876002 in pcpp::PcapFileReaderDevice::getNextPacket(pcpp::RawPacket&) /home/hexijie/fuzz/project/PcapPlusPlus/Pcap++/src/PcapFileDevice.cpp:475:23
    #3 0x55cabe87340a in pcpp::IFileReaderDevice::getNextPackets(pcpp::PointerVector<pcpp::RawPacket, std::default_delete<pcpp::RawPacket>>&, int) /home/hexijie/fuzz/project/PcapPlusPlus/Pcap++/src/PcapFileDevice.cpp:285:22

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/hexijie/fuzz/project/PcapPlusPlus/Packet++/src/ModbusLayer.cpp:45:10 in pcpp::ModbusLayer::getLength() const
Shadow bytes around the buggy address:
  0x507000000080: fa fa fd fd fd fd fd fd fd fd fd fd fa fa fa fa
  0x507000000100: 00 00 00 00 00 00 00 00 05 fa fa fa fa fa 00 00
  0x507000000180: 00 00 00 00 00 00 00 02 fa fa fa fa fd fd fd fd
  0x507000000200: fd fd fd fd fd fa fa fa fa fa 00 00 00 00 00 00
  0x507000000280: 00 00 07 fa fa fa fa fa 00 00 00 00 00 00 00 00
=>0x507000000300: 00[06]fa fa fa fa 00 00 00 00 00 00 00 00 05 fa
  0x507000000380: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x507000000400: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x507000000480: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x507000000500: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x507000000580: 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
==230375==ABORTING

PcapPlusPlus versions tested on

v25.05

Other PcapPlusPlus version (if applicable)

No response

Operating systems tested on

Linux

Other operation systems (if applicable)

No response

Compiler version

Ubuntu clang version 18.1.3 (1ubuntu1)

Packet capture backend (if applicable)

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions