Sploitus

Exploit for Classic Buffer Overflow in Cisco Adaptive Security Appliance Software

githubexploit Β· 2026-08-22

Exploit Code

README66 lines
## https://sploitus.com/exploit?id=89EF59E9-C994-5617-9C60-64831E998DC3
# LINA_FULL_RE β€” Full-Coverage Reverse Engineering of `lina`

**Created:** 2026-08-12
**Target:** `lab_asav9-12-4-67/lina` (ASAv 9.12(4)67, ELF x86-64, PIE; Ghidra image base `0x100000`; **112,687 functions**, ~167 MB mapped, 642,707 symbols)
**Track goal:** systematic, *complete* RE coverage of the `lina` binary β€” complementing the hypothesis-driven targeted RE done so far β€” to hunt the primitive the lab has not found (wild unauth root RCE on 20362+20333 is proven, so it exists).

---

## Why this track exists

All prior RE was **targeted** (fault-PC decompiles, string-anchor xref walks, binding-range sweeps). It closed every audited scope (H4-1/2/3/4, `tostring(userdata)` reflection, SSH/SNMP/IKE `%p`, alt overflow families, survival oracle) β€” see `../RCE_BLOCKERS.md`, `../NEW_ROADS_TO_CODE_EXECUTION.md`. Those closures are per-scope; the **un-audited remainder** of a 112k-function binary is exactly where the APT's missing primitive must live. This track replaces "guess the next surface" with **coverage**.

**Position vs critical path:** the known chain's hard gate remains **P0 (live remote infoleak)**; the CIFS readdir over-read (Step 19i, 4-byte window) is the only empirically-confirmed leak and stays the foreground empirical track. LINA_FULL_RE is the systematic hedge: find a *better* leak/primitive in unaudited code, or prove none exists.

## First triage targets (user-selected 2026-08-12)

1. **Leak sinks** β€” any network-reachable path that can emit a `0x55..` (lina) or `0x7f..` (libc/heap) qword: format-string emitters (`%p`/`%lx`/`%x` on pointers), over-reads (length-not-bounded-by-initialized-data), reflection of heap residue, uninitialized-buffer echoes.
2. **Overflow sinks** β€” every copy with attacker-influenced length not bounded by destination capacity (`memcpy`/`memmove`/`strcpy`/`strcat`/`sprintf`/`vsprintf`/`gets`-class + custom copy loops), across **all** protocol surfaces (not just the WebVPN Lua bindings already exhausted β€” see `../LINA_ALT_OVERFLOW_FAMILIES_RE.md`).
3. **Call-graph reachability** β€” forward call-graphs from all external entry points (WebVPN/HTTPS dispatchers, SSH, SNMP, IKE/ISAKMP, DTLS, ASDM, failover `csco_config`) so triage hits can be ranked by *actually reachable from the wire*.

(Deferred by user: CVE-2025-20363 sink hunt β€” OSINT says it *consumes* a leak, sibling of 20333; see `../CVE-2025-20363_OSINT.md`.)

## Method β€” the funnel (not a flat 112k read)

| Stage | What | Tooling | Output |
|-------|------|---------|--------|
| **1. Corpus** | Batch-decompile **all 112,687 functions** to disk, one `.c` per function, sharded by address | Ghidra `run_script_inline` (Java, DecompInterface), chunked + resumable | `corpus//.c` |
| **2. Mechanical triage** | ripgrep the corpus for sink patterns (leak + overflow classes below) | local scripts, `triage/` | hit tables (CSV/MD) |
| **3. Reachability filter** | keep hits in functions reachable from network entry points | Ghidra call-graph (`get_function_call_graph`/script) + entry-point seed list | ranked hit list |
| **4. Deep-dive** | targeted RE of ranked hits (the existing ghidra-re subagent pattern) | ghidra-mcp | findings docs in this folder |

Stage 1 is pure compute (est. hours–days, unattended, resumable). Stages 2–3 are cheap/local. Stage 4 is the only human/LLM-heavy stage and is bounded by the funnel.

## Folder layout

```
Research notes/LINA_FULL_RE/
  README.md            β€” this file (master plan + status)
  TRIAGE.md            β€” sink-pattern catalog + reachability plan (stage 2/3 spec)
  scripts/             β€” decompile + triage + driver scripts
  corpus/              β€” stage-1 output: decompiled functions, sharded (corpus/2c/02c6cc80.c)
    _progress.txt      β€” resumable progress marker (done count, last address)
    _errors.txt        β€” per-function decompile failures
  triage/              β€” stage-2/3 outputs (hit tables, ranked lists)
  findings/            β€” stage-4 deep-dive writeups (one MD per confirmed candidate)
```

## Status

| Stage | Status | Notes |
|-------|--------|-------|
| 1. Corpus | **DONE 2026-08-12** (~19:03 UTC-4) | **111,997/111,997 functions, 0 errors**, 548 MB in `corpus/`; took 26,214 s (~7.3 h) at 4.3 fn/s via `scripts/decompile_all.py` (GhidraMCP `batch_decompile`, 20 fn/call cap) |
| 2. Triage | **DONE 2026-08-13** | `scripts/triage_stage2.py`; per-class hit tables in `triage/*_hits.csv`; ranked join in `triage/RANKED.md` |
| 3. Reachability | **DONE 2026-08-13** (re-run, fixed seeds) | corpus call+ref graph (427,198 edges); **26,291/111,997 reachable (23.5%)** β€” preauth 26,278 / admin 13 / post20362 0. Seeds corrected: registrar `0317b4e0`, session `0317c0a0` (old `030ab4e0`/`030ac0a0` were not function starts). Still a lower bound (vtables). |
| 4. Deep-dive | **IN PROGRESS 2026-08-17** | **L1 CLOSED.** **L2 C-path GO (static):** WebVPN UCTE `create_type3_message` copies attacker Type-2 TargetInfo with no clamp; leak rides Type-3 `Authorization: NTLM` back to the attacker HTTP server ([findings/L2_NTLM_C_PATH_TYPE3.md](findings/L2_NTLM_C_PATH_TYPE3.md)). Lua `ntlm.parseType2` is the dormant twin. **O1/O5 protocol pass CLOSED (static):** IKE/SSH/SNMP/EWS/PDTS copies are dest-sized, send-side, or already-known (`031af6d0` / `02ff2f30`). **O1 β€œother” 725 CLOSED (static):** stack/global/`param+const` filter β†’ no new dest-overflow GO; `015a8b30` is an unclamped addrtype helper whose callers pass AF size 4/`0x10` only ([findings/O1_OTHER_SWEEP.md](findings/O1_OTHER_SWEEP.md)). **O2 CLOSED (static):** 605 reachable copy-loops; no new dest-overflow GO. `FUN_02fd7270` = `ucte_ntlm_authenticate_connection` β†’ same `create_type3_message` as the L2 C-path GO ([findings/O2_SWEEP.md](findings/O2_SWEEP.md)). Next: lab NTLM probe (post-20362) β€” beats CIFS on window if a `0x7f..`/`0x55..` qword shows up. Optional static: O3/O4. |

**Resume/control:** the driver skips functions whose output file already exists β€” re-run `python3 scripts/decompile_all.py` at any time to continue. Graceful stop: `touch corpus/STOP`. If Ghidra or the MCP plugin restarts, just re-run the driver.

**Plugin constraints discovered (2026-08-12):** `run_script_inline` is disabled (`GHIDRA_MCP_ALLOW_SCRIPTS` unset) β†’ corpus uses the HTTP batch endpoints only; `batch_decompile` takes `?functions=0x..,0x..,..` as a **GET query parameter** (not JSON body) and silently truncates responses to 20 functions β†’ driver chunk size = 20.

## Conventions

- All addresses in docs/scripts are **lina RVAs** (linked base 0). Corpus filenames are **RVAs** exactly as returned by `list_functions` (verified: `corpus/02/02c6cc80.c` = `luaH_get` = RVA `0x2c6cc80`). Note: the GhidraMCP HTTP plugin reports RVA-style addresses, *not* Ghidra VA = RVA + `0x100000`.
- Do not contradict existing closures without new evidence; cite `../RCE_BLOCKERS.md` / `../TODO.md` when a "new" candidate overlaps a closed route.
- Deep-dive findings must note: reachability (pre-auth / post-20362-bypass / admin), leak class (`0x55..` vs `0x7f..` vs heap), and whether it beats the CIFS readdir primitive (Step 19i) on reliability.