Skip to content

[SECURITY] OOB Heap Read in CF_CFDP_CopyStringFromLV via Unvalidated PDU LV Length Field — Remotely Triggerable via Crafted Metadata PDU #491

Description

@linerfan5114

[SECURITY] OOB Heap Read in CF_CFDP_CopyStringFromLV via Unvalidated PDU LV Length Field — Remotely Triggerable via Crafted Metadata PDU

Describe the bug
CF_CFDP_CopyStringFromLV() in fsw/src/cf_cfdp.c performs a memcpy using the attacker-supplied src_lv->length field from an incoming CFDP PDU without validating that the claimed length does not exceed the actual data present in the decode buffer. The only guard check (src_lv->length < buf_maxsz) compares against the destination buffer size, not against the remaining source PDU data. A remote attacker can send a specially crafted CFDP Metadata PDU containing an LV (Length-Value) field where the length octet claims a size far larger than the actual encoded payload — e.g., length = 0xFF (255) with only 3 bytes of filename data following it. The memcpy will then read ~252 bytes past the end of the allocated PDU buffer, disclosing adjacent heap memory contents into the destination string buffer.

This is the same class of vulnerability as:

  • CVE-2026-5474 — TO_LAB CCSDS length OOB read
  • cFE #2698 — SB transmit path missing lower-bound validation on CCSDS Length

Root Cause
In CF_CFDP_CopyStringFromLV(), the only validation is:

if (src_lv->length < buf_maxsz)  // checks DESTINATION size, not SOURCE availability
{
    memcpy(buf, src_lv->data_ptr, src_lv->length);  // OOB read when length > actual PDU data
}

The function lacks access to the codec state (remaining PDU buffer size), so it cannot validate src_lv->length against the actual source data bounds. This is a systemic issue: the CFDP decoder fills CF_Logical_Lv_t fields based on attacker-controlled PDU bytes without any cross-validation that the claimed LV payload fits within the received PDU.

To Reproduce

  1. Establish a CFDP channel. No authentication is required on default NOS3 deployments.
  2. Craft a CFDP Metadata PDU (PDU type=0, directive code=0x00 for Metadata) with:
    • Valid source/destination EID matching the local CF entity
    • Valid transaction sequence number
    • Source filename LV: length = 0xFF (255), actual data = 3 bytes
    • Destination filename LV: length = 0xFF (255), actual data = 3 bytes
  3. Send the PDU to the CF input MID via UDP (e.g., via CI_LAB passthrough)
  4. CF_CFDP_ReceivePdu()CF_CFDP_RecvPh()CF_CFDP_RecvMd()CF_CFDP_CopyStringFromLV()
  5. memcpy reads from src_lv->data_ptr for src_lv->length bytes, far beyond the actual 3 bytes of filename data, into adjacent heap memory
  6. The excess heap data ends up in txn->history->fnames.src_filename and can be exfiltrated via subsequent CFDP ACK/MD PDUs or EOT telemetry

Expected behavior
CF_CFDP_CopyStringFromLV() must validate src_lv->length against the actual remaining data in the PDU decode buffer before performing memcpy. If insufficient data remains, the function must return CF_ERROR without copying.

Proposed Fix
The function signature needs access to the codec state to know how much source data remains. Two approaches:

Approach A (Minimal — pass remaining PDU size):
Modify CF_CFDP_CopyStringFromLV() to accept an additional remaining_pdu_bytes parameter from the caller, and validate:

int CF_CFDP_CopyStringFromLV(char *buf, size_t buf_maxsz, const CF_Logical_Lv_t *src_lv, size_t remaining_pdu_bytes)
{
    if (src_lv->data_ptr != NULL 
        && src_lv->length <= remaining_pdu_bytes    // ← validate against actual PDU data available
        && src_lv->length < buf_maxsz)              // ← validate against destination buffer
    {
        memcpy(buf, src_lv->data_ptr, src_lv->length);
        buf[src_lv->length] = 0;
        return src_lv->length;
    }
    buf[0] = 0;
    return CF_ERROR;
}

Approach B (Robust — validate at decode time):
Validate the LV length during CF_CFDP_DecodeMd() (or wherever the LV is parsed from the PDU) while the codec state still knows the remaining buffer size. If the LV claims more bytes than available, set a decode error immediately rather than deferring the check.

Call sites that need updating:

  • CF_CFDP_RecvMd() — passes md->source_filename and md->dest_filename to CF_CFDP_CopyStringFromLV()

Code snips
File: fsw/src/cf_cfdp.c, lines near 2209-2242:

int CF_CFDP_CopyStringFromLV(char *buf, size_t buf_maxsz, const CF_Logical_Lv_t *src_lv)
{
    if (src_lv->length < buf_maxsz)                    // BUG: only checks destination
    {
        memcpy(buf, src_lv->data_ptr, src_lv->length); // BUG: no source bounds check
        buf[src_lv->length] = 0;
        return src_lv->length;
    }
    buf[0] = 0;
    return CF_ERROR;
}

System observed on

  • Hardware: x86-64 (POSIX cFS / NOS3 simulator)
  • OS: Linux 6.8.0 (Ubuntu 24.04)
  • Versions: CF bundle @ latest commit (Draco v7.0.0), CFE v7.0.0-rc4+dev370

Additional context

  • Remotely triggerable via any CFDP Metadata PDU. No prior authentication or handshake required.
  • The leaked heap region typically contains PDU buffer descriptors, transaction history structures (including peer EIDs, sequence numbers, and filenames from previous transfers), and potentially libc/mmap pointers.
  • After the OOB read into the filename buffer, the data may be sent back in subsequent ACK PDUs or End-of-Transaction telemetry — providing an exfiltration path.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions