diff options
| author | user <user@clank> | 2026-10-08 01:00:19 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-10-08 01:00:19 +0200 |
| commit | 54792cc2d53cc7aa7cba25e7aadffffbc2299b1b (patch) | |
| tree | a32397f4c2e54e75b50aaf955102553c16ba607a | |
| parent | crypto service (service 1) == SYS_DRV: the known overflow #9 is reachable fro... (diff) | |
| download | amd-psp-re-54792cc2d53cc7aa7cba25e7aadffffbc2299b1b.tar.gz amd-psp-re-54792cc2d53cc7aa7cba25e7aadffffbc2299b1b.tar.xz amd-psp-re-54792cc2d53cc7aa7cba25e7aadffffbc2299b1b.zip | |
step 1 done: resolve the SYS_DRV overflow opcodes (0x3102 read / 0x3103 write) via IDA + exploit recipe
Decompiled sub_E105A8: *a1==0x3102 -> sub_E0B090 (read/disclosure), *a1==0x3103 -> sub_E0F364
(write/overflow, gated v14>249 with v14=*a1-0x3009=250, passes). a1 is the 0x7c-byte stack copy of
the request, so req[0]=opcode, req[8]=ptr, req[12]=len, req[0x40]=ctx -- all inside the TA-window
message. Both opcodes are in ACL range 0x3100-0x31FF (mask 0xffff0) which SYS_DRV's caps own, so the
confused-deputy svc 0x76 passes.
Confirmed the overflow from the decompile: sub_E0F364 memcpy(v3, a1, v3[10]+76) with v3[10] at offset
40 inside the copied 76 bytes and the only length check being a2>=0x44C (not vs the copy length). Two
calls: call 1 (fresh, v3[10]=0) copies 76 bytes and plants v3[10]=a1[10]=huge; call 2 copies huge+76
from the caller buffer, overflowing the 1100-byte SYS_DRV global with attacker bytes. The guard
a1[3]==v3[3] is self-defeating (v3[3] is offset 12, inside the copied region, initially 0).
Wrote the full exploit recipe (msg[0]=0x3103, msg[8]=&buf, msg[12]=0x44C, buf[0]=0, buf[3]=0,
buf[10]=huge, buf[11..]=payload; send twice). Remaining: confirm cur_tcb[0x70]==0, demonstrate in
the emulator (full_system boots real SYS_DRV), then the GCP-ROP chain on silicon. Updated
fw/FINDING-GCP-ROP-TO-SYSDRV-VIA-SERVICE1.md.
| -rw-r--r-- | fw/FINDING-GCP-ROP-TO-SYSDRV-VIA-SERVICE1.md | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/fw/FINDING-GCP-ROP-TO-SYSDRV-VIA-SERVICE1.md b/fw/FINDING-GCP-ROP-TO-SYSDRV-VIA-SERVICE1.md index 355f2cd..d61a9a4 100644 --- a/fw/FINDING-GCP-ROP-TO-SYSDRV-VIA-SERVICE1.md +++ b/fw/FINDING-GCP-ROP-TO-SYSDRV-VIA-SERVICE1.md @@ -86,3 +86,75 @@ and no bug was found. This path reuses a **pre-existing, documented overflow** w precondition -- TA code execution -- this session supplied, and it lands in SYS_DRV (type 1), a strictly better target than ASD (type 2) and the one that owns fuse access. This is the highest-value open lead in the escalation chain. + +## Step 1 RESOLVED (via IDA on sys_drv2.elf): the opcodes and the exact request shape + +Decompiled `sub_E105A8`. The two overflow handlers are reached by direct opcode compares on `req[0]`: + + *a1 == 0x3102 (12546) -> sub_E0B090(a1[2], a1[3], v12) READ / disclosure + *a1 == 0x3103 (12547) -> sub_E0F364(a1[2], a1[3], v12) WRITE / overflow (gated v14>249, v14=*a1-0x3009=250, passes) + +`a1` is the 0x7C-byte stack copy of the request, so all fields sit inside the copied window (and +thus inside the TA-window message): + + req[0x00] = opcode (0x3103) + req[0x08] = a1[2] = ptr -> the caller buffer (in the TA window, host-controlled) + req[0x0c] = a1[3] = len -> must be >= 0x44C + req[0x40] = a1[16] = v12 (3rd arg, context) + +Both opcodes are in ACL range 4 (`0x3100-0x31FF`, mask `0xffff0`); the `svc 0x76` in `sub_E05A24` +checks SYS_DRV's own caps (confused deputy, above), which own that range, so it passes. + +## The WRITE overflow (opcode 0x3103), confirmed from the decompile + + sub_E0F364(_DWORD *a1, unsigned a2): // a1 = caller buffer (req[8]), a2 = len (req[12]) + if (a2 < 0x44C) return err; // the ONLY length check: >= 1100, not vs the copy len + if (*a1 >= 4) return err; // a1[0] = destination entry index, 0..3 + v3 = global_E0F3AC + 1100 * (*a1); + if (a1[3] != v3[3]) return err; // guard; v3[3] is offset 12, INSIDE the copied region + sub_E0192C(v3, a1, v3[10] + 76); // memcpy(dst=v3, src=a1, len = v3[10] + 76) + v3[1] = 4; + +`v3[10]` is offset 40 of the destination global entry, i.e. inside the always-copied first 76 bytes. +Two-call exploit, content wholly from the caller buffer `a1`: + + * **Call 1** (fresh entry, `v3[10]==0`, `v3[3]==0`): set `a1[0]=index`, `a1[3]=0` (passes guard), + `a1[10]=HUGE`. Copy length = `0+76` = 76 bytes (bounded) -> writes `v3[10]=a1[10]=HUGE`, + `v3[3]=a1[3]=0`. + * **Call 2** (same index, `a1[3]=0` still matches): copy length = `HUGE+76` -> overflows the + 1100-byte global with attacker bytes from `a1`. + +## The READ disclosure (opcode 0x3102) + + sub_E0B090(a1=caller OUT buf, a2=len): + if (a2 < 0x44C) return err; + find entry v5 with v5[1]==2; // requires an entry in state 2 + sub_E0192C(a1, v5, v5[10] + 76); // copy v5[10]+76 bytes of SYS_DRV global -> caller buf + v5[1] = 3; + +If `v5[10]` has been made large, this discloses that many bytes of SYS_DRV memory into the caller +buffer (host-readable after the RPC). Reaching `v5[1]==2` needs the entry's state machine driven +there first (the write path sets state 4, not 2) -- a secondary step; the WRITE overflow is the +primary primitive. + +## Full exploit recipe (static; demonstration pending) + +From GCP ROP, issue `SVC 0xF2` with r0=1 (crypto/SYS_DRV service) and a message in the TA window: + + msg[0] = 0x00003103 + msg[8] = &caller_buf (caller_buf in [0x400000,0x800000)) + msg[12] = 0x44C (len, satisfies the only length check) + caller_buf[0] = 0 (entry index 0) + caller_buf[3] = 0 (guard, matches fresh v3[3]=0) + caller_buf[10] = 0x40000000 (call 1: becomes v3[10]; call 2: copy length) + caller_buf[11..]= overflow payload + +Send twice. Call 2 overflows SYS_DRV's 1100-byte global by ~1 GB-capable length with chosen bytes, +inside SYS_DRV (type 1), the component that owns `fuse_read_*`. + +## Remaining to demonstrate +* Confirm `cur_tcb[0x70]==0` so the dispatch gate takes the `[0x4c]` (SYS_DRV-id) arm (normal case). +* Emulator: `full_system` boots real SYS_DRV -- drive opcode 0x3103 over service 1 from a TA context + and confirm it reaches `sub_E0F364` past the ACL. +* Silicon: the GCP-ROP chain issuing the two `SVC 0xF2` calls; observe the SYS_DRV overflow + (fault signature / state change) and, via the overflow, pivot to control of SYS_DRV. |
