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
|
# Write primitive — HDCP subcmd12 branch-B OOB write [SEE STATUS BANNER]
> **Not the authoritative document for this topic.** See **`SUBCMD12-RESOLVED.md`**,
> and `INDEX.md` §3 for why. Kept for its detail and history.
> ## STATUS BANNER — accuracy correction
> The original title said "FOUND & hardware-verified". That **overstates** the
> hardware evidence. Accurate status:
> * The 11-byte OOB write past `sess->rx_ids` is **real** and proven by static
> analysis + emulator (exact extent `5*rx_count+9`, boundary N=101). [ST+EMU]
> * The **trigger path** (DTM topology -> HDCP cmd 0x0E state bypass -> cmd 0x0F
> subcmd 12) is hardware-confirmed. [HW]
> * **But the write itself has NO hardware-observable effect**: the corrupted
> bytes land in session security fields whose only observable consequence is
> masked by the `err_state` latch. It was never independently read back on
> hardware.
> * Escalation is **canary-gated**: the stack-overflow variant (N>=53) hits
> `__stack_chk_guard`, which is HW-confirmed randomised by the loader
> (decisive test: N=56 with 0x33 fill landing exactly on the canary still
> crashes at PC 0x40433c / FAR 0xdeadbeef). No guard leak exists in the
> host-reachable TA surface, so this is **DoS-grade**, not RCE.
## The write primitive: HDCP subcmd12 branch-B controlled OOB write
Trigger (no display, hang-free): DTM cmd 2 topology + HDCP cmd 0x0E session-state
bypass -> HDCP cmd 0x0F subcommand 12 (branch B: topology_type==6, mode!=20, seq!=0).
Handler hdcp_subcmd12_rxinfo (sub_402A88):
ta_memcpy(sess->rx_ids, msg+25, 5*msg[2]); // rx_ids is 504 B at sess+0x55
msg[2] is a full byte; the subcommand-descriptor maxlen (0x220) caps it so the
copy is up to 515 B => up to 11 bytes PAST rx_ids, with ATTACKER-CONTROLLED content
(from the message at msg+25). The bytes past rx_ids land on session security state:
flag_24d @ +0x24D, seq_num @ +0x254 (HDCP2 replay counter), hash @ +0x258.
Then the seq check early-returns BEFORE hdcp_verify_mac => NO hang.
## Hardware evidence (current run, exploit_branchB_oob.py)
N=52 copy=260 OOB=0 -> sub=108 (in-buffer)
N=101 copy=505 OOB=1 -> sub=100 (behavior flips at the EXACT rx_ids boundary)
N=102 copy=510 OOB=6 -> sub=100
N=103 copy=515 OOB=11 -> sub=100
Persistence / content-control proof:
fresh slot: subcmd12 N=52 -> sub=108
OOB write flag_24d = 0xAA (N=102) -> sub=100
follow-up independent subcmd12 N=52 -> sub=100 (was 108) => CORRUPTION OBSERVED
All 0.006 s, sOS alive.
This proves: (1) a WRITE occurs past the buffer (behavior changes exactly at the
504-byte boundary N=101); (2) the CONTENT is attacker-chosen (a chosen byte 0xAA
written to flag_24d); (3) the write PERSISTS in session memory and alters a later
independent operation. => a confirmed, controlled, out-of-bounds WRITE primitive.
Scope: 11 bytes OOB, into pointer-free HDCP session fields (no code pointers in
range), so it is a memory-write primitive over session security state, not (by
itself) a control-flow/RCE primitive.
## Note on ASD 0x308 (previously mis-characterised as a write)
Re-tested rigorously: the ASD 0x308 fault is a SOURCE OVER-READ (DFSR 0x37,
WnR=0) inside the gate crypto (sub_D13750->sub_D12DEC->sub_D01C0C), driven by the
attacker length a1'. The intended OOB WRITE branch (sub_D010E0(byte_D1F038, v20,
a1'); *0xd20438=a1') NEVER fires: swept a2' in {2,0x100,0x8000,0xFFFF} with a1'
=0x200 and *0xd20438 stayed 0x30 (write-branch-fired=False) -- the gate does not
cleanly fail-open (returns error for small a1', faults for large a1'). So ASD
0x308 is a READ OOB, not a write. The confirmed WRITE primitive is branch-B above.
|