romandaripper's picture
ExecuTorch .pte/.ptd external-constant OOB read PoC (Cyfra Tech Solutions)
c5e2f40 verified
|
Raw
History Blame Contribute Delete
3.36 kB
metadata
license: apache-2.0
tags:
  - security
  - proof-of-concept
  - executorch
  - vulnerability-research

ExecuTorch .pte + .ptd external-constant heap out-of-bounds read β€” PoC

Security research artifact. Do not load these files with a vulnerable ExecuTorch build outside a sandbox.

Reported through huntr (Model File Formats / AI-ML) under coordinated disclosure by Roman Arce Bran β€” Cyfra Tech Solutions (Costa Rica).

  • Target: pytorch/executorch, main @ 55d693b1655314526d25436754d83b9554049540 (2026-07-27)
  • CWE-125 out-of-bounds read β†’ CWE-200 information disclosure, plus a remote worker crash
  • Entry point: the documented public API executorch::extension::Module(model_path, {data_files...})

The defect in one line

The .ptd declares the tensor's size twice and independently β€” once as NamedData.tensor_layout (scalar_type/sizes/dim_order β†’ nbytes()) and once as DataSegment.size (the bytes actually allocated and read). The runtime never compares them, so a 4 KiB tensor can be backed by a 16-byte heap allocation.

getTensorDataPtr() (runtime/executor/tensor_parser_exec_aten.cpp:194) receives the correct byte count as a parameter and uses it in every branch except the external-constant one (lines 221-229), which returns data->buffer.data() unchecked.

Files

file what it is
poc/poc_leak.{pte,ptd} 4 KiB declared over a 16-byte segment β€” silent 4080-byte over-read
poc/poc_leak16k.*, poc/poc_leak32k.*, poc/poc_leak64k.* larger exfiltration windows
poc/poc_big.{pte,ptd} 4 MiB declared β€” walks off the heap, SIGSEGV (denial of service)
poc/poc_tiny.{pte,ptd} 256 B declared β€” minimal ASAN witness
poc/poc_nokernel.{pte,ptd} empty instruction list β€” no kernel runs at all; the runtime still hands the caller a Tensor with nbytes()=256 over a 16-byte allocation
gen_poc.py builds every pair from the upstream schemas with upstream flatc β€” these are well-formed files, not fuzzer garbage
poc_main.cpp, build.sh standalone driver (ASAN, -O2 -DNDEBUG)
server_main.cpp, build_server.sh minimal model-serving service: loads an uploaded model over TCP and returns the output tensor
attacker_client.py remote client β€” separate process, socket only, no access to the server's memory
run_exfil_suite.sh, sweep_window.sh, aslr_analysis.py end-to-end exfiltration + measured ASLR-defeat harness
ASAN-witness.txt, EXFIL-witness.txt captured evidence

Measured impact

  • 4080 β†’ 65520 bytes of server heap delivered to the remote party that uploaded the model (the attacker picks the size)
  • 228 verbatim copies of another tenant's session token in a single 32 KiB response, 5/5 runs
  • ASLR: across 5 fresh victim processes, 2148/2148 base predictions matched the victim's real /proc/self/maps exactly β€” both the [heap] base and the PIE image base β€” from one 864-byte model
  • 4 MiB window β†’ SIGSEGV (exit 139), reproducible remote DoS of the worker

Reproduce

python3 gen_poc.py ./poc
./build_server.sh
./sweep_window.sh
POC=leak32k ./run_exfil_suite.sh 5
python3 aslr_analysis.py /tmp/cyfra_exfil 5

Access to this repository is gated and granted to protectai-bot for huntr triage.