Ok, mình sẽ đi theo đúng trình tự handshake của SSH theo RFC (chủ yếu là RFC 4253 – The Secure Shell (SSH) Transport Layer Protocol).
Mình sẽ giải thích theo flow thực tế khi bạn gõ:
ssh user@vps
Và phân tách rõ:
SSH được chia thành 3 lớp:
Handshake chủ yếu xảy ra ở Transport Layer.
RFC 4253 section 4.2
Ngay sau khi TCP kết nối tới port 22:
Client gửi:
SSH-2.0-OpenSSH_9.6
Server gửi:
SSH-2.0-OpenSSH_8.9
Cả hai bên:
Tại thời điểm này: chưa mã hóa.
RFC 4253 section 7.1
Hai bên gửi gói:
SSH_MSG_KEXINIT
Trong đó chứa:
Ví dụ client gửi danh sách:
kex_algorithms: curve25519-sha256 diffie-hellman-group14-sha256server_host_key_algorithms: ssh-ed25519 rsa-sha2-512ciphers: aes128-ctr [email protected]
Server cũng gửi tương tự.
Hai bên chọn:
Đây gọi là algorithm negotiation.
RFC 4253 section 8
Giả sử dùng:
curve25519-sha256
Flow sẽ như sau:
SSH_MSG_KEX_ECDH_INIT
chứa:
e = client_ephemeral_public_key
SSH_MSG_KEX_ECDH_REPLY
chứa:
Client có:
Server có:
Cả hai tính:
K = ECDH(x, f) K = ECDH(y, e)
Kết quả giống nhau.
Đây là shared secret.
RFC 4253 section 8
Cả hai tạo:
H = HASH( client_version_string || server_version_string || client_kexinit_payload || server_kexinit_payload || server_host_key || e || f || K )
H rất quan trọng.
Nó được dùng để:
Server dùng private host key để ký H.
Ví dụ nếu dùng ed25519:
signature = sign(H, server_host_private_key)
Server gửi signature trong SSH_MSG_KEX_ECDH_REPLY.
Client:
Nếu verify thất bại → MITM attack → đóng kết nối.
RFC 4253 section 7.2
Sau khi có:
SSH tạo:
bằng cách hash:
key = HASH(K || H || letter || session_id)
letter là A, B, C, D để sinh các key khác nhau.
session_id = H của lần handshake đầu tiên.
Client gửi:
SSH_MSG_NEWKEYS
Server gửi lại:
SSH_MSG_NEWKEYS
Từ thời điểm này:
Toàn bộ traffic được mã hóa.
Sau khi transport layer hoàn tất.
Client gửi:
SSH_MSG_SERVICE_REQUEST
request:
ssh-userauth
Server chấp nhận.
Sau đó bắt đầu authentication.
Ví dụ public key auth:
Client gửi:
SSH_MSG_USERAUTH_REQUEST
kèm:
Signature ở đây ký:
session_id || SSH_MSG_USERAUTH_REQUEST fields
Server verify bằng public key trong authorized_keys.
Server gửi:
SSH_MSG_USERAUTH_SUCCESS
Bắt đầu Connection Protocol (RFC 4254).
Client mở channel:
SSH_MSG_CHANNEL_OPEN
ví dụ:
Quan trọng nhất là:
Nếu attacker chèn giữa: