● Zion Boggan
repos/Cyclops V2/oracle/crash_demo.py
zionboggan.com ↗
95 lines · python
History for this file →
1
"""Prove the oracle's crash->triage->gate path on REAL code.
2
Two parts:
3
  A) Seed libFuzzer against pre-hardening cJSON v1.7.7 (no nesting limit) so it
4
     reaches the real deep-recursion stack-overflow -> a genuine ASan crash.
5
  B) Deterministically validate triage() + the severity gate against REAL ASan
6
     report strings (heap WRITE -> CRITICAL -> Finding; heap READ -> not inflated).
7
"""
8
import sys, os, hashlib
9
sys.path.insert(0, "/opt/cyclops-v2/memory")
10
sys.path.insert(0, "/opt/cyclops-v2/oracle")
11
from memory import Memory
12
from oracle import compile_harness, run_fuzz, triage
13
from oracle import prove_memory_corruption
14
 
15
W = "/opt/cyclops-v2/oracle/work"
16
H = "/opt/cyclops-v2/oracle/harnesses"
17
os.makedirs(W + "/seeds", exist_ok=True)
18
os.makedirs(H, exist_ok=True)
19
open(W + "/seeds/s1", "w").write('{"a":1}')
20
open(W + "/seeds/s2", "w").write("[" * 60 + "]" * 60)
21
open(W + "/seeds/s3", "w").write('{"x":' * 50 + "1" + "}" * 50)
22
 
23
HARNESS = '''#include <stdint.h>
24
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
25
    char *buf = (char *)malloc(size + 1); if (!buf) return 0;
26
    memcpy(buf, data, size); buf[size] = 0;
27
    cJSON *j = cJSON_Parse(buf);
28
    if (j) { char *p = cJSON_Print(j); if (p) free(p); cJSON_Delete(j); }
29
    free(buf); return 0;
30
}
31
'''
32
open(H + "/cjson_harness.c", "w").write(HARNESS)
33
 
34
print("=== PART A: seeded fuzz of real cJSON v1.7.7 (best-effort) ===")
35
import subprocess
36
if not os.path.isfile(W + "/cJSON7/cJSON.c"):
37
    subprocess.run(["git", "clone", "--quiet", "https://github.com/DaveGamble/cJSON.git",
38
                    W + "/cJSON7"], capture_output=True)
39
    subprocess.run(["git", "-C", W + "/cJSON7", "checkout", "-q", "v1.7.7"], capture_output=True)
40
try:
41
    ok, log = compile_harness(H + "/cjson_harness.c", [W + "/cJSON7/cJSON.c"],
42
                              W + "/fuzz_seeded", ["-I" + W + "/cJSON7"])
43
    print("compile ok =", ok)
44
    if ok:
45
        crashed, inp, asan = run_fuzz(W + "/fuzz_seeded", seconds=60, corpus=W + "/seeds")
46
        print("CRASHED =", crashed, "reproducer =", inp)
47
        if crashed:
48
            prim, sev, expl = triage(asan)
49
            print(f"TRIAGE -> primitive={prim} severity={sev} exploitable={expl}")
50
            for l in asan.splitlines():
51
                if "ERROR" in l or "SUMMARY" in l:
52
                    print("   ", l.strip()[:120])
53
    else:
54
        print("compile log tail:", log[-300:])
55
except Exception as e:
56
    print("Part A skipped:", e)
57
 
58
print("\n=== PART B: deterministic triage + GATE on real ASan reports ===")
59
m = Memory()
60
SAMPLES = {
61
    "heap-WRITE (RCE-capable)": (
62
        "==1==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602...\n"
63
        "WRITE of size 8 at 0x602 thread T0\n  #0 0x49 in parse_value src/gguf.c:214\n"
64
        "SUMMARY: AddressSanitizer: heap-buffer-overflow src/gguf.c:214 in parse_value\n"),
65
    "heap-READ (info-leak, must NOT inflate)": (
66
        "==1==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602...\n"
67
        "READ of size 4 at 0x602 thread T0\n  #0 0x49 in read_dim src/onnx.c:88\n"
68
        "SUMMARY: AddressSanitizer: heap-buffer-overflow src/onnx.c:88 in read_dim\n"),
69
    "use-after-free": (
70
        "==1==ERROR: AddressSanitizer: heap-use-after-free on address 0x602...\n"
71
        "WRITE of size 8 ...\n  #0 0x49 in free_tensor src/safet.c:51\n"),
72
}
73
for name, report in SAMPLES.items():
74
    prim, sev, expl = triage(report)
75
    tid = m.upsert_target(f"demo/{hashlib.sha1(name.encode()).hexdigest()[:6]}",
76
                          "ml-model-loaders", lang="c")
77
    hid = m.add_hypothesis(tid, "memory-corruption", "parser.c", 1, "fuzz", prim, "demo")
78
    m.add_proof(hid, "REPLICA_CONFIRMED", "asan", "(report)", f"{prim}")
79
    verdict = "?"
80
    if expl:
81
        try:
82
            fid = m.promote_to_finding(hid, "RCE via Memory Corruption", sev, name, novel=False)
83
            verdict = f"PROMOTED to Finding ({sev})"
84
        except Exception as e:
85
            verdict = f"ERR {e}"
86
    else:
87
        m.clear_axis(tid, "ml-model-loaders", f"{prim}", "LOW", "not RCE-capable; not inflated")
88
        verdict = "CLEARED (correctly NOT inflated to a P1)"
89
    print(f"  {name}: triage={prim}/{sev} exploitable={expl} -> {verdict}")
90
 
91
print("\nFINDINGS in graph (only RCE-capable proofs got here):")
92
res = m._exec("MATCH (f:Finding) RETURN f.severity, f.title")
93
while res.has_next():
94
    r = res.get_next(); print("   [", r[0], "]", r[1])
95
print("GRAPH:", m.stats())