aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoruser <user@clank>2026-10-08 01:00:19 +0200
committeruser <user@clank>2026-10-08 01:00:19 +0200
commit54792cc2d53cc7aa7cba25e7aadffffbc2299b1b (patch)
treea32397f4c2e54e75b50aaf955102553c16ba607a
parentcrypto service (service 1) == SYS_DRV: the known overflow #9 is reachable fro... (diff)
downloadamd-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.md72
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.