7

Usually, people recommend to use a single private-public key pair everywhere (if we're not talking about a possibility of compromising the private key):

  1. Best Practice: “One per-user ssh key” or “multiple per-host ssh keys”
  2. Reusing Private/Public Keys
  3. https://serverfault.com/questions/80478/ssh-do-you-use-one-private-public-key-pair-for-each-remote-machine-or-a-single/80483#80483

    It seems that this would result in a vulnerability when using client certificate authentication over SSH. Since it is so popular to suggest, I suspect the "algorithm" below won't work. But I just don't get what exactly is wrong with it.

I've tried to make my description as detailed as possible, to minimize possible discrepancies, so, please excuse me for the length...

Preconditions

  • PC1 has both (S1_id_rsa.pub and S2_id_rsa.pub) fingerprints in his known hosts.
  • Server1 somehow knows about existence of PC1's account on Server2.
  • Keys:
    • PC1 : has P1_id_rsa , S1_id_rsa.pub, S2_id_rsa.pub.
    • Server1, the attacker : has S1_id_rsa, P1_id_rsa.pub, S2_id_rsa.pub.
    • Server2: has S2_id_rsa, P1_id_rsa.pub

Algorithm

This is something like a well-known man-in-the-middle attack, but a bit different.

  1. PC1 sends "Hi" to Server1
  2. Server1 sends "Hi" to Server2
  3. Server shares it's public key

    1. Server2 sends Server1 S2_id_rsa.pub
    2. Server1 sends PC1 S1_id_rsa.pub (instead of S2_id_rsa.pub)
    3. PC1 accepts S1_id_rsa.pub's fingerprint (as it's known)
  4. Two separate shared-secret tunnels are generated using Diffie-Hellman:

    1. "Server1--Server2"

      1. Server2 generates DH1.a, and sends DH1.A,signed with S2_id_rsa, to Server1
      2. Server1 generates DH1.b and sends DH1.B to Server2
      3. Tunnel established
    2. "PC1--Server1"
      1. Server1 generates DH2.a, and sends DH1.A, signed with S1_id_rsa, to PC1
      2. PC1 generates DH2.b and sends DH2.B to Server1
      3. Tunnel is established.
  5. Client Authentication (Server2 now wants to be sure he is talking to PC1)

    1. PC1 sends P1_id_rsa.pub to Server1
    2. Server1 sends P1_id_rsa.pub to Server2
    3. Server2 generates a challenge, which could be solved only with P1_id_rsa and sends it to Server1
    4. Server1 just tunnels challenge to PC1
    5. PC1 solves the challenge and sends answer to Server1
    6. Server1 tunnels answer to Server2
    7. Done.
  6. Done

P.S. I've looked through public-key cryptography and man-in-the-middle attack on Wikipedia, and this quite detailed answer (my view of the whole process is largely based on it), but I've not found the answer..

I wasn't able to find a readable "complete ssh authentication and encryption processes for dummies"...

I've already asked the same question on Server Fault, but was suggested to re-post it here.

Igor
  • 201
  • 2
  • 8
  • Note that if you login to a rouge server with **SSH agent forwarding** enabled, and the SSH key available in your agent without verification, the rouge server can use this to log in into a different server, using a similar scheme as you described. – Paŭlo Ebermann Aug 23 '16 at 17:21

4 Answers4

3

Usually, people recommend to use a single private-public key pair everywhere (if we're not talking about a possibility of compromising the private key)

Note that there are two key pairs involved in an SSH connection:

  • one to identify the server host (the server has the private key);
  • one to identify the user (the client has the private key), if using public key authentication.

The recommendations you cite are about user keys. Having a single private key per user is a viable model, though I don't particularly recommend it (the cost of having separate key pairs per host or site isn't that high). Server keys, on the other hand, are never duplicated. They are generated afresh when the SSH server is installed.

On this topic, beyond the posts you've cited in your question, see also What is the difference between authorized_keys and known_hosts file for SSH?

In the attack you describe, you've mixed up the roles of the keys. These are the host keys, ssh_host_rsa, and not user keys. This isn't directly relevant in how the attack you describe works, but may be part of your confusion.

The sticky point is at step 3.3:

PC1 accepts S1_id_rsa.pub's fingerprint (as it's known)

There are two cases.

  • If PC1 has connected to S2 before, then PC1 (or more precisely the account of the user on PC1) has memorized S2's host public key in its known_hosts file. So if S1 sends S2's host key, the SSH client on PC1 will detect that the purported host key does not match the recorded host key and abort the connection with a scary message:

    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    @    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
    Someone could be eavesdropping on you right now (man-in-the-middle attack)!
    It is also possible that the RSA host key has just been changed.
    

    The attack is thus impossible in this scenario (unless the user goes out of his way to bypass the check — OpenSSH purposefully makes this difficult).

  • If PC1 has never connected to S2, then PC1 will happily accept whatever S1 is now telling it is S2's key. Since PC1 has no prior knowledge of any association between the name S2 and a cryptographic identity, nor any way to contact a trusted third party who knows such an association (i.e. a public-key infrastructure), there is no way to prevent this man-in-the-middle attack.

Gilles 'SO- stop being evil'
  • 50,912
  • 13
  • 120
  • 179
  • 1) >Note that there are two key pairs involved in an SSH connection There might be a slight confusion in the naming scheme: I've just named 'Server2\etc\ssh\ssh_host_rsa_key.pub' "S2_id_rsa.pub", and "S1_id_rsa.pub" is defined in the same manner. 2) >If PC1 has connected to S2 before, then PC1 (or more precisely the account of the user on PC1) has memorized S2's host public key in its known_hosts file Em... The thing is, in my algorithm , **Server1** provides **PC1** with **S1_id_rsa.pub**, not **S2<..>.pub** – Igor Jan 21 '13 at 22:23
3

Finally, I think I got it.

First of all - I've mixed up ssh v1 and ssh v2 protocols in my description. The challenge-response authorization is from ssh v1.

In ssh v2 client simply signs a specific package with his private key and server checks it using stored public key. This package is described in rfc, here:

To perform actual authentication, the client MAY then send a
signature generated using the private key.  The client MAY send the
signature directly without first verifying whether the key is
acceptable.  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

So, the signed package contains "session identifier" which is calculated as follows:

  H = hash(V_C || V_S || I_C || I_S || K_S || e || f || K)
  <…..>
  mpint     K, the shared secret  

That is, signature does depend on shared secret, and shared secrets are different for P1<->S1 and S1<->S2 tunnels.

Igor
  • 201
  • 2
  • 8
1

The problem is that the client would know they are talking to server 1 because of the use of server 1's certificate. If server1 has a valid certificate saying they are server2, then the client would be unaware if they had not connected to server2 before. Most SSH clients however track the fingerprint of the certificate that they connect to for a given server. When the client goes to connect and ends up with a connection to server 1, even if the cert is signed, the user will likely get a warning that the fingerprint has changed.

As for a known connection to S1 trying to claim to be s2, if s2 accurately knows p1,s private key, then if 01 wants to talk to s2 through s1 then it should encrypt for s2 and s2 would then encrypt for p1. When p1 doesn't get a key exchange from s2 it will know there is a problem since s1 can't sign an exchange with p1 as s2.

AJ Henderson
  • 41,816
  • 5
  • 63
  • 110
  • >the client would know they are talking to server 1 The thing is, client intentionally wants to connect to **Server1**, that's why I've named the question "**known**-man-in-the-middle – Igor Jan 21 '13 at 22:29
  • @Igor ah, OK. Updated my answer to address your question better. – AJ Henderson Jan 23 '13 at 01:27
  • Em.... As far as I understood, you're still saying that **P1** wants to connect to **S2**. But it wants to connect to **S1** instead. The thing is, as far as I understood, only ssh server needs to sign his part of DH key with his private key for server to get authenticated (maybe this is required for keyboard-interactive authentication to be possible). So, **S1** doesn't need to "fake" any private key. He may use his own key. It's known and trusted. Client authenthication works a bit different. And again, **S1** can fool **S2** by allowing **P1** to solve the "challenge". – Igor Jan 23 '13 at 16:40
  • @Igor - S2 knows it is talking to S1 though because the challenge should be formulated so that only P1 can read it. If P1 gets a challenge from S2 while talking to S1, it shouldn't answer it in a way that S1 can read. P1 should always know reliably which server they are talking to and if S1 should not be able to impersonate a tunnel to P2 without actually tunneling so long as either P1 knows S2 or vice versa. – AJ Henderson Jan 23 '13 at 18:25
  • as far as I understood, the challenge could only be SOLVED by **P1**, because he's the only one with **P1**'s private key. Anyone could receive the challenge, but only the one with an appropriate private key can solve it. That's the idea of the challenge,isn't it? The thing is, after receiving the challenge from **S2**, **S1** may just pass it to **P1** , for **P1** to solve it. At least, I don't understand, why he's unable to do so. This is the main idea of the whole question... – Igor Jan 23 '13 at 23:15
  • @Igor There should be two parts to a challenge. The message should be encrypted with the user (the one being challenged)'s public key, thus nobody else can read the challenge. It should also require a signed response, thus only the user can respond. Additionally, the client should encrypt the response so that only the original challenger can read the result of the challenge. For a challenge to be effective, it must be impossible for anyone in between the challenger and challengee to impersonate either party. – AJ Henderson Jan 24 '13 at 14:00
  • Yes, only **PC1** is able to solve (or read, what's the difference?)the challenge. But why **S1** is unable to tunnel the challenge received from **S2** to **S1** ? **S1** don't have to read it, just tunnel it. I can't find any statement like "challenge is signed with **S2_id_rsa** and THEN encrypted with **P1_id_rsa.pub**" (in this case, **S1** won't be able to modify the signature) anywhere. In fact, I haven't found anything at all about the challenge being signed and/or encrypted.... They all say,server is authenticated on DH key exchange step, why do double authentication? – Igor Jan 25 '13 at 16:36
  • @Igor - if S1 did that, then it could not read the response from PC1 because PC1 would respond in such a way that only S2 could read the handshake. Part of the handshake is a key exchange that would make it so that S1 did not know the session key and therefore could not tell what was being said between S2 and PC1. – AJ Henderson Jan 25 '13 at 18:10
  • @Igor - I guess I should ask, are you specifically asking about the legacy protocol SSH or the much more commonly used today SSH2? A lot of my comments have been about the Diffie Hellman key exchange and the use of MACs in SSH v2. – AJ Henderson Jan 25 '13 at 18:14
  • @Igor - the difference between solving and reading is that if someone can read, but not solve, then they can read the problem and pass the same problem along in such a way that they can read the solution. For example, if I ask you to solve 2+3 (and only you can solve 2+3), then if the man in the middle can read the question and you trust the man in the middle, they can ask you for 2+3 to prove who you are to them. They can then simply pass along your answer. Thus, it is important that only the one being challenged can read or respond to the challenge. – AJ Henderson Jan 25 '13 at 18:16
  • (IN response to:1/3) If **S1** would just tunnel DH key exchange data - it won't be able to encrypt/decrypt any data in this session. But that's a separate data packet, not the same as the packet with challenge. So, **S1** works differently with them. There are two different "DH channels", with symmetric encryption which **S1** is able to encrypt/decrypt, but there's only one client authentication process - **S1** just tunnels it. – Igor Jan 26 '13 at 01:11
  • (In response to:2/3) Yep, I believe, we're talking about ssh2... – Igor Jan 26 '13 at 01:13
  • (In response to 3/3) As far as I understood, the "challenge" packet to be sent to client is described [here](http://tools.ietf.org/html/rfc4252), around page 8, but I can't see any info about additional encryption for challenge, so, I assume that it's only encrypted with the same symmetric encryption, based on DH key exchange,that all data is encrypted with. But **S1** is able to read the data encrypted with it, since it had established two separate channels with **P1** and **S2** before. How exactly is the challenge encrypted? – Igor Jan 26 '13 at 01:14
  • @igor The key exchange occurs prior to authentication. The rfc you mentioned references one for ssh transport. If the user has connected to s2 previously or a certificate is expected, then s1 will not be able to convince p1 to establish a key exchange with s2 without having s2s private key. – AJ Henderson Jan 27 '13 at 00:29
  • Em...But **S1** is going to convince **P1** to establish a DH key exchange with itself, **S1**. So, for DH key exchange **P1<->S1** and **S1<->S2**. – Igor Jan 27 '13 at 14:40
  • @Igor - but P1 would know it was talking to S1 as a result of that exchange because DH will attest to the identity of the server. This would be unexpected behavior for P1 trying to talk to S2 as in that case, the SSH Tunnel should be established between P1 and S2 with S1 only serving to relay the packets. Thus, P1 would never respond to a challenge from S2 on an SSH tunnel that was negotiated with S1. – AJ Henderson Jan 28 '13 at 13:50
  • >>"_but_ _P1_ _would_ _know_ _it_ _was_ _talking_ _to_ _S1_" Indeed, **P1** knows that he is talking to **S1**, and the thing is - this is what **P1** wants.... **P1** WANTS to connect to **S1**, and, it doesn't want to connect to **S2** (this time). As far as I understood, the rest of your message seem to be based on the same idea... – Igor Jan 29 '13 at 23:32
  • I'm very sorry I failed to describe the whole situation in question in a manner that no misunderstanding would be possible... I've tried my best... Sorry.... – Igor Jan 29 '13 at 23:41
  • @Igor - If P1 is providing authentication for S2, then it should only provide that on a connection with S2 (even if being intentionally tunneled through S1). If P1 is giving S1 the authentication credentials to make a connection from S1 to S2 as P1, then yes a MITM is possible, but that is why P1 should tunnel a connection THRU S1 to get to S2 instead of giving S1 the connection. There isn't a meaningful difference as S1 is still the relay, just the connection and credentials are protected. – AJ Henderson Jan 30 '13 at 13:38
  • Em... **P1** thinks that there's a usual ssh server running on **S1**, and it tries to connect to it. But instead of normal ssh server, there's a specific "hack tool". **P1** doesn't know that he's request would be tunneled. – Igor Feb 01 '13 at 12:00
  • @Igor - in this case, P1 would know they are talking to S1 and not provide credentials for S2. – AJ Henderson Feb 01 '13 at 15:55
  • **P1** knows that he is talking to **S1**. But it looks like **P1** is unable to determine, whether challenge (received from **S1**) was generated by **S1** or someone else. – Igor Feb 01 '13 at 23:56
  • @Igor - P1 knows the transport that is asking. If P1 tunnels to S1 and then uses a client on S1 to talk to S2, then yes, a bad S1 could easily just record the input of P1, no breaking of the authentication is necessary. For P1 to talk to S2 while going through S1, P1 should establish a tunnel THRU S1 to S2 at which point the transport is validated as going to S2 prior to authentication messages being sent. S1 then has no access to the actual information on the tunnel, nor can it modify it meaningfully. – AJ Henderson Feb 04 '13 at 13:45
  • >>"_P1_ _tunnels_ _to_ _S1_ _and_ _then_ _uses_ _a_ _client_ _on_ _S1_ _to_ _talk_ _to_ _S2_" nope, **P1** is just trying to connect to **S1**, it doesn't want to connect to **S2** either directly or through someone. Connecting to **S2** is a result of the attack. >>"_For_ _P1_ _to_ _talk_ _to_ _S2_ _while_ _going_ _through_ _S1_ <...> _S1_ _then_ _has_ _no_ _access_ _to_ _the_ _actual_ _information_ _on_ _the_ _tunnel_, _nor_ _can_ _it_ _modify_ _it_ _meaningfully_" In this case, you treat **S1** as just a regular router, somewhere between **P1** and **S2**. This is not the case I'm ... – Igor Feb 05 '13 at 12:44
  • ...talking about. In my case, I'm playing on the authorization sequence features, not on the general concept of ssh. And the thing is - symmetric encryption is established firstly. Server would establish symmetrically encrypted channel with anyone who asks. And the identity of user is not yet verified. It would be verified during the second step. The symmetrically encrypted channel is secure, but you still don't know who you've got channel with. Only server signs his DH key part with his signature. So, on the first step **2** independent DH tunnels is established. **P1**<->**S1** and – Igor Feb 05 '13 at 12:54
  • **S1**<->**S2**. Now, **P1** is sure that he is connected to the server he intended to connect - to **S1**. And he is waiting for the challenge from **S1**, that he would solve to get authorized. On the other hand, **S1** is not authorized as ssh client on **S2**, and **S2** will send **S1** a challenge,that only **P1** is able to solve. The message structure is described in rfc I've mentioned above. I can't see any info that it's additionally encrypted or something. As far as I can tell, it is sent in quite plain format. It's only encrypted with general symmetric encryption, to make – Igor Feb 05 '13 at 13:01
  • sure only the one **S2** has a symmetrically encrypted connection with would be able to read it, that is - only **S1**. So, **S1** receives the challenge, and re-sends it to **P1**, now, **P1** is unable to determine, whether this challenge was generated by **S1** or someone else. So, it solves the challenge, and sends the answer to **S1**. – Igor Feb 05 '13 at 13:04
  • "P1 is just trying to connect to S1, it doesn't want to connect to S2 either directly or through someone." - Then why is P1 entering credentials for S2? If P1 is using the same credentials for both, then yes, there is a problem, but that's why credentials should be unique to a server. It's also worth noting that this would only be necessary if cryptographic authentication (like a client cert) is being used. If it was a password, then a bad server could simply save the password and reuse it when the password is initially set. – AJ Henderson Feb 05 '13 at 14:01
  • Sure, **P1** is not "entering the credentials" for **S2** (at least, he thinks so). **P1** only solves the challenge, received from **S1**, because he thinks, that this is required to authorize on **S1**. The whole attack would not be possible, if **P1** would use different key pairs for **S1** and **S2**... But, the thing is (and I've started the question with it): "_Usually,_ _people_ _recommend_ _to_ _use_ _a_ _single_ _private-public_ _key_ _pair_ _everywhere_"... That's why, in "preconditions" section, both **S1** and **S2** have the same "P1_id_rsa.pub". – Igor Feb 06 '13 at 13:37
  • PS... I'm not sure, what do you mean under "credentials".. – Igor Feb 06 '13 at 13:42
  • @Igor - ah, ok, I see now, it was not clear to me that you were talking about client certificate based SSL with a single private key being used. Now that I see that, yes, I agree that such an attack would bypass the transport protection. I'm not well read enough on the authentication itself, but I would hazard from my quick read that it seems like you are correct. – AJ Henderson Feb 06 '13 at 14:06
0

Actually that is a known vulnerability with cracked certificates in the trusted list, and why revocation lists are necessary.

For this attack to work there need to be 2 holes:

  1. PC1 must think it is communicating with server2 while it is actually communicating with server1.
  2. PC1 must have S1_id_rsa.pub as a trusted certificate and it points to server2's address (other wise PC1 will know it is talking to server1 instead of server2 in step #4.3).

#1 can happen on an untrusted network, while #2 can only happen when a previous breach has happened that allowed server1 to insert itself as a trusted party.

Bob Ortiz
  • 6,234
  • 8
  • 43
  • 90
ratchet freak
  • 325
  • 1
  • 8
  • >>1 But what if **PC1** intentionally wants to connect to **Server1**? (and S1_id_rsa.pub is trusted in this situation, yes) – Igor Jan 21 '13 at 22:35