Encryption of the default key in OpenSSH is worse than its absence
- Transfer
The authors of this material argue against the standard key encryption mechanisms in OpenSSH.
Recently, attackers used the eslint-scope npm package to steal npm tokens from users' home directories. In the light of this event, we started checking other similar vulnerabilities and thought about how to reduce the risks and consequences of such incidents.
Most of us have an RSA SSH key on hand. Such a key gives the owner a variety of privileges: as a rule, it is used to access the production environment or in GitHub. Unlike nmp-tokens, SSH keys are encrypted, and therefore it is generally accepted that nothing bad will happen even if they fall into the wrong hands. But is it really? Let's find out.
This key is encrypted, which is one of the first lines of the file. In addition, at the beginning there is no MII - base64 coding key used in RSA. And, of course, AES catches the eye! That's good, right? And CBC, at first glance, with a random initialization vector. No authentication code (MAC). Well, okay, but there will be no padding oracle attack, right?
Finding out what DEK-Info actually means is not so easy. Searching for the “DEK-Info” keyword in the openssh-portable repository shows only example keys. But the point here is that the AES key is nothing more than a simple MD5 hash (password || initialization vector [: 8]). And this is bad, because best practices for storing passwords say that passwords in their pure form, because of their low entropy, are bad encryption material. And to make it better, you need an expensive function like Argon2. But MD5, unlike the latter, is easy to calculate.
The only positive point in this scheme is that the salt is placed after the password, therefore, it is impossible to calculate the intermediate state of MD5 (IV [8:]) and pick passwords based on it. But this is poor consolation, especially in an era when machines that perform billions of MD5 calls per second are available to us — more than you can think of passwords.
You may be wondering how OpenSSH has survived to this. Alas, the answer is simple: the command line tool OpenSSL initially used this scheme by default, and it simply became the norm.
As a result, it becomes fair to say that standard, encrypted keys are no better than ordinary unencrypted keys simply because the encryption mechanism is inefficient. However, we would have spoken even more boldly - they are worse. And it’s easy to argue.
Many people are unlikely to use a password manager to store a password from an SSH key. Rather, the user will just remember it. And, since this is one of the memorized combinations, it is likely that the user has already applied it elsewhere. It may even coincide with the user password from the device. It is quite possible to guess it (the forming function is too unreliable), and if the password becomes known, you can certainly check it with the public key.
There are no complaints about the RSA-key pair itself: the whole question is only in the methods of symmetric encryption of the private key. To carry out the attack described above, knowing only one public key, it is impossible.
OpenSSH has a new key format that should be used. By new is meant introduced in 2013. This format uses bcrypt_pbkdf, which is essentially a fixed-complexity bcrypt, implemented within the PBKDF2 standard.
Conveniently, you automatically get a key of a new format when you generate Ed25519 keys, since the old SSH key format does not support newer key types. This is rather strange, because in fact we don’t need the key format to determine how Ed25519 serialization works, because Ed25519 itself sets the serialization work. But if you really need a good formative function, then you can not bother with such trifles. As a result, one of the answers is ssh-keygen -t ed25519 .
If for compatibility reasons you need to stick with RSA, you can use ssh-keygen -o. Thus, it is possible to get a new format even for old types of keys. You can update old keys using the ssh-keygen command -p -o -f key name . If your keys live on Yubikey or smart cards, then there these amendments are already taken into account.
One way or another, we are striving for a more optimal solution. On the one hand, there is a good aws-vault example in which credential information was moved from disk to keychains. There is another approach: moving the development to separate environments. And finally, most startups should consider avoiding the long-term storage of SSH keys and switching to an SSH certification center with a limited key storage period, coupled with a single sign-on system. Unfortunately, in the case of GitHub, this approach is impossible.
PSIt is difficult to verify this information in an authoritative source, but, if memory serves us, the versioned parameter in PEM private keys of the OpenSSH format only affects the encryption method. However, this does not play any role: the problem lies in the key-forming function, and, we think, this is another argument against discussing the protocols in parts. On this topic will be a separate post on our blog.
And finally - a link to the full key. This is in case you are set to hack something today.
Recently, attackers used the eslint-scope npm package to steal npm tokens from users' home directories. In the light of this event, we started checking other similar vulnerabilities and thought about how to reduce the risks and consequences of such incidents.
Most of us have an RSA SSH key on hand. Such a key gives the owner a variety of privileges: as a rule, it is used to access the production environment or in GitHub. Unlike nmp-tokens, SSH keys are encrypted, and therefore it is generally accepted that nothing bad will happen even if they fall into the wrong hands. But is it really? Let's find out.
user@work /tmp $ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/user/.ssh/id_rsa): mykey
...
user@work /tmp $ head -n 5 mykey
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,CB973D5520E952B8D5A6B86716C6223F
+5ZVNE65kl8kwZ808e4+Y7Pr8IFstgoArpZJ/bkOs7rB9eAfYrx2CLBqLATk1RT/
This key is encrypted, which is one of the first lines of the file. In addition, at the beginning there is no MII - base64 coding key used in RSA. And, of course, AES catches the eye! That's good, right? And CBC, at first glance, with a random initialization vector. No authentication code (MAC). Well, okay, but there will be no padding oracle attack, right?
Finding out what DEK-Info actually means is not so easy. Searching for the “DEK-Info” keyword in the openssh-portable repository shows only example keys. But the point here is that the AES key is nothing more than a simple MD5 hash (password || initialization vector [: 8]). And this is bad, because best practices for storing passwords say that passwords in their pure form, because of their low entropy, are bad encryption material. And to make it better, you need an expensive function like Argon2. But MD5, unlike the latter, is easy to calculate.
The only positive point in this scheme is that the salt is placed after the password, therefore, it is impossible to calculate the intermediate state of MD5 (IV [8:]) and pick passwords based on it. But this is poor consolation, especially in an era when machines that perform billions of MD5 calls per second are available to us — more than you can think of passwords.
You may be wondering how OpenSSH has survived to this. Alas, the answer is simple: the command line tool OpenSSL initially used this scheme by default, and it simply became the norm.
As a result, it becomes fair to say that standard, encrypted keys are no better than ordinary unencrypted keys simply because the encryption mechanism is inefficient. However, we would have spoken even more boldly - they are worse. And it’s easy to argue.
Many people are unlikely to use a password manager to store a password from an SSH key. Rather, the user will just remember it. And, since this is one of the memorized combinations, it is likely that the user has already applied it elsewhere. It may even coincide with the user password from the device. It is quite possible to guess it (the forming function is too unreliable), and if the password becomes known, you can certainly check it with the public key.
There are no complaints about the RSA-key pair itself: the whole question is only in the methods of symmetric encryption of the private key. To carry out the attack described above, knowing only one public key, it is impossible.
How can I fix the situation?
OpenSSH has a new key format that should be used. By new is meant introduced in 2013. This format uses bcrypt_pbkdf, which is essentially a fixed-complexity bcrypt, implemented within the PBKDF2 standard.
Conveniently, you automatically get a key of a new format when you generate Ed25519 keys, since the old SSH key format does not support newer key types. This is rather strange, because in fact we don’t need the key format to determine how Ed25519 serialization works, because Ed25519 itself sets the serialization work. But if you really need a good formative function, then you can not bother with such trifles. As a result, one of the answers is ssh-keygen -t ed25519 .
If for compatibility reasons you need to stick with RSA, you can use ssh-keygen -o. Thus, it is possible to get a new format even for old types of keys. You can update old keys using the ssh-keygen command -p -o -f key name . If your keys live on Yubikey or smart cards, then there these amendments are already taken into account.
One way or another, we are striving for a more optimal solution. On the one hand, there is a good aws-vault example in which credential information was moved from disk to keychains. There is another approach: moving the development to separate environments. And finally, most startups should consider avoiding the long-term storage of SSH keys and switching to an SSH certification center with a limited key storage period, coupled with a single sign-on system. Unfortunately, in the case of GitHub, this approach is impossible.
PSIt is difficult to verify this information in an authoritative source, but, if memory serves us, the versioned parameter in PEM private keys of the OpenSSH format only affects the encryption method. However, this does not play any role: the problem lies in the key-forming function, and, we think, this is another argument against discussing the protocols in parts. On this topic will be a separate post on our blog.
And finally - a link to the full key. This is in case you are set to hack something today.