Critical fix: KEXINIT exchange hash encoding (prepend SSH_MSG_KEXINIT byte)
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled

OpenSSH kexgex.c source code analysis:
- KEXINIT payload stored without SSH_MSG_KEXINIT type byte
- Exchange hash prepends SSH_MSG_KEXINIT byte (20) with adjusted length

Before fix:
- client_kexinit_payload included SSH_MSG_KEXINIT byte
- Direct use without prepending

After fix:
- Remove SSH_MSG_KEXINIT byte from payload
- Prepend byte (20) in exchange hash with length+1
- Both kex_exchange.rs and kex_complete.rs updated

Testing result: MAC still fails, indicating additional encoding issues
Next: Detailed comparison of all exchange hash components
This commit is contained in:
Warren
2026-06-14 23:14:14 +08:00
parent 9e4b14a2b7
commit 4778081866
3 changed files with 30 additions and 8 deletions

View File

@@ -215,11 +215,25 @@ impl KexExchangeHandler {
hasher.update(&(server_version.len() as u32).to_be_bytes());
hasher.update(server_version.as_bytes());
hasher.update(&(client_kexinit_payload.len() as u32).to_be_bytes());
hasher.update(client_kexinit_payload);
// OpenSSH kexgex.c: "kexinit messages: fake header: len+SSH2_MSG_KEXINIT"
// KEXINIT payload should NOT include SSH_MSG_KEXINIT type byte
// OpenSSH stores payload starting from cookie, prepends SSH_MSG_KEXINIT in exchange hash
hasher.update(&(server_kexinit_payload.len() as u32).to_be_bytes());
hasher.update(server_kexinit_payload);
// Remove SSH_MSG_KEXINIT type byte from payloads (our payload includes it)
let client_kexinit_without_type = &client_kexinit_payload[1..];
let server_kexinit_without_type = &server_kexinit_payload[1..];
info!("I_C (client KEXINIT without type byte): {} bytes (first byte should be cookie)", client_kexinit_without_type.len());
info!("I_S (server KEXINIT without type byte): {} bytes", server_kexinit_without_type.len());
// Exchange hash: uint32(len+1) + uint8(SSH_MSG_KEXINIT) + payload_without_type
hasher.update(&((client_kexinit_without_type.len() + 1) as u32).to_be_bytes());
hasher.update(&[20]); // SSH_MSG_KEXINIT type byte
hasher.update(client_kexinit_without_type);
hasher.update(&((server_kexinit_without_type.len() + 1) as u32).to_be_bytes());
hasher.update(&[20]); // SSH_MSG_KEXINIT type byte
hasher.update(server_kexinit_without_type);
hasher.update(&(host_key_blob.len() as u32).to_be_bytes());
hasher.update(host_key_blob);