SSH Man-in-the-Middle Attack and Public-Key Authentication Method

SSH is a protocol for secure remote login and other secure network services over insecure networks. To detect man-in-the-middle attacks SSH clients are supposed to check the host key of the server, for example by comparing it with a known good key. Should the client neglect to check the server key (or an attacker manage to steal the private key of the server) the connection becomes vulnerable to active man-in-the-middle attacks, according to RFC 4251, The Secure Shell (SSH) Protocol Architecture (section 4.1) and other resources.

The above statement is indeed true for password-based authentication. There are some tools implementing the attack, for example MITM-SSH. However, there are no tools implementing MITM against an SSH connection authenticated using public-key method (this feature is in TODO list of the above mentioned tool though). Being pressed to produce a PoC for this attack, I have attempted to implement it only to discover it is quite impossible and here is why.

During SSH connection setup the peers use Diffie-Hellman to generate encryption keys and a session ID. We assume that MITM attacker has already managed to circumvent the server host key validation and tricked the peers into establishing the connection. Effectively, there are two connections: first between the client and the attacker, and second between the attacker and the server. In case of password authentication the game is over: the attacker can see the password sent by the client, relay it to the server, and basically do whatever he or she wants.

The things are a bit more complicated in case of the public key authentication method. The client uses its private key to generate a signature and submits the signature to the server. RFC 4252, The Secure Shell (SSH) Authentication Protocol (section 7) tells us exactly how the signature is generated:

   To perform actual authentication, the client MAY then send a
   signature generated using the private key. ... The signature is sent
   using the following packet:

      byte      SSH_MSG_USERAUTH_REQUEST
      string    user name
      string    service name
      string    "publickey"
      boolean   TRUE
      string    public key algorithm name
      string    public key to be used for authentication
      string    signature

   The value of 'signature' is a signature by the corresponding private
   key over the following data, in the following order:

      string    session identifier
      byte      SSH_MSG_USERAUTH_REQUEST
      string    user name
      string    service name
      string    "publickey"
      boolean   TRUE
      string    public key algorithm name
      string    public key to be used for authentication

Now the attacker has a problem, as the client and the server have different ideas about what session identifier is supposed to be. Obviously, the server will reject the signature supplied by the client and public-key authentication will fail.

The session identifier is calculated based on (among other things) the shared secret negotiated by the peers using Diffie-Hellman algorithm. The algorithm itself does not protect against active MITM attack, but it makes it impossible for MITM attacker to influence the choice of the shared key (and by extension the session ID) by the victims.

So public-key authentication has somewhat unexpected side effect of preventing MITM. Nice to know.