Skip to content

RCE in cFS with cf module by .so CFDP upload and CFE_ES_START_APP no check in dl_open #955

@junfuture1103

Description

@junfuture1103

Checklist (Please check before submitting)

  • I reviewed the Contributing Guide.
  • I performed a cursory search to see if the bug report is relevant, not redundant, nor in conflict with other tickets.

Describe the bug
A full-chain Remote Code Execution (RCE) vulnerability exists in NASA cFS when deployed with the CF (CCSDS File Delivery Protocol) application. An unauthenticated attacker with network access to the CI_LAB command ingest port can achieve arbitrary code execution as the cFS process — using only UDP packets, with zero prior filesystem access.

The attack exploits three combined weaknesses:

  1. CF App: No destination path validation in CFDP file reception — An attacker can upload arbitrary files to any writable path via CFDP Metadata PDU.
  2. CFE_ES: No code signing or integrity verification on dlopen()ES_START_APP loads any .so file without signature checks.
  3. CI_LAB: No authentication on command interface — All commands are accepted via plaintext UDP.

Demonstrated Impact: Bind shell (/bin/sh) spawned on port 4444 as the cFS process, accessible from the network. Full system compromise.

POC Videos:

To Reproduce
Steps to reproduce the behavior:

  1. Clone cFS with CF App
# Clone cFS Draco release
git clone --branch v7.0.0 [https://github.com/nasa/cFS.git](https://github.com/nasa/cFS.git)
cd cFS
git submodule init && git submodule update
cp -r cfe/cmake/sample_defs/ sample_defs
cp cfe/cmake/Makefile.sample Makefile

# Clone CF app
cd apps
git clone --branch v7.0.0 [https://github.com/nasa/CF.git](https://github.com/nasa/CF.git) cf
cd ..

# Add CF to build configuration
sed -i 's/SET(cpu1_APPLIST ci_lab to_lab sch_lab)/SET(cpu1_APPLIST ci_lab to_lab sch_lab cf)/' sample_defs/targets.cmake

# Add CF to startup script
sed -i '/^!/i CFE_APP, cf, CF_AppMain, CF, 90, 16384, 0x0, 0;' sample_defs/cpu1_cfe_es_startup.scr
  1. Build cFS with CF
make SIMULATION=native prep
make
make install

cd build
make mission-cfetables
cd ..
  1. Start cFS
cd build/exe/cpu1
./core-cpu1

(Verify CF loaded successfully in the output: CF Initialized. Version 7.0.0.0 and CI_LAB listening on UDP port: 1234)

  1. Execute the exploit script (fullchain_rce.py)
    (censored)
python3 fullchain_rce.py
  1. Connect to the Bind Shell
nc 127.0.0.1 4444

Expected behavior

  • The CF application should validate destination paths in CFDP to prevent arbitrary file writes outside of designated directories.
  • ES_START_APP should verify the integrity and authenticity (e.g., via code signing) of loaded .so modules before invoking dlopen().
  • CI_LAB should implement an authentication mechanism on its command interface to prevent unauthorized packet execution.

Code snips

Root Cause #1: CFDP Destination Path Not Validated (CF App)
apps/cf/fsw/src/cf_cfdp.c
When the CF app receives a CFDP Metadata PDU, it extracts the destination filename from the PDU and stores it directly — without any path validation, directory restriction, or whitelist check.

// cf_cfdp.c — CF_CFDP_CopyStringFromLV()
int CF_CFDP_CopyStringFromLV(char *buf, size_t buf_maxsz, const CF_Logical_Lv_t *src_lv)
{
    if (src_lv->length < buf_maxsz)
    {
        memcpy(buf, src_lv->data_ptr, src_lv->length);  // ← Raw memcpy, no validation
        buf[src_lv->length] = 0;
        return src_lv->length;
    }
    buf[0] = 0;
    return CF_ERROR;
}

// cf_cfdp.c — CF_CFDP_RecvMd() (partial)
lv_ret = CF_CFDP_CopyStringFromLV(
    txn->history->fnames.dst_filename,
    sizeof(txn->history->fnames.dst_filename),
    &md->dest_filename  // ← Attacker-controlled from Metadata PDU
);

Root Cause #2: No Code Signing on Dynamic Module Loading (CFE_ES)
cfe/modules/es/fsw/src/cfe_es_task.c
The ES_START_APP command (Command Code 4) loads any .so file via dlopen() without signature verification, path whitelisting, or hash comparison.

// cfe_es_task.c — CFE_ES_StartAppCmd() (critical path)
Result = CFE_ES_AppCreate(&AppID, LocalAppName, &StartParams);
// → calls CFE_ES_LoadModule()
// → calls OS_ModuleLoad()
// → calls dlopen(filename)  ← No signature check before this

System observed on:

  • Hardware: Ubuntu Host Architecture (Tested on x86_64, Payload targets aarch64/x86_64 depending on local compiler)
  • OS: Ubuntu 24.04.4 LTS (Noble Numbat)
  • Versions:
    • cFS v7.0.0 (Draco) - Commit: b4ec679b2fd9d825610515ceb186cbfa80c60cfc
    • CF App v7.0.0 - Commit: 92baf07dc04d92cb388d899119df060126f708a0
    • cFE v7.0.0+dev0 (Included in cFS Draco)
    • OSAL v7.0.0 (Included in cFS Draco)

Additional context
Upon successful exploitation, the following Check Result outputs are observable from the target:

CF R1(23:1): successfully retained file as /cf/pwned.so
Started PWNED from /cf/pwned.so, AppID = 1114123

Screenshot of successful remote bind shell session:
image

Reporter Info
juntheworld (junhak lee)

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