jtesta / ssh-audit

SSH server & client security auditing (banner, key exchange, encryption, mac, compression, compatibility, security, etc)
MIT License
3.41k stars 176 forks source link

Don't recommend weaker crypto #166

Open notramo opened 1 year ago

notramo commented 1 year ago

If a server has AES 256 enabled, but not AES 128, don't indicate it as an error, despite AES 128 being considered as modern. Similarly, if a server has sntrup761x25519-sha512@openssh.com enabled, but not curve25519-ssh, dont' indicate it as an error.

jtesta commented 1 year ago

Could you please paste the entire ssh-audit output of a scan that produced this output? That would help me understand what it did exactly, and why. Thanks!

notramo commented 1 year ago

sshd_config cryptography section:

HostKey /etc/ssh/ssh_host_ed25519_key
Ciphers chacha20-poly1305@openssh.com
KexAlgorithms sntrup761x25519-sha512@openssh.com
HostKeyAlgorithms ssh-ed25519
MACs hmac-sha2-512-etm@openssh.com

output:

# general
(gen) banner: SSH-2.0-OpenSSH_9.1
(gen) software: OpenSSH 9.1
(gen) compatibility: OpenSSH 8.5+
(gen) compression: enabled (zlib@openssh.com)

# key exchange algorithms
(kex) sntrup761x25519-sha512@openssh.com  -- [warn] using experimental algorithm
                                          `- [info] available since OpenSSH 8.5

# host-key algorithms
(key) ssh-ed25519                         -- [info] available since OpenSSH 6.5

# encryption algorithms (ciphers)
(enc) chacha20-poly1305@openssh.com       -- [info] available since OpenSSH 6.5
                                          `- [info] default cipher since OpenSSH 6.9.

# message authentication code algorithms
(mac) hmac-sha2-512-etm@openssh.com       -- [info] available since OpenSSH 6.2

# algorithm recommendations (for OpenSSH 9.1)
(rec) +aes128-ctr                         -- enc algorithm to append 
(rec) +aes128-gcm@openssh.com             -- enc algorithm to append 
(rec) +aes192-ctr                         -- enc algorithm to append 
(rec) +aes256-ctr                         -- enc algorithm to append 
(rec) +aes256-gcm@openssh.com             -- enc algorithm to append 
(rec) +curve25519-sha256                  -- kex algorithm to append 
(rec) +curve25519-sha256@libssh.org       -- kex algorithm to append 
(rec) +diffie-hellman-group-exchange-sha256-- kex algorithm to append 
(rec) +diffie-hellman-group14-sha256      -- kex algorithm to append 
(rec) +diffie-hellman-group16-sha512      -- kex algorithm to append 
(rec) +diffie-hellman-group18-sha512      -- kex algorithm to append 
(rec) +hmac-sha2-256-etm@openssh.com      -- mac algorithm to append 
(rec) +rsa-sha2-256                       -- key algorithm to append 
(rec) +rsa-sha2-512                       -- key algorithm to append 
(rec) +umac-128-etm@openssh.com           -- mac algorithm to append 
(rec) -sntrup761x25519-sha512@openssh.com -- kex algorithm to remove 

# additional info
(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>
jtesta commented 1 year ago

Thanks for posting the output. I see what you're referring to.

The tool's recommendations section is simply saying what safe additional algorithms could be enabled on the server. I suppose this messaging could be refined and made more clear. For example, the tool can recommend that unsafe algorithms are removed, then use another section to suggest additional safe algorithms.

If anyone wants to work on the PR for this functionality, its up for grabs!

oam7575 commented 7 months ago

I have had a look at this on and off for a little while and have made zero progress.

I did try to add a few checks in the area of the def _get_cbc_ciphers_enabled(algs: Algorithms) -> List[str]:" ; and def _get_etm_macs_enabled(algs: Algorithms) -> List[str]:

While these checks certainly suppressed any weaker recommendations they also suppressed removal prompts ( not good ) ; and were very fragile as they relied on "bit checking" in the cipher name ( eg : check 128 exists in aes128-ctr ).

This would of tend to break down whenever checking a something with different names; EG : Curve25519SHA256 vs diffie-hellman-group16-sha256

I had another go attempting to update the ssh2_kexdb.py file with a numerical value to each ENC / MAC / KEX and KEY. Something like so:

'aes128-ctr': [['3.7,d0.52,l10.4.1'], [], [], [], [20]], 'aes192-ctr': [['3.7,l10.4.1'], [], [], [], [200]], 'aes256-ctr': [['3.7,d0.52,l10.4.1'], [], [], [], [2000]],

The thought was to then to A) extract that value for ciphers / keys etc that the server is currently using. ( ValueA ) B) compare that value to can ciphers / keys that would be recommended. ( ValueB )

If (Value B) is smaller than (Value A) DONT recommend that cipher.

This is were I became stuck as (I am not a very well versed programmer ) and am having trouble following the code.

oam7575 commented 7 months ago

The spaghetti below attempts to explain my thoughts from my previous comment.

In the example code it uses the SSH2_KexDB as provided from the repo. I have modified it with weight values as shown in my previous comment.

This version of the code returns two algorithm's with the largest weight numbers - it seemed safer than only returning 1x.

Notes: all_algs and suggested_algs are place holder for easier testing.

aes64-ctr and others are garbage created for testing.

If I can manage to make something work that looks sensible I will create a pull request.

Feel free to use / modify / destroy as you see fit.

CAUTION : Might eat your cat.

from ssh2_kexdb import SSH2_KexDB

db = SSH2_KexDB.get_db()

all_algs = [('aes64-ctr', 5), ('aes128-ctr', 20), ('aes192-ctr', 200), ('aes256-ctr', 2000), ('aes256-gcm@openssh.com', 2500)]
suggest_algs = [('aes256-gcm@openssh.com', 2500), ('aes64-ctr', 5), ('aes128-ctr', 20), ('aes192-ctr', 200), ('aes256-ctr', 2000)]

enabled_algs = ['aes128-ctr', 'aes192-ctr']

check_list = []
check_list_weight = []
suggest_list = []

largest_suggest = None
second_largest_suggest = None

# Iterate through the SSH2_KEXDB 
# Format First (kex, key, enc, mac )
for format in db:
    # Iterate alg per format
    for algorithm_name in db[format]:
        # If we match an enabled alg vs a database record grab the details
        for alg in enabled_algs:
            if algorithm_name == alg:
                # Stuff everything into variables
                # weight is the new prefered_weight of an alg
                ver, fail, warn, info, weight = db[format][algorithm_name]

                # Convert the weight to an int so we can use it later.
                weight = int(weight[0])

                # append to a list of enabled algs
                # we will need seperate lists for each format key, kex, mac
                check_list.append((alg))

                # For this format put all weights into their own list.
                # will need seperate lists for each format
                check_list_weight.append(weight)

# Iterate over our list of potential algs to suggest
for suggested_alg in suggest_algs:
    # Extract their name and weight value
    suggested_name = suggested_alg[0]
    suggested_weight = suggested_alg[1]

    # Find the two largest values
    if largest_suggest is None or int(suggested_weight) > largest_suggest:
        second_largest_suggest = largest_suggest
        largest_suggest = suggested_weight
    elif (second_largest_suggest is None) or (int(suggested_weight) > second_largest_suggest and int(suggested_weight) != largest_suggest):
        second_largest_suggest = int(suggested_weight)

# Iterate the list a second time and if the alg weight matches either the 
# largest or second largest number then add to the suggested list.
for suggested_alg in suggest_algs:
    # Extract their name and weight value
    suggested_name = suggested_alg[0]
    suggested_weight = suggested_alg[1]

    if suggested_weight == second_largest_suggest and suggested_name not in suggest_list and suggested_name not in enabled_algs:
        suggest_list.append(suggested_name)
    elif suggested_weight == largest_suggest and suggested_name not in suggest_list and suggested_name not in enabled_algs:
        suggest_list.append(suggested_name)

print(suggest_list)