diff --git a/data/auth.sqlite b/data/auth.sqlite index 41ae3dc..8dfdb10 100644 Binary files a/data/auth.sqlite and b/data/auth.sqlite differ diff --git a/markbase-core/src/ssh_server/kex_complete.rs b/markbase-core/src/ssh_server/kex_complete.rs index 16a972e..2355403 100644 --- a/markbase-core/src/ssh_server/kex_complete.rs +++ b/markbase-core/src/ssh_server/kex_complete.rs @@ -71,11 +71,19 @@ impl KexState { // V_S: 服务器版本字符串(SSH string格式) write_ssh_string_to_hash(&mut hasher, &self.server_version)?; - // I_C: 客户端KEXINIT payload(SSH string格式) - write_ssh_string_to_hash(&mut hasher, &String::from_utf8_lossy(&self.client_kexinit_payload))?; + // OpenSSH kexgex.c: "kexinit messages: fake header: len+SSH2_MSG_KEXINIT" + // Remove SSH_MSG_KEXINIT type byte from payloads and prepend it in exchange hash - // I_S: 服务器KEXINIT payload(SSH string格式) - write_ssh_string_to_hash(&mut hasher, &String::from_utf8_lossy(&self.server_kexinit_payload))?; + let client_kexinit_without_type = &self.client_kexinit_payload[1..]; + let server_kexinit_without_type = &self.server_kexinit_payload[1..]; + + 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); // K_S: 服务器主机密钥blob(SSH string格式) hasher.update(server_host_key_blob); diff --git a/markbase-core/src/ssh_server/kex_exchange.rs b/markbase-core/src/ssh_server/kex_exchange.rs index a5963bf..47fe19f 100644 --- a/markbase-core/src/ssh_server/kex_exchange.rs +++ b/markbase-core/src/ssh_server/kex_exchange.rs @@ -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);