aboutsummaryrefslogtreecommitdiffstats

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.