Skip to content

Heap Buffer Overflow leading to RCE in `AIS::Message`

Critical
jvde-github published GHSA-v53x-f5hh-g2g6 Nov 27, 2025

Package

AIS-catcher (C++ / Marine Navigation)

Affected versions

v 0.63<

Patched versions

None

Description

Summary

A critical Heap Buffer Overflow vulnerability has been identified in the AIS::Message class of AIS-catcher. This vulnerability allows an attacker to write approximately 1KB of arbitrary data into a 128-byte buffer.

We have successfully demonstrated Remote Code Execution (RCE) by exploiting this vulnerability. By overwriting adjacent C++ objects (specifically std::vector pointers), we achieved an Arbitrary Read/Write primitive, which was then used to hijack control flow and execute arbitrary commands.

Technical Details

1. The Root Cause: Byte vs. Bit Confusion

The vulnerability stems from a logical error in the bounds checking mechanism within Source/Marine/Message.cpp.

The AIS::Message class defines a fixed-size buffer:

// Source/Marine/Message.h
#define MAX_AIS_LENGTH (128 * 8) // 1024 bits
uint8_t data[128];               // 128 bytes

However, the setUint method incorrectly compares the length in bytes against the maximum length in bits:

// Source/Marine/Message.cpp
bool Message::setUint(int start, int len, unsigned val)
{
    // VULNERABILITY:
    // 'length >> 3' is the current length in BYTES.
    // 'MAX_AIS_LENGTH' is 1024 (BITS).
    // The check allows 'length' to reach 1024 BYTES before stopping.
    // The buffer 'data' is only 128 BYTES.
    if (length >> 3 >= MAX_AIS_LENGTH)
        return false;

    // ... write operation proceeds, overflowing 'data' by up to 896 bytes ...
}

2. Exploitation Path: From Overflow to RCE

The AIS::Message object layout in memory typically places the data buffer adjacent to other critical members, such as std::vector<std::string> NMEA.

Memory Layout:

[ data (128 bytes) ] [ padding ] [ NMEA vector (24 bytes) ] ...

Attack Chain:

  1. Overflow: We use setUint to write past the 128-byte data buffer.
  2. Object Corruption: We overwrite the internal pointers of the NMEA vector (_M_start, _M_finish, _M_end_of_storage).
  3. Arbitrary Read/Write: By controlling the vector's _M_start and _M_finish pointers, we can trick the program into reading from or writing to any memory address when it accesses NMEA elements.
  4. RCE: We use this arbitrary write primitive to overwrite a function return address on the stack (or a GOT entry), redirecting execution to our shellcode or a ROP chain.

PoC (Proof of Concept)

We have developed a PoC that confirms the crash and the ability to overwrite memory.

1. Crash Verification (ASan)
This simple PoC triggers the overflow, causing AddressSanitizer to report a heap-buffer-overflow.

// poc.cpp
#include "Message.h"
int main() {
    AIS::Message msg;
    msg.clear();
    // Write to bit offset 2000 (byte 250), far beyond the 128-byte limit
    msg.setUint(2000, 32, 0xDEADBEEF);
    return 0;
}

2. RCE Capability
In our internal testing, we successfully exploited this to execute arbitrary system commands (e.g., popping a calculator or shell) on a Linux environment by overwriting the return address.

Impact

  • Remote Code Execution (RCE): Confirmed. An attacker can execute arbitrary code with the privileges of the AIS-catcher process.
  • Denial of Service (DoS): Trivial to crash the service by sending oversized packets.

Severity

Critical

CVE ID

CVE-2025-66216

Weaknesses

Incorrect Calculation of Buffer Size

The product does not correctly calculate the size to be used when allocating a buffer, which could lead to a buffer overflow. Learn more on MITRE.

Credits