Skip to content

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

@linerfan5114

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions