aboutsummaryrefslogtreecommitdiffstats
path: root/psp_out/WRITE-PRIMITIVE.md
blob: a36b27ba296ac6c35c1bfb2965c8bd48df824ebc (plain) (blame)
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.