# !exploitable Episode Two - Enter the Matrix

OpenSSH 1.x crc32 attack detector truncates a 32-bit hash size into a 16-bit static, yielding n=0 and an arbitrary write primitive via crafted SSH1 packet.

- Payout: Research
- Severity: critical
- Program: OpenSSH
- Credited: Doyensec
- Disclosed: 2025-02-26
- Write-up quality: 8/10
- Original: https://blog.doyensec.com/2025/02/27/exploitable-sshd.html
- Library page: https://bugbountylibrary.com/r/blog%3Ablog-doyensec-com-2025-02-27-exploitable-sshd-html
- Tags: domain:network impact:rce kind:research platform:research sev:critical tech:linux technique:reverse-engineering technique:source-review vuln:memory-corruption vuln:rce

**Target:** OpenSSH SSH protocol 1 daemon (deattack.c detect_attack)
**Root cause:** The static hash size n is u_int16_t while the computed size l is u_int32_t; when len forces l to 0x10000, n truncates to 0, so the hash index mask (n-1) becomes 0xffff and attacker-controlled HASH(c) values index out of bounds.
**Steps:**
1. Send SSH1 packet with len >= 0x15560 (multiple of 8, <= 0x40000) so the l<<2 loop reaches 0x10000 and n truncates to 0.
2. Craft block contents so HASH(c) & (n-1) yields a chosen index i (e.g. 0x41414141 observed in crash).
3. Loop iterates j per 8-byte block; when h[i] reads as HASH_UNUSED, execute `h[i] = j` giving write-what-where.
4. Patch sshconnect1.c to send `packet_start(SSH_MSG_IGNORE)` with a 0x15560-0x10 buffer of 'A's to reach detect_attack on the server.
5. Observe EXC_BAD_ACCESS at `h[i] != HASH_UNUSED` with i=0x41414141 confirming controlled index.
**How found:** Author first hypothesized integer overflow in xmalloc(n*HASH_ENTRYSIZE) but realized n's restrictions prevent it; tracing the `for (l = n; l < HASH_FACTOR(...); l = l << 2)` loop revealed l reaches 0x10000 which truncates to 0 when assigned to u_int16_t n, then confirmed via lldb that n=0 and index i=0x41414141 crashes.
**Exploitation:** Attacker sends a crafted SSH1 encrypted packet whose length drives the hash-size loop to 0x10000, truncating n to 0; subsequent hash probing uses attacker-controlled indices, and the `h[i] = j` store provides a write-what-where primitive (j bounded by buffer length, i fully controlled) enabling memory corruption toward RCE.
**Escalation:** Write primitive could overwrite auth state, root password, or RSA exponent; constraints: only half of j values reachable due to 0x40000 length cap, and n=0 persists across calls causing infinite loop unless n is repaired via the write itself.
**Insight:** When a size variable is narrower than the value computed for it, check whether the truncation collapses a mask (n-1) to all-ones; a static cache size that becomes 0 turns every subsequent hash lookup into an attacker-indexed OOB access.
