ssh-audit
ssh-audit copied to clipboard
builtin hardened policy vs ED25519 CA
- OpenSSH 8.4p1 / Debian 11
Hardened OpenSSH Server v8.4 (version 1)policy
server host keys signed with ED25519 CA
$ ./ssh-audit.py debian11 -P "Hardened OpenSSH Server v8.4 (version 1)"
Host: debian11
Policy: Hardened OpenSSH Server v8.4 (version 1)
Result: ❌ Failed!
Errors:
* RSA CA key ([email protected]) sizes did not match.
- Expected: 4096
- Actual: 0
* RSA CA key ([email protected]) sizes did not match.
- Expected: 4096
- Actual: 0
$ ./ssh-audit.py debian11
# general
(gen) banner: SSH-2.0-OpenSSH_8.4p1
(gen) software: OpenSSH 8.4p1
(gen) compatibility: OpenSSH 7.8+, Dropbear SSH 2018.76+
(gen) compression: enabled ([email protected])
# key exchange algorithms
(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4, Dropbear SSH 2018.76
(kex) [email protected] -- [info] available since OpenSSH 6.5, Dropbear SSH 2013.62
(kex) diffie-hellman-group16-sha512 -- [info] available since OpenSSH 7.3, Dropbear SSH 2016.73
(kex) diffie-hellman-group18-sha512 -- [info] available since OpenSSH 7.3
(kex) diffie-hellman-group-exchange-sha256 (2048-bit) -- [info] available since OpenSSH 4.4
# host-key algorithms
(key) rsa-sha2-512 (4096-bit) -- [info] available since OpenSSH 7.2
(key) rsa-sha2-256 (4096-bit) -- [info] available since OpenSSH 7.2
(key) [email protected] (4096-bit) -- [info] available since OpenSSH 7.8
(key) [email protected] (4096-bit) -- [info] available since OpenSSH 7.8
(key) ssh-ed25519 -- [info] available since OpenSSH 6.5
(key) [email protected] (4096-bit) -- [info] available since OpenSSH 6.5
# encryption algorithms (ciphers)
(enc) [email protected] -- [info] available since OpenSSH 6.5
`- [info] default cipher since OpenSSH 6.9.
(enc) [email protected] -- [info] available since OpenSSH 6.2
(enc) [email protected] -- [info] available since OpenSSH 6.2
(enc) aes256-ctr -- [info] available since OpenSSH 3.7, Dropbear SSH 0.52
(enc) aes192-ctr -- [info] available since OpenSSH 3.7
(enc) aes128-ctr -- [info] available since OpenSSH 3.7, Dropbear SSH 0.52
# message authentication code algorithms
(mac) [email protected] -- [info] available since OpenSSH 6.2
(mac) [email protected] -- [info] available since OpenSSH 6.2
(mac) [email protected] -- [info] available since OpenSSH 6.2
# fingerprints
(fin) ssh-ed25519: SHA256:czmBs9YLHz3r9Y4ETVrSpkZwaTcCmZ+1ET7q8oipveo
(fin) ssh-rsa: SHA256:AmB9U/EExKoewKgfX5rGtVsY75Z4rt3y0+tmWOqzjyc
# algorithm recommendations (for OpenSSH 8.4)
(rec) +diffie-hellman-group14-sha256 -- kex algorithm to append
signed with RSA CA
$ ./ssh-audit.py debian11 -P "Hardened OpenSSH Server v8.4 (version 1)"
Host: debian11
Policy: Hardened OpenSSH Server v8.4 (version 1)
Result: ✔ Passed
$ ./ssh-audit.py debian11
# general
(gen) banner: SSH-2.0-OpenSSH_8.4p1
(gen) software: OpenSSH 8.4p1
(gen) compatibility: OpenSSH 7.8+, Dropbear SSH 2018.76+
(gen) compression: enabled ([email protected])
# key exchange algorithms
(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4, Dropbear SSH 2018.76
(kex) [email protected] -- [info] available since OpenSSH 6.5, Dropbear SSH 2013.62
(kex) diffie-hellman-group16-sha512 -- [info] available since OpenSSH 7.3, Dropbear SSH 2016.73
(kex) diffie-hellman-group18-sha512 -- [info] available since OpenSSH 7.3
(kex) diffie-hellman-group-exchange-sha256 (2048-bit) -- [info] available since OpenSSH 4.4
# host-key algorithms
(key) rsa-sha2-512 (4096-bit) -- [info] available since OpenSSH 7.2
(key) rsa-sha2-256 (4096-bit) -- [info] available since OpenSSH 7.2
(key) [email protected] (4096-bit cert/4096-bit CA) -- [info] available since OpenSSH 7.8
(key) [email protected] (4096-bit cert/4096-bit CA) -- [info] available since OpenSSH 7.8
(key) ssh-ed25519 -- [info] available since OpenSSH 6.5
(key) [email protected] (4096-bit cert/4096-bit CA) -- [info] available since OpenSSH 6.5
# encryption algorithms (ciphers)
(enc) [email protected] -- [info] available since OpenSSH 6.5
`- [info] default cipher since OpenSSH 6.9.
(enc) [email protected] -- [info] available since OpenSSH 6.2
(enc) [email protected] -- [info] available since OpenSSH 6.2
(enc) aes256-ctr -- [info] available since OpenSSH 3.7, Dropbear SSH 0.52
(enc) aes192-ctr -- [info] available since OpenSSH 3.7
(enc) aes128-ctr -- [info] available since OpenSSH 3.7, Dropbear SSH 0.52
# message authentication code algorithms
(mac) [email protected] -- [info] available since OpenSSH 6.2
(mac) [email protected] -- [info] available since OpenSSH 6.2
(mac) [email protected] -- [info] available since OpenSSH 6.2
# fingerprints
(fin) ssh-ed25519: SHA256:czmBs9YLHz3r9Y4ETVrSpkZwaTcCmZ+1ET7q8oipveo
(fin) ssh-rsa: SHA256:AmB9U/EExKoewKgfX5rGtVsY75Z4rt3y0+tmWOqzjyc
# algorithm recommendations (for OpenSSH 8.4)
(rec) +diffie-hellman-group14-sha256 -- kex algorithm to append
it's not clear if it's a detection bug/difficulty or a case of "ed25519 ca = bad"
the fact that it's not showing something similar to (4096-bit cert/4096-bit CA) for ed25519 CA makes me think that it's not detecting, but then is it a policy issue or elsewhere?
Thank you
I took a look at this and found that the code isn't correctly handling the case of mixed ED25519 CAs with RSA host keys. I'll be taking a closer look at this and fixing it shortly.
it's not clear if it's a detection bug/difficulty or a case of "ed25519 ca = bad"
ED25519 CAs are good, actually.
Thanks for reporting this!
-- Joseph S. Testa II Founder & Principal Security Consultant Positron Security
awesome! thank you
@zomfg: At long last, I've finally checked in a fix for this issue! (See https://github.com/jtesta/ssh-audit/commit/263267c5ad013d314574fcd9bc6a744699e226c5.) If you have an opportunity to give it a try, please let me know how it works for you.
Thanks for reporting!!
Hi! I get this
Traceback (most recent call last):
File ./ssh-audit.py", line 16, in <module>
exit_code = main()
File "src/ssh_audit/ssh_audit.py", line 1380, in main
ret = audit(out, aconf)
File "src/ssh_audit/ssh_audit.py", line 1104, in audit
program_retval = exitcodes.GOOD if evaluate_policy(out, aconf, banner, s.client_host, kex=kex) else exitcodes.FAILURE
File "src/ssh_audit/ssh_audit.py", line 566, in evaluate_policy
passed, error_struct, error_str = aconf.policy.evaluate(banner, kex)
File "src/ssh_audit/policy.py", line 400, in evaluate
expected_hostkey_size = self._hostkey_sizes[hostkey_type]['hostkey_size']
TypeError: 'int' object is not subscriptable
and indeed, at that point self._hostkey_sizes seems to be
{'rsa-sha2-256': 4096, 'rsa-sha2-512': 4096}
looks like format mismatch
Is the target host available on the public Internet? If so, I'd like to test against it. Could I get the host/IP? You could e-mail it to me at jtesta at-sign positronsecurity dot com, if you'd rather not list it here.
I suppose the policy file you used would make it easier to debug as well.
it's an internal work thing so wont be much help ok so this is what happens same test as originally
$ ./ssh-audit.py debian11 -P "Hardened OpenSSH Server v8.4 (version 1)"
I get the crash above.
Looking here
https://github.com/jtesta/ssh-audit/blob/0bfb5d6979420c295bcc50c6c4213c2dd3a8e2f2/src/ssh_audit/policy.py#L399-L400
self._hostkey_sizes and server_host_keys are like this
{'rsa-sha2-256': 4096, 'rsa-sha2-512': 4096}
{
'rsa-sha2-256': {'raw_hostkey_bytes': b'\x00\x00\x00\x07ssh-rsa...', 'hostkey_size': 4096, 'ca_key_type': '', 'ca_key_size': 0},
'ssh-rsa': {'raw_hostkey_bytes': b'\x00\x00\x00\x07ssh-rsa...', 'hostkey_size': 4096, 'ca_key_type': '', 'ca_key_size': 0},
'rsa-sha2-512': {'raw_hostkey_bytes': b'\x00\x00\x00\x07ssh-rsa...', 'hostkey_size': 4096, 'ca_key_type': '', 'ca_key_size': 0},
'[email protected]': {'raw_hostkey_bytes': b'\x00\x00\x00\[email protected]...', 'hostkey_size': 4096, 'ca_key_type': 'ssh-ed25519', 'ca_key_size': 256},
'[email protected]': {'raw_hostkey_bytes': b'\x00\x00\x00\[email protected]...', 'hostkey_size': 4096, 'ca_key_type': 'ssh-ed25519', 'ca_key_size': 256},
'ssh-ed25519': {'raw_hostkey_bytes': b'\x00\x00\x00\x0bssh-ed25519...', 'hostkey_size': 256, 'ca_key_type': '', 'ca_key_size': 0},
'[email protected]': {'raw_hostkey_bytes': b'\x00\x00\x00 [email protected]...', 'hostkey_size': 256, 'ca_key_type': 'ssh-ed25519', 'ca_key_size': 256}
}
However it works with a generated certpolicy.txt
$ ./ssh-audit.py -M certpolicy.txt debian11
$ ./ssh-audit.py -P certpolicy.txt debian11
Host: debian11
Policy: Custom Policy (based on debian11 on 2023/04/25) (version 1)
Result: ✔ Passed
and self._hostkey_sizes looks like
{
'rsa-sha2-256': {'hostkey_size': 4096, 'ca_key_type': '', 'ca_key_size': 0, 'raw_hostkey_bytes': b''},
'ssh-rsa': {'hostkey_size': 4096, 'ca_key_type': '', 'ca_key_size': 0, 'raw_hostkey_bytes': b''},
'rsa-sha2-512': {'hostkey_size': 4096, 'ca_key_type': '', 'ca_key_size': 0, 'raw_hostkey_bytes': b''},
'[email protected]': {'hostkey_size': 4096, 'ca_key_type': 'ssh-ed25519', 'ca_key_size': 256, 'raw_hostkey_bytes': b''},
'[email protected]': {'hostkey_size': 4096, 'ca_key_type': 'ssh-ed25519', 'ca_key_size': 256, 'raw_hostkey_bytes': b''},
'ssh-ed25519': {'hostkey_size': 256, 'ca_key_type': '', 'ca_key_size': 0, 'raw_hostkey_bytes': b''},
'[email protected]': {'hostkey_size': 256, 'ca_key_type': 'ssh-ed25519', 'ca_key_size': 256, 'raw_hostkey_bytes': b''}
}
#
# Custom policy based on debian11 (created on 2023/04/25)
#
# The name of this policy (displayed in the output during scans). Must be in quotes.
name = "Custom Policy (based on debian11 on 2023/04/25)"
# The version of this policy (displayed in the output during scans). Not parsed, and may be any value, including strings.
version = 1
# The banner that must match exactly. Commented out to ignore banners, since minor variability in the banner is sometimes normal.
# banner = "SSH-2.0-OpenSSH_8.4p1 Debian-5+deb11u1"
# The compression options that must match exactly (order matters). Commented out to ignore by default.
# compressions = none, [email protected]
# Dictionary containing all host key and size information. Optionally contains the certificate authority's signature algorithm ('ca_key_type') and signature length ('ca_key_size'), if any.
host_key_sizes = {"rsa-sha2-256": {"hostkey_size": 4096}, "ssh-rsa": {"hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "[email protected]": {"hostkey_size": 4096, "ca_key_type": "ssh-ed25519", "ca_key_size": 256}, "[email protected]": {"hostkey_size": 4096, "ca_key_type": "ssh-ed25519", "ca_key_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "[email protected]": {"hostkey_size": 256, "ca_key_type": "ssh-ed25519", "ca_key_size": 256}}
# Group exchange DH modulus sizes.
dh_modulus_sizes = {"diffie-hellman-group-exchange-sha256": 2048}
# The host key types that must match exactly (order matters).
host keys = rsa-sha2-512, rsa-sha2-256, [email protected], [email protected], ssh-ed25519, [email protected]
# Host key types that may optionally appear.
#optional host keys = [email protected],[email protected],[email protected],[email protected],[email protected]
# The key exchange algorithms that must match exactly (order matters).
key exchanges = curve25519-sha256, [email protected], diffie-hellman-group16-sha512, diffie-hellman-group18-sha512, diffie-hellman-group-exchange-sha256
# The ciphers that must match exactly (order matters).
ciphers = [email protected], [email protected], [email protected], aes256-ctr, aes192-ctr, aes128-ctr
# The MACs that must match exactly (order matters).
macs = [email protected], [email protected], [email protected]
Now if I alter the built-in Hardened OpenSSH Server v8.4 (version 1) policy as follows
'hostkey_sizes': {'rsa-sha2-256': {'hostkey_size': 4096, 'ca_key_type': '', 'ca_key_size': 0, 'raw_hostkey_bytes': b''}, 'rsa-sha2-512': {'hostkey_size': 4096, 'ca_key_type': '', 'ca_key_size': 0, 'raw_hostkey_bytes': b''}}
it doesn't crash
$ ./ssh-audit.py -P "Hardened OpenSSH Server v8.4 (version 1)" debian11
Host: debian11
Policy: Hardened OpenSSH Server v8.4 (version 1)
Result: ✔ Passed
@zomfg : Thanks for the quick response! I updated the master branch with a fix for the built-in policies. Might you be able to test it again?
We are back more or less to the original issue
Host: debian11
Policy: Hardened OpenSSH Server v8.4 (version 2)
Result: ❌ Failed!
Errors:
* CA signature type did not match.
- Expected: ssh-rsa
- Actual: ssh-ed25519
* CA signature type did not match.
- Expected: ssh-rsa
- Actual: ssh-ed25519
builtin policy assumes same type for hostkey and CA so mixing types doesn't work
{'[email protected]': {'hostkey_size': 4096, 'ca_key_type': 'ssh-rsa', 'ca_key_size': 4096, 'raw_hostkey_bytes': b''}}
while the custom policy dump saves mixed types
{'[email protected]': {'hostkey_size': 4096, 'ca_key_type': 'ssh-ed25519', 'ca_key_size': 256, 'raw_hostkey_bytes': b''}}
so maybe CA types/sizes could be something like
{
'[email protected]': {
'hostkey_size': 4096,
'ca': [('ssh-rsa', 4096), ('ssh-ed25519', 256)],
'raw_hostkey_bytes': b''
}
}
and then
if (server_ca_key_type, server_ca_key_size) in self._hostkey_sizes[hostkey_type]['ca']
Supporting multiple CA types per individual host key for built-in policies wasn't part of the plan here. The primary goal was to support custom policies with mixed host key/CA key types (i.e.: an RSA host key signed by an ED25519 CA). Does the recent commit fix this part for you?
If you'd like the built-in policies to support multiple CAs per host key, that would be a valid feature request for a future release. Feel free to open a new issue!
I just realized that the original issue (and title) do talk about the built-in policies. I suppose at some point along the way, I re-framed it in my mind in terms of getting basic support for mixed host key/CA key support (which I think I succeeded in).
Still... this is an improvement over what we had before. And we can still track the desired functionality for a future release.
oh yeah this is an improvement for sure and yeah if you want to close this one and deal with further improvements later is fine by me oh and thank you for adding policies for recent versions!
Sure thing! And thanks for reporting this, and thanks for helping test!