1
0
mirror of https://github.com/jtesta/ssh-audit.git synced 2025-07-06 14:02:49 -05:00

39 Commits

Author SHA1 Message Date
dc36622b50 Bumped version to v2.1.1. 2019-11-26 11:48:18 -05:00
8e0b83176a Updated ChangeLog. Added link to hardening guide. 2019-11-26 11:47:35 -05:00
a16eb2d6cb Added three new PuTTY vulns. 2019-11-18 22:08:17 -05:00
2848c1fb16 Added two new ciphers: 'des', and '3des'. 2019-11-18 20:22:12 -05:00
2cff202b32 Added two new host key types: 'rsa-sha2-256-cert-v01@openssh.com' and 'rsa-sha2-512-cert-v01@openssh.com'. 2019-11-14 16:45:40 -05:00
dae92513fd During client tests, client IP is now listed in output. 2019-11-14 13:52:36 -05:00
e101e22720 Bumped version number. 2019-11-14 11:07:16 -05:00
b3a46e8318 Added pypi notes. 2019-11-14 11:06:05 -05:00
3606863ebf Updated client auditing screen shot. 2019-11-14 10:15:30 -05:00
cf1e069db4 Updated version to 2.1.0. 2019-11-14 08:56:43 -05:00
1e1220807f Updated README with client auditing screen shot and credit for JSON output code. 2019-11-14 08:56:14 -05:00
0263769243 Added JSON output tests to docker testing suite. 2019-11-08 18:40:32 -05:00
e6c31ee4f5 Added JSON output to ChangeLog. 2019-11-08 11:10:41 -05:00
14e0ed0e00 Fixed minor whitespace issue. 2019-11-08 11:09:00 -05:00
0f21f2131c Merge pull request #12 from x-way/json_output
RFC: JSON output
2019-11-08 11:06:09 -05:00
b6c64d296b Add --json output option 2019-11-07 22:08:09 +01:00
1ec13c653e A timeout can now be specified when auditing client connections. 2019-11-06 20:40:25 -05:00
a401afd099 Merged arthepsy/ssh-audit#47 2019-10-25 11:48:02 -04:00
8a3ae321f1 Added five kex algorithms: gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g==, gss-group14-sha1-, gss-group14-sha1-toWM5Slw5Ew8Mqkay+al2g==, gss-group14-sha256-toWM5Slw5Ew8Mqkay+al2g==, gss-group15-sha512-toWM5Slw5Ew8Mqkay+al2g==; added four ciphers: idea-cbc, serpent128-cbc, serpent192-cbc, serpent256-cbc; added four MACs: hmac-ripemd, hmac-sha256-96@ssh.com, umac-32@openssh.com, umac-96@openssh.com. 2019-10-25 11:27:22 -04:00
e62b548677 Updated info on curve25519-sha256 kex. 2019-10-21 11:50:23 -04:00
fd85e247e7 Improved IPv4/IPv6 error handling during client testing. 2019-10-10 23:09:45 -04:00
e3a59a3e21 Client auditing feature now supports IPv6. 2019-10-09 22:29:56 -04:00
4ebefdf894 Updated usage message. 2019-10-09 21:12:09 -04:00
83544836c9 Fixed client parsing crash. 2019-10-09 20:57:31 -04:00
4c9b871f5c Removed duplicate MAC. 2019-10-07 11:00:11 -04:00
1d707276d7 Updated README. 2019-10-07 10:59:52 -04:00
166c93ace4 Updated project description to mention client auditing ability. 2019-09-27 18:19:49 -04:00
9759480ae4 Updated ChangeLog. 2019-09-27 18:16:50 -04:00
fd3a1f7d41 Added client audit functionality. (#3) 2019-09-27 18:14:36 -04:00
08677d65b1 Added potential fix for additional crash against Sun_SSH. 2019-09-19 22:25:30 -04:00
8c5493ae3e Added 2 key exchanges (ecdh-sha2-1.3.132.0.10, curve448-sha512), 1 host key type (ecdsa-sha2-1.3.132.0.10), and 2 MACs (hmac-sha2-256-96-etm@openssh.com, hmac-sha2-512-96-etm@openssh.com). 2019-09-19 22:19:26 -04:00
14af53cf04 Updated ChangeLog for v2.1.0. 2019-09-19 20:10:37 -04:00
bbf6204ce1 Add support for Sun_SSH (on Solaris). Add 'gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==' key exchange. 2019-09-19 20:08:10 -04:00
0df63c20ac Updated screen shot. 2019-09-05 18:52:32 -04:00
209bcab427 Added entries to .gitignore. 2019-09-04 15:06:49 -04:00
eac81455a9 Added PyPI package support. 2019-09-04 15:05:07 -04:00
bce9e2b152 Added new KEX: diffie-hellman-group15-sha256. 2019-09-03 20:41:53 -04:00
f5431559ff Bumped version number. 2019-08-29 16:52:38 -04:00
f2e6f1a71c Replace getopt.getopt with getopt.gnu_getopt
Addresses Issue #41, gnu_getopt allows non-option arguments to be intermingled with option arguments whereas getopt stops processing arguments when a non option is found.
2019-06-05 16:19:33 -04:00
32 changed files with 543 additions and 87 deletions

3
.gitignore vendored
View File

@ -6,3 +6,6 @@ venv*/
.coverage* .coverage*
reports/ reports/
.scannerwork/ .scannerwork/
pypi/sshaudit/LICENSE
pypi/sshaudit/README.md
pypi/sshaudit/sshaudit.py

View File

@ -5,10 +5,11 @@
[![codecov](https://codecov.io/gh/arthepsy/ssh-audit/branch/develop/graph/badge.svg)](https://codecov.io/gh/arthepsy/ssh-audit) [![codecov](https://codecov.io/gh/arthepsy/ssh-audit/branch/develop/graph/badge.svg)](https://codecov.io/gh/arthepsy/ssh-audit)
[![Quality Gate](https://sonarqube.com/api/badges/gate?key=arthepsy-github%3Assh-audit%3Adevelop&template=ROUNDED)](https://sq.evolutiongaming.com/dashboard?id=arthepsy-github%3Assh-audit%3Adevelop) [![Quality Gate](https://sonarqube.com/api/badges/gate?key=arthepsy-github%3Assh-audit%3Adevelop&template=ROUNDED)](https://sq.evolutiongaming.com/dashboard?id=arthepsy-github%3Assh-audit%3Adevelop)
--> -->
**ssh-audit** is a tool for ssh server auditing. **ssh-audit** is a tool for ssh server & client configuration auditing.
## Features ## Features
- SSH1 and SSH2 protocol server support; - SSH1 and SSH2 protocol server support;
- analyze SSH client configuration;
- grab banner, recognize device or software and operating system, detect compression; - grab banner, recognize device or software and operating system, detect compression;
- gather key-exchange, host-key, encryption and message authentication code algorithms; - gather key-exchange, host-key, encryption and message authentication code algorithms;
- output algorithm information (available since, removed/disabled, unsafe/weak/legacy, etc); - output algorithm information (available since, removed/disabled, unsafe/weak/legacy, etc);
@ -16,11 +17,11 @@
- output security information (related issues, assigned CVE list, etc); - output security information (related issues, assigned CVE list, etc);
- analyze SSH version compatibility based on algorithm information; - analyze SSH version compatibility based on algorithm information;
- historical information from OpenSSH, Dropbear SSH and libssh; - historical information from OpenSSH, Dropbear SSH and libssh;
- no dependencies, compatible with Python 2.6+, Python 3.x and PyPy; - no dependencies
## Usage ## Usage
``` ```
usage: ssh-audit.py [-1246pbnvlt] <host> usage: ssh-audit.py [-1246pbcnjvlt] <host>
-1, --ssh1 force ssh version 1 only -1, --ssh1 force ssh version 1 only
-2, --ssh2 force ssh version 2 only -2, --ssh2 force ssh version 2 only
@ -28,20 +29,47 @@ usage: ssh-audit.py [-1246pbnvlt] <host>
-6, --ipv6 enable IPv6 (order of precedence) -6, --ipv6 enable IPv6 (order of precedence)
-p, --port=<port> port to connect -p, --port=<port> port to connect
-b, --batch batch output -b, --batch batch output
-c, --client-audit starts a server on port 2222 to audit client
software config (use -p to change port;
use -t to change timeout)
-n, --no-colors disable colors -n, --no-colors disable colors
-j, --json JSON output
-v, --verbose verbose output -v, --verbose verbose output
-l, --level=<level> minimum output level (info|warn|fail) -l, --level=<level> minimum output level (info|warn|fail)
-t, --timeout=<secs> timeout (in seconds) for connection and reading -t, --timeout=<secs> timeout (in seconds) for connection and reading
(default: 5) (default: 5)
``` ```
* if both IPv4 and IPv6 are used, order of precedence can be set by using either `-46` or `-64`. * if both IPv4 and IPv6 are used, order of precedence can be set by using either `-46` or `-64`.
* batch flag `-b` will output sections without header and without empty lines (implies verbose flag). * batch flag `-b` will output sections without header and without empty lines (implies verbose flag).
* verbose flag `-v` will prefix each line with section type and algorithm name. * verbose flag `-v` will prefix each line with section type and algorithm name.
### Example ### Server Audit Example
![screenshot](https://user-images.githubusercontent.com/2982011/63970414-104bdb00-ca72-11e9-832f-3e535be32811.png) Below is a screen shot of the server-auditing output when connecting to an unhardened OpenSSH v5.3 service:
![screenshot](https://user-images.githubusercontent.com/2982011/64388792-317e6f80-d00e-11e9-826e-a4934769bb07.png)
### Client Audit Example
Below is a screen shot of the client-auditing output when an unhardened OpenSSH v7.2 client connects:
![client_screenshot](https://user-images.githubusercontent.com/2982011/68867998-b946c100-06c4-11ea-975f-1f47e4178a74.png)
### Hardening Guides
Guides to harden server & client configuration can be found here: [https://www.ssh-audit.com/hardening_guides.html](https://www.ssh-audit.com/hardening_guides.html)
## ChangeLog ## ChangeLog
### v2.1.1 (2019-11-26)
- Added 2 new host key types: `rsa-sha2-256-cert-v01@openssh.com`, `rsa-sha2-512-cert-v01@openssh.com`.
- Added 2 new ciphers: `des`, `3des`.
- Added 3 new PuTTY vulnerabilities.
- During client testing, client IP address is now listed in output.
### v2.1.0 (2019-11-14)
- Added client software auditing functionality (see `-c` / `--client-audit` option).
- Added JSON output option (see `-j` / `--json` option; credit [Andreas Jaggi](https://github.com/x-way)).
- Fixed crash while scanning Solaris Sun_SSH.
- Added 9 new key exchanges: `gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==`, `gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g==`, `gss-group14-sha1-`, `gss-group14-sha1-toWM5Slw5Ew8Mqkay+al2g==`, `gss-group14-sha256-toWM5Slw5Ew8Mqkay+al2g==`, `gss-group15-sha512-toWM5Slw5Ew8Mqkay+al2g==`, `diffie-hellman-group15-sha256`, `ecdh-sha2-1.3.132.0.10`, `curve448-sha512`.
- Added 1 new host key type: `ecdsa-sha2-1.3.132.0.10`.
- Added 4 new ciphers: `idea-cbc`, `serpent128-cbc`, `serpent192-cbc`, `serpent256-cbc`.
- Added 6 new MACs: `hmac-sha2-256-96-etm@openssh.com`, `hmac-sha2-512-96-etm@openssh.com`, `hmac-ripemd`, `hmac-sha256-96@ssh.com`, `umac-32@openssh.com`, `umac-96@openssh.com`.
### v2.0.0 (2019-08-29) ### v2.0.0 (2019-08-29)
- Forked from https://github.com/arthepsy/ssh-audit (development was stalled, and developer went MIA). - Forked from https://github.com/arthepsy/ssh-audit (development was stalled, and developer went MIA).
- Added RSA host key length test. - Added RSA host key length test.
@ -51,7 +79,7 @@ usage: ssh-audit.py [-1246pbnvlt] <host>
- Added 5 new key exchanges: `sntrup4591761x25519-sha512@tinyssh.org`, `diffie-hellman-group-exchange-sha256@ssh.com`, `diffie-hellman-group-exchange-sha512@ssh.com`, `diffie-hellman-group16-sha256`, `diffie-hellman-group17-sha512`. - Added 5 new key exchanges: `sntrup4591761x25519-sha512@tinyssh.org`, `diffie-hellman-group-exchange-sha256@ssh.com`, `diffie-hellman-group-exchange-sha512@ssh.com`, `diffie-hellman-group16-sha256`, `diffie-hellman-group17-sha512`.
- Added 3 new encryption algorithms: `des-cbc-ssh1`, `blowfish-ctr`, `twofish-ctr`. - Added 3 new encryption algorithms: `des-cbc-ssh1`, `blowfish-ctr`, `twofish-ctr`.
- Added 10 new MACs: `hmac-sha2-56`, `hmac-sha2-224`, `hmac-sha2-384`, `hmac-sha3-256`, `hmac-sha3-384`, `hmac-sha3-512`, `hmac-sha256`, `hmac-sha256@ssh.com`, `hmac-sha512`, `hmac-512@ssh.com`. - Added 10 new MACs: `hmac-sha2-56`, `hmac-sha2-224`, `hmac-sha2-384`, `hmac-sha3-256`, `hmac-sha3-384`, `hmac-sha3-512`, `hmac-sha256`, `hmac-sha256@ssh.com`, `hmac-sha512`, `hmac-512@ssh.com`.
- Added command line argument (-t / --timeout) for connection & reading timeouts. - Added command line argument (`-t` / `--timeout`) for connection & reading timeouts.
- Updated CVEs for libssh & Dropbear. - Updated CVEs for libssh & Dropbear.
### v1.7.0 (2016-10-26) ### v1.7.0 (2016-10-26)

View File

@ -385,24 +385,32 @@ function run_test {
options=$4 options=$4
server_exec= server_exec=
test_result= test_result_stdout=
expected_result= test_result_json=
expected_result_stdout=
expected_result_json=
test_name= test_name=
if [[ $server_type == 'OpenSSH' ]]; then if [[ $server_type == 'OpenSSH' ]]; then
server_exec="/openssh/sshd-${version} -D -f /etc/ssh/sshd_config-${version}_${test_number}" server_exec="/openssh/sshd-${version} -D -f /etc/ssh/sshd_config-${version}_${test_number}"
test_result="${TEST_RESULT_DIR}/openssh_${version}_${test_number}.txt" test_result_stdout="${TEST_RESULT_DIR}/openssh_${version}_${test_number}.txt"
expected_result="test/docker/expected_results/openssh_${version}_${test_number}.txt" test_result_json="${TEST_RESULT_DIR}/openssh_${version}_${test_number}.json"
expected_result_stdout="test/docker/expected_results/openssh_${version}_${test_number}.txt"
expected_result_json="test/docker/expected_results/openssh_${version}_${test_number}.json"
test_name="OpenSSH ${version} ${test_number}" test_name="OpenSSH ${version} ${test_number}"
options= options=
elif [[ $server_type == 'Dropbear' ]]; then elif [[ $server_type == 'Dropbear' ]]; then
server_exec="/dropbear/dropbear-${version} -F ${options}" server_exec="/dropbear/dropbear-${version} -F ${options}"
test_result="${TEST_RESULT_DIR}/dropbear_${version}_${test_number}.txt" test_result_stdout="${TEST_RESULT_DIR}/dropbear_${version}_${test_number}.txt"
expected_result="test/docker/expected_results/dropbear_${version}_${test_number}.txt" test_result_json="${TEST_RESULT_DIR}/dropbear_${version}_${test_number}.json"
expected_result_stdout="test/docker/expected_results/dropbear_${version}_${test_number}.txt"
expected_result_json="test/docker/expected_results/dropbear_${version}_${test_number}.json"
test_name="Dropbear ${version} ${test_number}" test_name="Dropbear ${version} ${test_number}"
elif [[ $server_type == 'TinySSH' ]]; then elif [[ $server_type == 'TinySSH' ]]; then
server_exec="/usr/bin/tcpserver -HRDl0 0.0.0.0 22 /tinysshd/tinyssh-20190101 -v /etc/tinyssh/" server_exec="/usr/bin/tcpserver -HRDl0 0.0.0.0 22 /tinysshd/tinyssh-20190101 -v /etc/tinyssh/"
test_result="${TEST_RESULT_DIR}/tinyssh_${version}_${test_number}.txt" test_result_stdout="${TEST_RESULT_DIR}/tinyssh_${version}_${test_number}.txt"
expected_result="test/docker/expected_results/tinyssh_${version}_${test_number}.txt" test_result_json="${TEST_RESULT_DIR}/tinyssh_${version}_${test_number}.json"
expected_result_stdout="test/docker/expected_results/tinyssh_${version}_${test_number}.txt"
expected_result_json="test/docker/expected_results/tinyssh_${version}_${test_number}.json"
test_name="TinySSH ${version} ${test_number}" test_name="TinySSH ${version} ${test_number}"
fi fi
@ -412,7 +420,14 @@ function run_test {
exit 1 exit 1
fi fi
./ssh-audit.py localhost:2222 > $test_result ./ssh-audit.py localhost:2222 > $test_result_stdout
if [[ $? != 0 ]]; then
echo -e "${REDB}Failed to run ssh-audit.py! (exit code: $?)${CLR}"
docker container stop $cid > /dev/null
exit 1
fi
./ssh-audit.py -j localhost:2222 > $test_result_json
if [[ $? != 0 ]]; then if [[ $? != 0 ]]; then
echo -e "${REDB}Failed to run ssh-audit.py! (exit code: $?)${CLR}" echo -e "${REDB}Failed to run ssh-audit.py! (exit code: $?)${CLR}"
docker container stop $cid > /dev/null docker container stop $cid > /dev/null
@ -429,19 +444,25 @@ function run_test {
# we need to filter out the banner part of the output so we get stable, repeatable # we need to filter out the banner part of the output so we get stable, repeatable
# results. # results.
if [[ $server_type == 'TinySSH' ]]; then if [[ $server_type == 'TinySSH' ]]; then
grep -v "(gen) banner: " ${test_result} > "${test_result}.tmp" grep -v "(gen) banner: " ${test_result_stdout} > "${test_result_stdout}.tmp"
mv "${test_result}.tmp" ${test_result} mv "${test_result_stdout}.tmp" ${test_result_stdout}
cat "${test_result_json}" | perl -pe 's/"comments": ".*?"/"comments": ""/' | perl -pe 's/"raw": ".+?"/"raw": ""/' > "${test_result_json}.tmp"
mv "${test_result_json}.tmp" ${test_result_json}
fi fi
diff=`diff -u ${expected_result} ${test_result}` diff=`diff -u ${expected_result_stdout} ${test_result_stdout}`
if [[ $? == 0 ]]; then if [[ $? != 0 ]]; then
echo -e "${test_name} ${GREEN}passed${CLR}."
else
echo -e "${test_name} ${REDB}FAILED${CLR}.\n\n${diff}\n" echo -e "${test_name} ${REDB}FAILED${CLR}.\n\n${diff}\n"
exit 1 exit 1
fi fi
}
diff=`diff -u ${expected_result_json} ${test_result_json}`
if [[ $? != 0 ]]; then
echo -e "${test_name} ${REDB}FAILED${CLR}.\n\n${diff}\n"
exit 1
fi
echo -e "${test_name} ${GREEN}passed${CLR}."
}
# First check if docker is functional. # First check if docker is functional.

1
pypi/MANIFEST.in Normal file
View File

@ -0,0 +1 @@
include sshaudit/LICENSE

14
pypi/Makefile Normal file
View File

@ -0,0 +1,14 @@
all:
cp ../ssh-audit.py sshaudit/sshaudit.py
cp ../LICENSE sshaudit/LICENSE
cp ../README.md sshaudit/README.md
python3 setup.py sdist bdist_wheel
uploadtest:
twine upload --repository-url https://test.pypi.org/legacy/ dist/*
uploadprod:
twine upload dist/*
clean:
rm -rf build/ dist/ *.egg-info/ sshaudit/sshaudit.py sshaudit/LICENSE sshaudit/README.md

17
pypi/notes.txt Normal file
View File

@ -0,0 +1,17 @@
To create package and upload to test server:
# apt install virtualenv
$ virtualenv -p /usr/bin/python3 pypi_upload
$ cd pypi_upload; source bin/activate
$ pip3 install twine
$ cp -R path/to/ssh-audit .
$ cd ssh-audit/pypi
$ make
$ make uploadtest
To download from test server and verify:
$ virtualenv -p /usr/bin/python3 pypi_test
$ cd pypi_test; source bin/activate
$ pip3 install --index-url https://test.pypi.org/simple ssh-audit

38
pypi/setup.py Normal file
View File

@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
import re
from setuptools import setup
version = re.search('^VERSION\s*=\s*\'v(\d\.\d\.\d)\'', open('sshaudit/sshaudit.py').read(), re.M).group(1)
print("\n\nPackaging ssh-audit v%s...\n\n" % version)
with open("sshaudit/README.md", "rb") as f:
long_descr = f.read().decode("utf-8")
setup(
name = "ssh-audit",
packages = ["sshaudit"],
license = 'MIT',
entry_points = {
"console_scripts": ['ssh-audit = sshaudit.sshaudit:main']
},
version = version,
description = "An SSH server configuration security auditing tool",
long_description = long_descr,
long_description_content_type = "text/markdown",
author = "Joe Testa",
author_email = "jtesta@positronsecurity.com",
url = "https://github.com/jtesta/ssh-audit",
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Information Technology",
"Intended Audience :: System Administrators",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Topic :: Security",
"Topic :: Security :: Cryptography"
])

View File

View File

@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
from .sshaudit import main
main()

View File

@ -25,9 +25,10 @@
THE SOFTWARE. THE SOFTWARE.
""" """
from __future__ import print_function from __future__ import print_function
import binascii, os, io, sys, socket, struct, random, errno, getopt, re, hashlib, base64 import base64, binascii, errno, hashlib, getopt, io, os, random, re, select, socket, struct, sys, json
VERSION = 'v2.0.0' VERSION = 'v2.1.1'
SSH_HEADER = 'SSH-{0}-OpenSSH_8.0' # SSH software to impersonate
if sys.version_info.major < 3: if sys.version_info.major < 3:
print("\n!!!! NOTE: Python 2 is being considered for deprecation. If you have a good reason to need continued Python 2 support, please e-mail jtesta@positronsecurity.com with your rationale.\n\n") print("\n!!!! NOTE: Python 2 is being considered for deprecation. If you have a good reason to need continued Python 2 support, please e-mail jtesta@positronsecurity.com with your rationale.\n\n")
@ -61,7 +62,7 @@ def usage(err=None):
uout.head('# {0} {1}, https://github.com/jtesta/ssh-audit\n'.format(p, VERSION)) uout.head('# {0} {1}, https://github.com/jtesta/ssh-audit\n'.format(p, VERSION))
if err is not None and len(err) > 0: if err is not None and len(err) > 0:
uout.fail('\n' + err) uout.fail('\n' + err)
uout.info('usage: {0} [-1246pbnvlt] <host>\n'.format(p)) uout.info('usage: {0} [-1246pbcnjvlt] <host>\n'.format(p))
uout.info(' -h, --help print this help') uout.info(' -h, --help print this help')
uout.info(' -1, --ssh1 force ssh version 1 only') uout.info(' -1, --ssh1 force ssh version 1 only')
uout.info(' -2, --ssh2 force ssh version 2 only') uout.info(' -2, --ssh2 force ssh version 2 only')
@ -69,10 +70,12 @@ def usage(err=None):
uout.info(' -6, --ipv6 enable IPv6 (order of precedence)') uout.info(' -6, --ipv6 enable IPv6 (order of precedence)')
uout.info(' -p, --port=<port> port to connect') uout.info(' -p, --port=<port> port to connect')
uout.info(' -b, --batch batch output') uout.info(' -b, --batch batch output')
uout.info(' -c, --client-audit starts a server on port 2222 to audit client\n software config (use -p to change port;\n use -t to change timeout)')
uout.info(' -n, --no-colors disable colors') uout.info(' -n, --no-colors disable colors')
uout.info(' -j, --json JSON output')
uout.info(' -v, --verbose verbose output') uout.info(' -v, --verbose verbose output')
uout.info(' -l, --level=<level> minimum output level (info|warn|fail)') uout.info(' -l, --level=<level> minimum output level (info|warn|fail)')
uout.info(' -t, --timeout=<secs> timeout (in seconds) for connection and reading\n (default: 5)') uout.info(' -t, --timeout=<secs> timeout (in seconds) for connection and reading\n (default: 5)')
uout.sep() uout.sep()
sys.exit(1) sys.exit(1)
@ -86,18 +89,21 @@ class AuditConf(object):
self.ssh1 = True self.ssh1 = True
self.ssh2 = True self.ssh2 = True
self.batch = False self.batch = False
self.client_audit = False
self.colors = True self.colors = True
self.json = False
self.verbose = False self.verbose = False
self.level = 'info' self.level = 'info'
self.ipvo = () # type: Sequence[int] self.ipvo = () # type: Sequence[int]
self.ipv4 = False self.ipv4 = False
self.ipv6 = False self.ipv6 = False
self.timeout = 5.0 self.timeout = 5.0
self.timeout_set = False # Set to True when the user explicitly sets it.
def __setattr__(self, name, value): def __setattr__(self, name, value):
# type: (str, Union[str, int, bool, Sequence[int]]) -> None # type: (str, Union[str, int, bool, Sequence[int]]) -> None
valid = False valid = False
if name in ['ssh1', 'ssh2', 'batch', 'colors', 'verbose']: if name in ['ssh1', 'ssh2', 'batch', 'client_audit', 'colors', 'verbose', 'timeout_set', 'json']:
valid, value = True, True if bool(value) else False valid, value = True, True if bool(value) else False
elif name in ['ipv4', 'ipv6']: elif name in ['ipv4', 'ipv6']:
valid = False valid = False
@ -144,10 +150,10 @@ class AuditConf(object):
# pylint: disable=too-many-branches # pylint: disable=too-many-branches
aconf = cls() aconf = cls()
try: try:
sopts = 'h1246p:bnvl:t:' sopts = 'h1246p:bcnjvl:t:'
lopts = ['help', 'ssh1', 'ssh2', 'ipv4', 'ipv6', 'port', lopts = ['help', 'ssh1', 'ssh2', 'ipv4', 'ipv6', 'port', 'json',
'batch', 'no-colors', 'verbose', 'level=', 'timeout='] 'batch', 'client-audit', 'no-colors', 'verbose', 'level=', 'timeout=']
opts, args = getopt.getopt(args, sopts, lopts) opts, args = getopt.gnu_getopt(args, sopts, lopts)
except getopt.GetoptError as err: except getopt.GetoptError as err:
usage_cb(str(err)) usage_cb(str(err))
aconf.ssh1, aconf.ssh2 = False, False aconf.ssh1, aconf.ssh2 = False, False
@ -168,8 +174,12 @@ class AuditConf(object):
elif o in ('-b', '--batch'): elif o in ('-b', '--batch'):
aconf.batch = True aconf.batch = True
aconf.verbose = True aconf.verbose = True
elif o in ('-c', '--client-audit'):
aconf.client_audit = True
elif o in ('-n', '--no-colors'): elif o in ('-n', '--no-colors'):
aconf.colors = False aconf.colors = False
elif o in ('-j', '--json'):
aconf.json = True
elif o in ('-v', '--verbose'): elif o in ('-v', '--verbose'):
aconf.verbose = True aconf.verbose = True
elif o in ('-l', '--level'): elif o in ('-l', '--level'):
@ -178,23 +188,29 @@ class AuditConf(object):
aconf.level = a aconf.level = a
elif o in ('-t', '--timeout'): elif o in ('-t', '--timeout'):
aconf.timeout = float(a) aconf.timeout = float(a)
if len(args) == 0: aconf.timeout_set = True
if len(args) == 0 and aconf.client_audit == False:
usage_cb() usage_cb()
if oport is not None: if aconf.client_audit == False:
host = args[0] if oport is not None:
else: host = args[0]
mx = re.match(r'^\[([^\]]+)\](?::(.*))?$', args[0])
if bool(mx):
host, oport = mx.group(1), mx.group(2)
else: else:
s = args[0].split(':') mx = re.match(r'^\[([^\]]+)\](?::(.*))?$', args[0])
if len(s) > 2: if bool(mx):
host, oport = args[0], '22' host, oport = mx.group(1), mx.group(2)
else: else:
host, oport = s[0], s[1] if len(s) > 1 else '22' s = args[0].split(':')
if len(s) > 2:
host, oport = args[0], '22'
else:
host, oport = s[0], s[1] if len(s) > 1 else '22'
if not host:
usage_cb('host is empty')
else:
host = None
if oport is None:
oport = '2222'
port = utils.parse_int(oport) port = utils.parse_int(oport)
if not host:
usage_cb('host is empty')
if port <= 0 or port > 65535: if port <= 0 or port > 65535:
usage_cb('port {0} is not valid'.format(oport)) usage_cb('port {0} is not valid'.format(oport))
aconf.host = host aconf.host = host
@ -213,6 +229,7 @@ class Output(object):
self.batch = False self.batch = False
self.verbose = False self.verbose = False
self.use_colors = True self.use_colors = True
self.json = False
self.__level = 0 self.__level = 0
self.__colsupport = 'colorama' in sys.modules or os.name == 'posix' self.__colsupport = 'colorama' in sys.modules or os.name == 'posix'
@ -303,6 +320,7 @@ class SSH2(object): # pylint: disable=too-few-public-methods
FAIL_DEPRECATED_CIPHER = 'deprecated cipher' FAIL_DEPRECATED_CIPHER = 'deprecated cipher'
FAIL_WEAK_CIPHER = 'using weak cipher' FAIL_WEAK_CIPHER = 'using weak cipher'
FAIL_PLAINTEXT = 'no encryption/integrity' FAIL_PLAINTEXT = 'no encryption/integrity'
FAIL_DEPRECATED_MAC = 'deprecated MAC'
WARN_CURVES_WEAK = 'using weak elliptic curves' WARN_CURVES_WEAK = 'using weak elliptic curves'
WARN_RNDSIG_KEY = 'using weak random number generator could reveal the key' WARN_RNDSIG_KEY = 'using weak random number generator could reveal the key'
WARN_MODULUS_SIZE = 'using small 1024-bit modulus' WARN_MODULUS_SIZE = 'using small 1024-bit modulus'
@ -312,14 +330,22 @@ class SSH2(object): # pylint: disable=too-few-public-methods
WARN_CIPHER_WEAK = 'using weak cipher' WARN_CIPHER_WEAK = 'using weak cipher'
WARN_ENCRYPT_AND_MAC = 'using encrypt-and-MAC mode' WARN_ENCRYPT_AND_MAC = 'using encrypt-and-MAC mode'
WARN_TAG_SIZE = 'using small 64-bit tag size' WARN_TAG_SIZE = 'using small 64-bit tag size'
WARN_TAG_SIZE_96 = 'using small 96-bit tag size'
WARN_EXPERIMENTAL = 'using experimental algorithm' WARN_EXPERIMENTAL = 'using experimental algorithm'
ALGORITHMS = { ALGORITHMS = {
# Format: 'algorithm_name': [['version_first_appeared_in'], [reason_for_failure1, reason_for_failure2, ...], [warning1, warning2, ...]] # Format: 'algorithm_name': [['version_first_appeared_in'], [reason_for_failure1, reason_for_failure2, ...], [warning1, warning2, ...]]
'kex': { 'kex': {
'diffie-hellman-group1-sha1': [['2.3.0,d0.28,l10.2', '6.6', '6.9'], [FAIL_OPENSSH67_UNSAFE, FAIL_OPENSSH70_LOGJAM], [WARN_MODULUS_SIZE, WARN_HASH_WEAK]], 'diffie-hellman-group1-sha1': [['2.3.0,d0.28,l10.2', '6.6', '6.9'], [FAIL_OPENSSH67_UNSAFE, FAIL_OPENSSH70_LOGJAM], [WARN_MODULUS_SIZE, WARN_HASH_WEAK]],
'gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==': [[], [FAIL_OPENSSH67_UNSAFE, FAIL_OPENSSH70_LOGJAM], [WARN_MODULUS_SIZE, WARN_HASH_WEAK]],
'gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g==': [[], [], [WARN_HASH_WEAK]],
'gss-group14-sha1-': [[], [], [WARN_HASH_WEAK]],
'gss-group14-sha1-toWM5Slw5Ew8Mqkay+al2g==': [[], [], [WARN_HASH_WEAK]],
'gss-group14-sha256-toWM5Slw5Ew8Mqkay+al2g==': [[]],
'gss-group15-sha512-toWM5Slw5Ew8Mqkay+al2g==': [[]],
'diffie-hellman-group14-sha1': [['3.9,d0.53,l10.6.0'], [], [WARN_HASH_WEAK]], 'diffie-hellman-group14-sha1': [['3.9,d0.53,l10.6.0'], [], [WARN_HASH_WEAK]],
'diffie-hellman-group14-sha256': [['7.3,d2016.73']], 'diffie-hellman-group14-sha256': [['7.3,d2016.73']],
'diffie-hellman-group15-sha256': [[]],
'diffie-hellman-group15-sha512': [[]], 'diffie-hellman-group15-sha512': [[]],
'diffie-hellman-group16-sha256': [[]], 'diffie-hellman-group16-sha256': [[]],
'diffie-hellman-group16-sha512': [['7.3,d2016.73']], 'diffie-hellman-group16-sha512': [['7.3,d2016.73']],
@ -332,12 +358,16 @@ class SSH2(object): # pylint: disable=too-few-public-methods
'ecdh-sha2-nistp256': [['5.7,d2013.62,l10.6.0'], [WARN_CURVES_WEAK]], 'ecdh-sha2-nistp256': [['5.7,d2013.62,l10.6.0'], [WARN_CURVES_WEAK]],
'ecdh-sha2-nistp384': [['5.7,d2013.62'], [WARN_CURVES_WEAK]], 'ecdh-sha2-nistp384': [['5.7,d2013.62'], [WARN_CURVES_WEAK]],
'ecdh-sha2-nistp521': [['5.7,d2013.62'], [WARN_CURVES_WEAK]], 'ecdh-sha2-nistp521': [['5.7,d2013.62'], [WARN_CURVES_WEAK]],
'ecdh-sha2-1.3.132.0.10': [[]], # ECDH over secp256k1 (i.e.: the Bitcoin curve)
'curve25519-sha256@libssh.org': [['6.5,d2013.62,l10.6.0']], 'curve25519-sha256@libssh.org': [['6.5,d2013.62,l10.6.0']],
'curve25519-sha256': [['7.4']], 'curve25519-sha256': [['7.4,d2018.76']],
'curve448-sha512': [[]],
'kexguess2@matt.ucc.asn.au': [['d2013.57']], 'kexguess2@matt.ucc.asn.au': [['d2013.57']],
'rsa1024-sha1': [[], [], [WARN_MODULUS_SIZE, WARN_HASH_WEAK]], 'rsa1024-sha1': [[], [], [WARN_MODULUS_SIZE, WARN_HASH_WEAK]],
'rsa2048-sha256': [[]], 'rsa2048-sha256': [[]],
'sntrup4591761x25519-sha512@tinyssh.org': [['8.0'], [], [WARN_EXPERIMENTAL]], 'sntrup4591761x25519-sha512@tinyssh.org': [['8.0'], [], [WARN_EXPERIMENTAL]],
'ext-info-c': [[]], # Extension negotiation (RFC 8308)
'ext-info-s': [[]], # Extension negotiation (RFC 8308)
}, },
'key': { 'key': {
'rsa-sha2-256': [['7.2']], 'rsa-sha2-256': [['7.2']],
@ -356,12 +386,17 @@ class SSH2(object): # pylint: disable=too-few-public-methods
'ecdsa-sha2-nistp256-cert-v01@openssh.com': [['5.7'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]], 'ecdsa-sha2-nistp256-cert-v01@openssh.com': [['5.7'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
'ecdsa-sha2-nistp384-cert-v01@openssh.com': [['5.7'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]], 'ecdsa-sha2-nistp384-cert-v01@openssh.com': [['5.7'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
'ecdsa-sha2-nistp521-cert-v01@openssh.com': [['5.7'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]], 'ecdsa-sha2-nistp521-cert-v01@openssh.com': [['5.7'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
'rsa-sha2-256-cert-v01@openssh.com': [['7.8']],
'rsa-sha2-512-cert-v01@openssh.com': [['7.8']],
'ssh-rsa-sha256@ssh.com': [[]], 'ssh-rsa-sha256@ssh.com': [[]],
'ecdsa-sha2-1.3.132.0.10': [[], [], [WARN_RNDSIG_KEY]], # ECDSA over secp256k1 (i.e.: the Bitcoin curve)
}, },
'enc': { 'enc': {
'none': [['1.2.2,d2013.56,l10.2'], [FAIL_PLAINTEXT]], 'none': [['1.2.2,d2013.56,l10.2'], [FAIL_PLAINTEXT]],
'des': [[], [FAIL_WEAK_CIPHER], [WARN_CIPHER_MODE, WARN_BLOCK_SIZE]],
'des-cbc': [[], [FAIL_WEAK_CIPHER], [WARN_CIPHER_MODE, WARN_BLOCK_SIZE]], 'des-cbc': [[], [FAIL_WEAK_CIPHER], [WARN_CIPHER_MODE, WARN_BLOCK_SIZE]],
'des-cbc-ssh1': [[], [FAIL_WEAK_CIPHER], [WARN_CIPHER_MODE, WARN_BLOCK_SIZE]], 'des-cbc-ssh1': [[], [FAIL_WEAK_CIPHER], [WARN_CIPHER_MODE, WARN_BLOCK_SIZE]],
'3des': [[], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH74_UNSAFE, WARN_CIPHER_WEAK, WARN_CIPHER_MODE, WARN_BLOCK_SIZE]],
'3des-cbc': [['1.2.2,d0.28,l10.2', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH74_UNSAFE, WARN_CIPHER_WEAK, WARN_CIPHER_MODE, WARN_BLOCK_SIZE]], '3des-cbc': [['1.2.2,d0.28,l10.2', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH74_UNSAFE, WARN_CIPHER_WEAK, WARN_CIPHER_MODE, WARN_BLOCK_SIZE]],
'3des-ctr': [['d0.52'], [FAIL_WEAK_CIPHER]], '3des-ctr': [['d0.52'], [FAIL_WEAK_CIPHER]],
'blowfish-cbc': [['1.2.2,d0.28,l10.2', '6.6,d0.52', '7.1,d0.52'], [FAIL_OPENSSH67_UNSAFE, FAIL_DBEAR53_DISABLED], [WARN_OPENSSH72_LEGACY, WARN_CIPHER_MODE, WARN_BLOCK_SIZE]], 'blowfish-cbc': [['1.2.2,d0.28,l10.2', '6.6,d0.52', '7.1,d0.52'], [FAIL_OPENSSH67_UNSAFE, FAIL_DBEAR53_DISABLED], [WARN_OPENSSH72_LEGACY, WARN_CIPHER_MODE, WARN_BLOCK_SIZE]],
@ -374,9 +409,13 @@ class SSH2(object): # pylint: disable=too-few-public-methods
'twofish128-ctr': [['d2015.68']], 'twofish128-ctr': [['d2015.68']],
'twofish192-ctr': [[]], 'twofish192-ctr': [[]],
'twofish256-ctr': [['d2015.68']], 'twofish256-ctr': [['d2015.68']],
'serpent128-cbc': [[], [FAIL_DEPRECATED_CIPHER], [WARN_CIPHER_MODE]],
'serpent192-cbc': [[], [FAIL_DEPRECATED_CIPHER], [WARN_CIPHER_MODE]],
'serpent256-cbc': [[], [FAIL_DEPRECATED_CIPHER], [WARN_CIPHER_MODE]],
'serpent128-ctr': [[], [FAIL_DEPRECATED_CIPHER]], 'serpent128-ctr': [[], [FAIL_DEPRECATED_CIPHER]],
'serpent192-ctr': [[], [FAIL_DEPRECATED_CIPHER]], 'serpent192-ctr': [[], [FAIL_DEPRECATED_CIPHER]],
'serpent256-ctr': [[], [FAIL_DEPRECATED_CIPHER]], 'serpent256-ctr': [[], [FAIL_DEPRECATED_CIPHER]],
'idea-cbc': [[], [FAIL_DEPRECATED_CIPHER], [WARN_CIPHER_MODE]],
'idea-ctr': [[], [FAIL_DEPRECATED_CIPHER]], 'idea-ctr': [[], [FAIL_DEPRECATED_CIPHER]],
'cast128-ctr': [[], [FAIL_DEPRECATED_CIPHER]], 'cast128-ctr': [[], [FAIL_DEPRECATED_CIPHER]],
'cast128-cbc': [['2.1.0', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_CIPHER_MODE, WARN_BLOCK_SIZE]], 'cast128-cbc': [['2.1.0', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_CIPHER_MODE, WARN_BLOCK_SIZE]],
@ -412,23 +451,29 @@ class SSH2(object): # pylint: disable=too-few-public-methods
'hmac-sha3-384': [[], [], [WARN_ENCRYPT_AND_MAC]], 'hmac-sha3-384': [[], [], [WARN_ENCRYPT_AND_MAC]],
'hmac-sha3-512': [[], [], [WARN_ENCRYPT_AND_MAC]], 'hmac-sha3-512': [[], [], [WARN_ENCRYPT_AND_MAC]],
'hmac-sha256': [[], [], [WARN_ENCRYPT_AND_MAC]], 'hmac-sha256': [[], [], [WARN_ENCRYPT_AND_MAC]],
'hmac-sha256-96@ssh.com': [[], [], [WARN_ENCRYPT_AND_MAC, WARN_TAG_SIZE]],
'hmac-sha256@ssh.com': [[], [], [WARN_ENCRYPT_AND_MAC]], 'hmac-sha256@ssh.com': [[], [], [WARN_ENCRYPT_AND_MAC]],
'hmac-sha512': [[], [], [WARN_ENCRYPT_AND_MAC]], 'hmac-sha512': [[], [], [WARN_ENCRYPT_AND_MAC]],
'hmac-sha512@ssh.com': [[], [], [WARN_ENCRYPT_AND_MAC]], 'hmac-sha512@ssh.com': [[], [], [WARN_ENCRYPT_AND_MAC]],
'hmac-md5': [['2.1.0,d0.28', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_ENCRYPT_AND_MAC, WARN_HASH_WEAK]], 'hmac-md5': [['2.1.0,d0.28', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_ENCRYPT_AND_MAC, WARN_HASH_WEAK]],
'hmac-md5-96': [['2.5.0', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_ENCRYPT_AND_MAC, WARN_HASH_WEAK]], 'hmac-md5-96': [['2.5.0', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_ENCRYPT_AND_MAC, WARN_HASH_WEAK]],
'hmac-ripemd': [[], [FAIL_DEPRECATED_MAC], [WARN_OPENSSH72_LEGACY, WARN_ENCRYPT_AND_MAC]],
'hmac-ripemd160': [['2.5.0', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_ENCRYPT_AND_MAC]], 'hmac-ripemd160': [['2.5.0', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_ENCRYPT_AND_MAC]],
'hmac-ripemd160@openssh.com': [['2.1.0', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_ENCRYPT_AND_MAC]], 'hmac-ripemd160@openssh.com': [['2.1.0', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_ENCRYPT_AND_MAC]],
'umac-64@openssh.com': [['4.7'], [], [WARN_ENCRYPT_AND_MAC, WARN_TAG_SIZE]], 'umac-64@openssh.com': [['4.7'], [], [WARN_ENCRYPT_AND_MAC, WARN_TAG_SIZE]],
'umac-128@openssh.com': [['6.2'], [], [WARN_ENCRYPT_AND_MAC]], 'umac-128@openssh.com': [['6.2'], [], [WARN_ENCRYPT_AND_MAC]],
'hmac-sha1-etm@openssh.com': [['6.2'], [], [WARN_HASH_WEAK]], 'hmac-sha1-etm@openssh.com': [['6.2'], [], [WARN_HASH_WEAK]],
'hmac-sha1-96-etm@openssh.com': [['6.2', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_HASH_WEAK]], 'hmac-sha1-96-etm@openssh.com': [['6.2', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_HASH_WEAK]],
'hmac-sha2-256-96-etm@openssh.com': [[], [], [WARN_TAG_SIZE_96]], # Despite the @openssh.com tag, it doesn't appear that this was ever shipped with OpenSSH; it is only implemented in AsyncSSH (?).
'hmac-sha2-512-96-etm@openssh.com': [[], [], [WARN_TAG_SIZE_96]], # Despite the @openssh.com tag, it doesn't appear that this was ever shipped with OpenSSH; it is only implemented in AsyncSSH (?).
'hmac-sha2-256-etm@openssh.com': [['6.2']], 'hmac-sha2-256-etm@openssh.com': [['6.2']],
'hmac-sha2-512-etm@openssh.com': [['6.2']], 'hmac-sha2-512-etm@openssh.com': [['6.2']],
'hmac-md5-etm@openssh.com': [['6.2', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_HASH_WEAK]], 'hmac-md5-etm@openssh.com': [['6.2', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_HASH_WEAK]],
'hmac-md5-96-etm@openssh.com': [['6.2', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_HASH_WEAK]], 'hmac-md5-96-etm@openssh.com': [['6.2', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_HASH_WEAK]],
'hmac-ripemd160-etm@openssh.com': [['6.2', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY]], 'hmac-ripemd160-etm@openssh.com': [['6.2', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY]],
'umac-32@openssh.com': [[], [], [WARN_ENCRYPT_AND_MAC, WARN_TAG_SIZE]], # Despite having the @openssh.com suffix, this may never have shipped with OpenSSH (!).
'umac-64-etm@openssh.com': [['6.2'], [], [WARN_TAG_SIZE]], 'umac-64-etm@openssh.com': [['6.2'], [], [WARN_TAG_SIZE]],
'umac-96@openssh.com': [[], [], [WARN_ENCRYPT_AND_MAC]], # Despite having the @openssh.com suffix, this may never have shipped with OpenSSH (!).
'umac-128-etm@openssh.com': [['6.2']], 'umac-128-etm@openssh.com': [['6.2']],
} }
} # type: Dict[str, Dict[str, List[List[Optional[str]]]]] } # type: Dict[str, Dict[str, List[List[Optional[str]]]]]
@ -1193,6 +1238,7 @@ class SSH(object): # pylint: disable=too-few-public-methods
class Protocol(object): # pylint: disable=too-few-public-methods class Protocol(object): # pylint: disable=too-few-public-methods
# pylint: disable=bad-whitespace # pylint: disable=bad-whitespace
SMSG_PUBLIC_KEY = 2 SMSG_PUBLIC_KEY = 2
MSG_DEBUG = 4
MSG_KEXINIT = 20 MSG_KEXINIT = 20
MSG_NEWKEYS = 21 MSG_NEWKEYS = 21
MSG_KEXDH_INIT = 30 MSG_KEXDH_INIT = 30
@ -1207,6 +1253,7 @@ class SSH(object): # pylint: disable=too-few-public-methods
DropbearSSH = 'Dropbear SSH' DropbearSSH = 'Dropbear SSH'
LibSSH = 'libssh' LibSSH = 'libssh'
TinySSH = 'TinySSH' TinySSH = 'TinySSH'
PuTTY = 'PuTTY'
class Software(object): class Software(object):
def __init__(self, vendor, product, version, patch, os_version): def __init__(self, vendor, product, version, patch, os_version):
@ -1410,6 +1457,9 @@ class SSH(object): # pylint: disable=too-few-public-methods
mx = re.match(r'^tinyssh_(.*)', software) mx = re.match(r'^tinyssh_(.*)', software)
if bool(mx): if bool(mx):
return cls(None, SSH.Product.TinySSH, mx.group(1), None, None) return cls(None, SSH.Product.TinySSH, mx.group(1), None, None)
mx = re.match(r'^PuTTY_Release_(.*)', software)
if bool(mx):
return cls(None, SSH.Product.PuTTY, mx.group(1), None, None)
return None return None
class Banner(object): class Banner(object):
@ -1900,7 +1950,22 @@ class SSH(object): # pylint: disable=too-few-public-methods
['3.0', '3.0p1', 1, 'CVE-2001-1507', 7.5, 'bypass authentication'], ['3.0', '3.0p1', 1, 'CVE-2001-1507', 7.5, 'bypass authentication'],
['1.2.3', '3.0.1p1', 5, 'CVE-2001-0872', 7.2, 'privilege escalation via crafted environment variables'], ['1.2.3', '3.0.1p1', 5, 'CVE-2001-0872', 7.2, 'privilege escalation via crafted environment variables'],
['1.2.3', '2.1.1', 1, 'CVE-2001-0361', 4.0, 'recover plaintext from ciphertext'], ['1.2.3', '2.1.1', 1, 'CVE-2001-0361', 4.0, 'recover plaintext from ciphertext'],
['1.2', '2.1', 1, 'CVE-2000-0525', 10.0, 'execute arbitrary code (improper privileges)']] ['1.2', '2.1', 1, 'CVE-2000-0525', 10.0, 'execute arbitrary code (improper privileges)']],
'PuTTY': [
['0.0', '0.72', 2, 'CVE-2019-17069', 5.0, 'potential DOS by remote SSHv1 server'],
['0.71', '0.72', 2, 'CVE-2019-17068', 5.0, 'xterm bracketed paste mode command injection'],
['0.52', '0.72', 2, 'CVE-2019-17067', 7.5, 'port rebinding weakness in port forward tunnel handling'],
['0.0', '0.71', 2, 'CVE-2019-XXXX', 5.0, 'undefined vulnerability in obsolete SSHv1 protocol handling'],
['0.0', '0.71', 6, 'CVE-2019-XXXX', 5.0, 'local privilege escalation in Pageant'],
['0.0', '0.70', 2, 'CVE-2019-9898', 7.5, 'potential recycling of random numbers'],
['0.0', '0.70', 2, 'CVE-2019-9897', 5.0, 'multiple denial-of-service issues from writing to the terminal'],
['0.0', '0.70', 6, 'CVE-2019-9896', 4.6, 'local application hijacking through malicious Windows help file'],
['0.0', '0.70', 2, 'CVE-2019-9894', 6.4, 'buffer overflow in RSA key exchange'],
['0.0', '0.69', 6, 'CVE-2016-6167', 4.4, 'local application hijacking through untrusted DLL loading'],
['0.0', '0.67', 2, 'CVE-2017-6542', 7.5, 'buffer overflow in UNIX client that can result in privilege escalation or denial-of-service'],
['0.0', '0.66', 2, 'CVE-2016-2563', 7.5, 'buffer overflow in SCP command-line utility'],
['0.0', '0.65', 2, 'CVE-2015-5309', 4.3, 'integer overflow in terminal-handling code'],
]
} # type: Dict[str, List[List[Any]]] } # type: Dict[str, List[List[Any]]]
TXT = { TXT = {
'Dropbear SSH': [ 'Dropbear SSH': [
@ -1917,16 +1982,17 @@ class SSH(object): # pylint: disable=too-few-public-methods
SM_BANNER_SENT = 1 SM_BANNER_SENT = 1
def __init__(self, host, port, ipvo, timeout): def __init__(self, host, port, ipvo, timeout, timeout_set):
# type: (Optional[str], int) -> None # type: (Optional[str], int) -> None
super(SSH.Socket, self).__init__() super(SSH.Socket, self).__init__()
self.__sock = None # type: Optional[socket.socket] self.__sock = None # type: Optional[socket.socket]
self.__sock_map = {}
self.__block_size = 8 self.__block_size = 8
self.__state = 0 self.__state = 0
self.__header = [] # type: List[text_type] self.__header = [] # type: List[text_type]
self.__banner = None # type: Optional[SSH.Banner] self.__banner = None # type: Optional[SSH.Banner]
if host is None: # if host is None:
raise ValueError('undefined host') # raise ValueError('undefined host')
nport = utils.parse_int(port) nport = utils.parse_int(port)
if nport < 1 or nport > 65535: if nport < 1 or nport > 65535:
raise ValueError('invalid port: {0}'.format(port)) raise ValueError('invalid port: {0}'.format(port))
@ -1937,6 +2003,9 @@ class SSH(object): # pylint: disable=too-few-public-methods
else: else:
self.__ipvo = () self.__ipvo = ()
self.__timeout = timeout self.__timeout = timeout
self.__timeout_set = timeout_set
self.client_host = None
self.client_port = None
def _resolve(self, ipvo): def _resolve(self, ipvo):
@ -1962,6 +2031,65 @@ class SSH(object): # pylint: disable=too-few-public-methods
out.fail('[exception] {0}'.format(e)) out.fail('[exception] {0}'.format(e))
sys.exit(1) sys.exit(1)
# Listens on a server socket and accepts one connection (used for
# auditing client connections).
def listen_and_accept(self):
try:
# Socket to listen on all IPv4 addresses.
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('0.0.0.0', self.__port))
s.listen()
self.__sock_map[s.fileno()] = s
except Exception as e:
print("Warning: failed to listen on any IPv4 interfaces.")
pass
try:
# Socket to listen on all IPv6 addresses.
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
s.bind(('::', self.__port))
s.listen()
self.__sock_map[s.fileno()] = s
except Exception as e:
print("Warning: failed to listen on any IPv6 interfaces.")
pass
# If we failed to listen on any interfaces, terminate.
if len(self.__sock_map.keys()) == 0:
print("Error: failed to listen on any IPv4 and IPv6 interfaces!")
exit(-1)
# Wait for an incoming connection. If a timeout was explicitly
# set by the user, terminate when it elapses.
fds = None
time_elapsed = 0.0
interval = 1.0
while True:
# Wait for a connection on either socket.
fds = select.select(self.__sock_map.keys(), [], [], interval)
time_elapsed += interval
# We have incoming data on at least one of the sockets.
if len(fds[0]) > 0:
break
if self.__timeout_set and time_elapsed >= self.__timeout:
print("Timeout elapsed. Terminating...")
exit(-1)
# Accept the connection.
c, addr = self.__sock_map[fds[0][0]].accept()
self.client_host = addr[0]
self.client_port = addr[1]
c.settimeout(self.__timeout)
self.__sock = c
def connect(self): def connect(self):
# type: () -> None # type: () -> None
err = None err = None
@ -1988,15 +2116,15 @@ class SSH(object): # pylint: disable=too-few-public-methods
# type: (int) -> Tuple[Optional[SSH.Banner], List[text_type], Optional[str]] # type: (int) -> Tuple[Optional[SSH.Banner], List[text_type], Optional[str]]
if self.__sock is None: if self.__sock is None:
return self.__banner, self.__header, 'not connected' return self.__banner, self.__header, 'not connected'
banner = 'SSH-{0}-OpenSSH_7.4'.format('1.5' if sshv == 1 else '2.0') banner = SSH_HEADER.format('1.5' if sshv == 1 else '2.0')
rto = self.__sock.gettimeout()
self.__sock.settimeout(0.7)
s, e = self.recv()
self.__sock.settimeout(rto)
if s < 0:
return self.__banner, self.__header, e
if self.__state < self.SM_BANNER_SENT: if self.__state < self.SM_BANNER_SENT:
self.send_banner(banner) self.send_banner(banner)
# rto = self.__sock.gettimeout()
# self.__sock.settimeout(0.7)
s, e = self.recv()
# self.__sock.settimeout(rto)
if s < 0:
return self.__banner, self.__header, e
e = None e = None
while self.__banner is None: while self.__banner is None:
if not s > 0: if not s > 0:
@ -2153,6 +2281,8 @@ class SSH(object): # pylint: disable=too-few-public-methods
def __cleanup(self): def __cleanup(self):
# type: () -> None # type: () -> None
self._close_socket(self.__sock) self._close_socket(self.__sock)
for fd in self.__sock_map:
self._close_socket(self.__sock_map[fd])
self.__sock = None self.__sock = None
@ -2200,6 +2330,11 @@ class KexDH(object): # pragma: nocover
# key blob (from which the fingerprint can be calculated). # key blob (from which the fingerprint can be calculated).
def recv_reply(self, s, parse_host_key_size=True): def recv_reply(self, s, parse_host_key_size=True):
packet_type, payload = s.read_packet(2) packet_type, payload = s.read_packet(2)
# Skip any & all MSG_DEBUG messages.
while packet_type == SSH.Protocol.MSG_DEBUG:
packet_type, payload = s.read_packet(2)
if packet_type != -1 and packet_type not in [SSH.Protocol.MSG_KEXDH_REPLY, SSH.Protocol.MSG_KEXDH_GEX_REPLY]: if packet_type != -1 and packet_type not in [SSH.Protocol.MSG_KEXDH_REPLY, SSH.Protocol.MSG_KEXDH_GEX_REPLY]:
# TODO: change Exception to something more specific. # TODO: change Exception to something more specific.
raise Exception('Expected MSG_KEXDH_REPLY (%d) or MSG_KEXDH_GEX_REPLY (%d), but got %d instead.' % (SSH.Protocol.MSG_KEXDH_REPLY, SSH.Protocol.MSG_KEXDH_GEX_REPLY, packet_type)) raise Exception('Expected MSG_KEXDH_REPLY (%d) or MSG_KEXDH_GEX_REPLY (%d), but got %d instead.' % (SSH.Protocol.MSG_KEXDH_REPLY, SSH.Protocol.MSG_KEXDH_GEX_REPLY, packet_type))
@ -2517,10 +2652,14 @@ class KexGroupExchange(KexDH):
s.send_packet() s.send_packet()
packet_type, payload = s.read_packet(2) packet_type, payload = s.read_packet(2)
if packet_type != SSH.Protocol.MSG_KEXDH_GEX_GROUP: if (packet_type != SSH.Protocol.MSG_KEXDH_GEX_GROUP) and (packet_type != SSH.Protocol.MSG_DEBUG):
# TODO: replace with a better exception type. # TODO: replace with a better exception type.
raise Exception('Expected MSG_KEXDH_GEX_REPLY (%d), but got %d instead.' % (SSH.Protocol.MSG_KEXDH_GEX_REPLY, packet_type)) raise Exception('Expected MSG_KEXDH_GEX_REPLY (%d), but got %d instead.' % (SSH.Protocol.MSG_KEXDH_GEX_REPLY, packet_type))
# Skip any & all MSG_DEBUG messages.
while packet_type == SSH.Protocol.MSG_DEBUG:
packet_type, payload = s.read_packet(2)
# Parse the modulus (p) and generator (g) values from the server. # Parse the modulus (p) and generator (g) values from the server.
ptr = 0 ptr = 0
p_len = struct.unpack('>I', payload[ptr:ptr + 4])[0] p_len = struct.unpack('>I', payload[ptr:ptr + 4])[0]
@ -2624,8 +2763,13 @@ def output_algorithm(alg_db, alg_type, alg_name, unknown_algs, alg_max_len=0, al
f(' ' * len(prefix + alg_name) + comment) f(' ' * len(prefix + alg_name) + comment)
def output_compatibility(algs, for_server=True): def output_compatibility(algs, client_audit, for_server=True):
# type: (SSH.Algorithms, bool) -> None # type: (SSH.Algorithms, bool) -> None
# Don't output any compatibility info if we're doing a client audit.
if client_audit:
return
ssh_timeframe = algs.get_ssh_timeframe(for_server) ssh_timeframe = algs.get_ssh_timeframe(for_server)
comp_text = [] comp_text = []
for ssh_prod in [SSH.Product.OpenSSH, SSH.Product.DropbearSSH]: for ssh_prod in [SSH.Product.OpenSSH, SSH.Product.DropbearSSH]:
@ -2650,7 +2794,7 @@ def output_compatibility(algs, for_server=True):
out.good('(gen) compatibility: ' + ', '.join(comp_text)) out.good('(gen) compatibility: ' + ', '.join(comp_text))
def output_security_sub(sub, software, padlen): def output_security_sub(sub, software, client_audit, padlen):
# type: (str, Optional[SSH.Software], int) -> None # type: (str, Optional[SSH.Software], int) -> None
secdb = SSH.Security.CVE if sub == 'cve' else SSH.Security.TXT secdb = SSH.Security.CVE if sub == 'cve' else SSH.Security.TXT
if software is None or software.product not in secdb: if software is None or software.product not in secdb:
@ -2661,9 +2805,11 @@ def output_security_sub(sub, software, padlen):
continue continue
target, name = line[2:4] # type: int, str target, name = line[2:4] # type: int, str
is_server = target & 1 == 1 is_server = target & 1 == 1
# is_client = target & 2 == 2 is_client = target & 2 == 2
# is_local = target & 4 == 4 # is_local = target & 4 == 4
if not is_server:
# If this security entry applies only to servers, but we're testing a client, then skip it. Similarly, skip entries that apply only to clients, but we're testing a server.
if (is_server and not is_client and client_audit) or (is_client and not is_server and not client_audit):
continue continue
p = '' if out.batch else ' ' * (padlen - len(name)) p = '' if out.batch else ' ' * (padlen - len(name))
if sub == 'cve': if sub == 'cve':
@ -2679,13 +2825,13 @@ def output_security_sub(sub, software, padlen):
out.fail('(sec) {0}{1} -- {2}'.format(name, p, descr)) out.fail('(sec) {0}{1} -- {2}'.format(name, p, descr))
def output_security(banner, padlen): def output_security(banner, client_audit, padlen):
# type: (Optional[SSH.Banner], int) -> None # type: (Optional[SSH.Banner], int) -> None
with OutputBuffer() as obuf: with OutputBuffer() as obuf:
if banner is not None: if banner is not None:
software = SSH.Software.parse(banner) software = SSH.Software.parse(banner)
output_security_sub('cve', software, padlen) output_security_sub('cve', software, client_audit, padlen)
output_security_sub('txt', software, padlen) output_security_sub('txt', software, client_audit, padlen)
if len(obuf) > 0: if len(obuf) > 0:
out.head('# security') out.head('# security')
obuf.flush() obuf.flush()
@ -2730,8 +2876,39 @@ def output_fingerprints(algs, sha256=True):
out.sep() out.sep()
# Returns True if no warnings or failures encountered in configuration.
def output_recommendations(algs, software, padlen=0): def output_recommendations(algs, software, padlen=0):
# type: (SSH.Algorithms, Optional[SSH.Software], int) -> None # type: (SSH.Algorithms, Optional[SSH.Software], int) -> None
ret = True
# PuTTY's algorithms cannot be modified, so there's no point in issuing recommendations.
if (software is not None) and (software.product == SSH.Product.PuTTY):
max_vuln_version = 0.0
max_cvssv2_severity = 0.0
# Search the CVE database for the most recent vulnerable version and the max CVSSv2 score.
for cve_list in SSH.Security.CVE['PuTTY']:
vuln_version = float(cve_list[1])
cvssv2_severity = cve_list[4]
if vuln_version > max_vuln_version:
max_vuln_version = vuln_version
if cvssv2_severity > max_cvssv2_severity:
max_cvssv2_severity = cvssv2_severity
fn = out.warn
if max_cvssv2_severity > 8.0:
fn = out.fail
# Assuming that PuTTY versions will always increment by 0.01, we can calculate the first safe version by adding 0.01 to the latest vulnerable version.
current_version = float(software.version)
upgrade_to_version = max_vuln_version + 0.01
if current_version < upgrade_to_version:
out.head('# recommendations')
fn('(rec) Upgrade to PuTTY v%.2f' % upgrade_to_version)
out.sep()
ret = False
return ret
for_server = True for_server = True
with OutputBuffer() as obuf: with OutputBuffer() as obuf:
software, alg_rec = algs.get_recommendations(software, for_server) software, alg_rec = algs.get_recommendations(software, for_server)
@ -2749,12 +2926,14 @@ def output_recommendations(algs, software, padlen=0):
chg_additional_info = '' chg_additional_info = ''
if action == 'del': if action == 'del':
an, sg, fn = 'remove', '-', out.warn an, sg, fn = 'remove', '-', out.warn
ret = False
if alg_rec[sshv][alg_type][action][name] >= 10: if alg_rec[sshv][alg_type][action][name] >= 10:
fn = out.fail fn = out.fail
elif action == 'add': elif action == 'add':
an, sg, fn = 'append', '+', out.good an, sg, fn = 'append', '+', out.good
elif action == 'chg': elif action == 'chg':
an, sg, fn = 'change', '!', out.fail an, sg, fn = 'change', '!', out.fail
ret = False
chg_additional_info = ' (increase modulus size to 2048 bits or larger)' chg_additional_info = ' (increase modulus size to 2048 bits or larger)'
b = '(SSH{0})'.format(sshv) if sshv == 1 else '' b = '(SSH{0})'.format(sshv) if sshv == 1 else ''
fm = '(rec) {0}{1}{2}-- {3} algorithm to {4}{5} {6}' fm = '(rec) {0}{1}{2}-- {3} algorithm to {4}{5} {6}'
@ -2767,13 +2946,34 @@ def output_recommendations(algs, software, padlen=0):
out.head('# algorithm recommendations {0}'.format(title)) out.head('# algorithm recommendations {0}'.format(title))
obuf.flush(True) # Sort the output so that it is always stable (needed for repeatable testing). obuf.flush(True) # Sort the output so that it is always stable (needed for repeatable testing).
out.sep() out.sep()
return ret
def output(banner, header, kex=None, pkm=None): # Output additional information & notes.
def output_info(algs, software, client_audit, any_problems, padlen=0):
with OutputBuffer() as obuf:
# Tell user that PuTTY cannot be hardened at the protocol-level.
if client_audit and (software is not None) and (software.product == SSH.Product.PuTTY):
out.warn('(nfo) PuTTY does not have the option of restricting any algorithms during the SSH handshake.')
# If any warnings or failures were given, print a link to the hardening guides.
if any_problems:
out.warn('(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>')
if len(obuf) > 0:
out.head('# additional info')
obuf.flush()
out.sep()
def output(banner, header, client_host=None, kex=None, pkm=None):
# type: (Optional[SSH.Banner], List[text_type], Optional[SSH2.Kex], Optional[SSH1.PublicKeyMessage]) -> None # type: (Optional[SSH.Banner], List[text_type], Optional[SSH2.Kex], Optional[SSH1.PublicKeyMessage]) -> None
client_audit = (client_host != None) # If set, this is a client audit.
sshv = 1 if pkm is not None else 2 sshv = 1 if pkm is not None else 2
algs = SSH.Algorithms(pkm, kex) algs = SSH.Algorithms(pkm, kex)
with OutputBuffer() as obuf: with OutputBuffer() as obuf:
if client_audit:
out.good('(gen) client IP: {0}'.format(client_host))
if len(header) > 0: if len(header) > 0:
out.info('(gen) header: ' + '\n'.join(header)) out.info('(gen) header: ' + '\n'.join(header))
if banner is not None: if banner is not None:
@ -2788,7 +2988,7 @@ def output(banner, header, kex=None, pkm=None):
out.good('(gen) software: {0}'.format(software)) out.good('(gen) software: {0}'.format(software))
else: else:
software = None software = None
output_compatibility(algs) output_compatibility(algs, client_audit)
if kex is not None: if kex is not None:
compressions = [x for x in kex.server.compression if x != 'none'] compressions = [x for x in kex.server.compression if x != 'none']
if len(compressions) > 0: if len(compressions) > 0:
@ -2801,7 +3001,7 @@ def output(banner, header, kex=None, pkm=None):
obuf.flush() obuf.flush()
out.sep() out.sep()
maxlen = algs.maxlen + 1 maxlen = algs.maxlen + 1
output_security(banner, maxlen) output_security(banner, client_audit, maxlen)
unknown_algorithms = [] # Filled in by output_algorithms() with unidentified algs. unknown_algorithms = [] # Filled in by output_algorithms() with unidentified algs.
if pkm is not None: if pkm is not None:
adb = SSH1.KexDB.ALGORITHMS adb = SSH1.KexDB.ALGORITHMS
@ -2824,7 +3024,9 @@ def output(banner, header, kex=None, pkm=None):
title, atype = 'message authentication code algorithms', 'mac' title, atype = 'message authentication code algorithms', 'mac'
output_algorithms(title, adb, atype, kex.server.mac, unknown_algorithms, maxlen) output_algorithms(title, adb, atype, kex.server.mac, unknown_algorithms, maxlen)
output_fingerprints(algs, True) output_fingerprints(algs, True)
output_recommendations(algs, software, maxlen) perfect_config = output_recommendations(algs, software, maxlen)
output_info(algs, software, client_audit, not perfect_config)
# If we encountered any unknown algorithms, ask the user to report them. # If we encountered any unknown algorithms, ask the user to report them.
if len(unknown_algorithms) > 0: if len(unknown_algorithms) > 0:
@ -2952,6 +3154,82 @@ class Utils(object):
except: # pylint: disable=bare-except except: # pylint: disable=bare-except
return -1.0 return -1.0
def build_struct(banner, kex=None, pkm=None, client_host=None):
res = {
"banner": {
"raw": str(banner),
"protocol": banner.protocol,
"software": banner.software,
"comments": banner.comments,
},
}
if client_host is not None:
res['client_ip'] = client_host
if kex is not None:
res['compression'] = kex.server.compression
res['kex'] = []
alg_sizes = kex.dh_modulus_sizes()
for algorithm in kex.kex_algorithms:
entry = {
'algorithm': algorithm,
}
if (alg_sizes is not None) and (algorithm in alg_sizes):
hostkey_size, ca_size = alg_sizes[algorithm]
entry['keysize'] = hostkey_size
if ca_size > 0:
entry['casize'] = ca_size
res['kex'].append(entry)
res['key'] = []
alg_sizes = kex.rsa_key_sizes()
for algorithm in kex.key_algorithms:
entry = {
'algorithm': algorithm,
}
if (alg_sizes is not None) and (algorithm in alg_sizes):
hostkey_size, ca_size = alg_sizes[algorithm]
entry['keysize'] = hostkey_size
if ca_size > 0:
entry['casize'] = ca_size
res['key'].append(entry)
res['enc'] = kex.server.encryption
res['mac'] = kex.server.mac
res['fingerprints'] = []
host_keys = kex.host_keys()
# Normalize all RSA key types to 'ssh-rsa'. Otherwise, due to Python's order-indifference dictionary types, we would iterate key types in unpredictable orders, which interferes with the docker testing framework (i.e.: tests would fail because elements are reported out of order, even though the output is semantically the same).
for host_key_type in host_keys.keys():
if host_key_type in SSH2.HostKeyTest.RSA_FAMILY:
val = host_keys[host_key_type]
del(host_keys[host_key_type])
host_keys['ssh-rsa'] = val
for host_key_type in sorted(host_keys):
if host_keys[host_key_type] is None:
continue
fp = SSH.Fingerprint(host_keys[host_key_type])
# Skip over certificate host types (or we would return invalid fingerprints).
if '-cert-' in host_key_type:
continue
entry = {
'type': host_key_type,
'fp': fp.sha256,
}
res['fingerprints'].append(entry)
else:
res['key'] = ['ssh-rsa1']
res['enc'] = pkm.supported_ciphers
res['aut'] = pkm.supported_authentications
res['fingerprints'] = [{
'type': 'ssh-rsa1',
'fp': SSH.Fingerprint(pkm.host_key_fingerprint_data).sha256,
}]
return res
def audit(aconf, sshv=None): def audit(aconf, sshv=None):
# type: (AuditConf, Optional[int]) -> None # type: (AuditConf, Optional[int]) -> None
@ -2959,8 +3237,11 @@ def audit(aconf, sshv=None):
out.verbose = aconf.verbose out.verbose = aconf.verbose
out.level = aconf.level out.level = aconf.level
out.use_colors = aconf.colors out.use_colors = aconf.colors
s = SSH.Socket(aconf.host, aconf.port, aconf.ipvo, aconf.timeout) s = SSH.Socket(aconf.host, aconf.port, aconf.ipvo, aconf.timeout, aconf.timeout_set)
s.connect() if aconf.client_audit:
s.listen_and_accept()
else:
s.connect()
if sshv is None: if sshv is None:
sshv = 2 if aconf.ssh2 else 1 sshv = 2 if aconf.ssh2 else 1
err = None err = None
@ -3001,16 +3282,27 @@ def audit(aconf, sshv=None):
sys.exit(1) sys.exit(1)
if sshv == 1: if sshv == 1:
pkm = SSH1.PublicKeyMessage.parse(payload) pkm = SSH1.PublicKeyMessage.parse(payload)
output(banner, header, pkm=pkm) if aconf.json:
print(json.dumps(build_struct(banner, pkm=pkm), sort_keys=True))
else:
output(banner, header, pkm=pkm)
elif sshv == 2: elif sshv == 2:
kex = SSH2.Kex.parse(payload) kex = SSH2.Kex.parse(payload)
SSH2.HostKeyTest.run(s, kex) if aconf.client_audit is False:
SSH2.GEXTest.run(s, kex) SSH2.HostKeyTest.run(s, kex)
output(banner, header, kex=kex) SSH2.GEXTest.run(s, kex)
if aconf.json:
print(json.dumps(build_struct(banner, kex=kex, client_host=s.client_host), sort_keys=True))
else:
output(banner, header, client_host=s.client_host, kex=kex)
utils = Utils() utils = Utils()
out = Output() out = Output()
if __name__ == '__main__': # pragma: nocover
def main():
conf = AuditConf.from_cmdline(sys.argv[1:], usage) conf = AuditConf.from_cmdline(sys.argv[1:], usage)
audit(conf) audit(conf)
if __name__ == '__main__': # pragma: nocover
main()

View File

@ -0,0 +1 @@
{"banner": {"comments": null, "protocol": [2, 0], "raw": "SSH-2.0-dropbear_2019.78", "software": "dropbear_2019.78"}, "compression": ["zlib@openssh.com", "none"], "enc": ["aes128-ctr", "aes256-ctr", "aes128-cbc", "aes256-cbc", "3des-ctr", "3des-cbc"], "fingerprints": [{"fp": "SHA256:CDfAU12pjQS7/91kg7gYacza0U/6PDbE04Ic3IpYxkM", "type": "ssh-rsa"}], "kex": [{"algorithm": "curve25519-sha256"}, {"algorithm": "curve25519-sha256@libssh.org"}, {"algorithm": "ecdh-sha2-nistp521"}, {"algorithm": "ecdh-sha2-nistp384"}, {"algorithm": "ecdh-sha2-nistp256"}, {"algorithm": "diffie-hellman-group14-sha256"}, {"algorithm": "diffie-hellman-group14-sha1"}, {"algorithm": "kexguess2@matt.ucc.asn.au"}], "key": [{"algorithm": "ecdsa-sha2-nistp256"}, {"algorithm": "ssh-rsa", "keysize": 1024}, {"algorithm": "ssh-dss"}], "mac": ["hmac-sha1-96", "hmac-sha1", "hmac-sha2-256"]}

View File

@ -1,11 +1,11 @@
# general # general
(gen) banner: SSH-2.0-dropbear_2019.78 (gen) banner: SSH-2.0-dropbear_2019.78
(gen) software: Dropbear SSH 2019.78 (gen) software: Dropbear SSH 2019.78
(gen) compatibility: OpenSSH 7.4+ (some functionality from 6.6), Dropbear SSH 2016.73+ (gen) compatibility: OpenSSH 7.4+ (some functionality from 6.6), Dropbear SSH 2018.76+
(gen) compression: enabled (zlib@openssh.com) (gen) compression: enabled (zlib@openssh.com)
# key exchange algorithms # key exchange algorithms
(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4 (kex) curve25519-sha256 -- [info] available since OpenSSH 7.4, Dropbear SSH 2018.76
(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.5, Dropbear SSH 2013.62 (kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.5, Dropbear SSH 2013.62
(kex) ecdh-sha2-nistp521 -- [fail] using weak elliptic curves (kex) ecdh-sha2-nistp521 -- [fail] using weak elliptic curves
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62 `- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
@ -80,3 +80,6 @@
(rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove  (rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove 
(rec) -hmac-sha1 -- mac algorithm to remove  (rec) -hmac-sha1 -- mac algorithm to remove 
# additional info
(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>

View File

@ -0,0 +1 @@
{"banner": {"comments": null, "protocol": [1, 99], "raw": "SSH-1.99-OpenSSH_4.0", "software": "OpenSSH_4.0"}, "compression": ["none", "zlib"], "enc": ["aes128-cbc", "3des-cbc", "blowfish-cbc", "cast128-cbc", "arcfour", "aes192-cbc", "aes256-cbc", "rijndael-cbc@lysator.liu.se", "aes128-ctr", "aes192-ctr", "aes256-ctr"], "fingerprints": [{"fp": "SHA256:YZ457EBcJTSxRKI3yXRgtAj3PBf5B9/F36b1SVooml4", "type": "ssh-rsa"}], "kex": [{"algorithm": "diffie-hellman-group-exchange-sha1", "keysize": 1024}, {"algorithm": "diffie-hellman-group14-sha1"}, {"algorithm": "diffie-hellman-group1-sha1"}], "key": [{"algorithm": "ssh-rsa", "keysize": 1024}, {"algorithm": "ssh-dss"}], "mac": ["hmac-md5", "hmac-sha1", "hmac-ripemd160", "hmac-ripemd160@openssh.com", "hmac-sha1-96", "hmac-md5-96"]}

View File

@ -134,3 +134,6 @@
(rec) -rijndael-cbc@lysator.liu.se -- enc algorithm to remove  (rec) -rijndael-cbc@lysator.liu.se -- enc algorithm to remove 
(rec) -ssh-dss -- key algorithm to remove  (rec) -ssh-dss -- key algorithm to remove 
# additional info
(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>

View File

@ -0,0 +1 @@
{"banner": {"comments": null, "protocol": [2, 0], "raw": "SSH-2.0-OpenSSH_5.6", "software": "OpenSSH_5.6"}, "compression": ["none", "zlib@openssh.com"], "enc": ["aes128-ctr", "aes192-ctr", "aes256-ctr", "arcfour256", "arcfour128", "aes128-cbc", "3des-cbc", "blowfish-cbc", "cast128-cbc", "aes192-cbc", "aes256-cbc", "arcfour", "rijndael-cbc@lysator.liu.se"], "fingerprints": [{"fp": "SHA256:YZ457EBcJTSxRKI3yXRgtAj3PBf5B9/F36b1SVooml4", "type": "ssh-rsa"}], "kex": [{"algorithm": "diffie-hellman-group-exchange-sha256", "keysize": 1024}, {"algorithm": "diffie-hellman-group-exchange-sha1", "keysize": 1024}, {"algorithm": "diffie-hellman-group14-sha1"}, {"algorithm": "diffie-hellman-group1-sha1"}], "key": [{"algorithm": "ssh-rsa", "keysize": 1024}, {"algorithm": "ssh-dss"}], "mac": ["hmac-md5", "hmac-sha1", "umac-64@openssh.com", "hmac-ripemd160", "hmac-ripemd160@openssh.com", "hmac-sha1-96", "hmac-md5-96"]}

View File

@ -143,3 +143,6 @@
(rec) -ssh-dss -- key algorithm to remove  (rec) -ssh-dss -- key algorithm to remove 
(rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove  (rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove 
# additional info
(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>

View File

@ -0,0 +1 @@
{"banner": {"comments": null, "protocol": [2, 0], "raw": "SSH-2.0-OpenSSH_5.6", "software": "OpenSSH_5.6"}, "compression": ["none", "zlib@openssh.com"], "enc": ["aes128-ctr", "aes192-ctr", "aes256-ctr", "arcfour256", "arcfour128", "aes128-cbc", "3des-cbc", "blowfish-cbc", "cast128-cbc", "aes192-cbc", "aes256-cbc", "arcfour", "rijndael-cbc@lysator.liu.se"], "fingerprints": [{"fp": "SHA256:YZ457EBcJTSxRKI3yXRgtAj3PBf5B9/F36b1SVooml4", "type": "ssh-rsa"}], "kex": [{"algorithm": "diffie-hellman-group-exchange-sha256", "keysize": 1024}, {"algorithm": "diffie-hellman-group-exchange-sha1", "keysize": 1024}, {"algorithm": "diffie-hellman-group14-sha1"}, {"algorithm": "diffie-hellman-group1-sha1"}], "key": [{"algorithm": "ssh-rsa", "keysize": 1024}, {"algorithm": "ssh-rsa-cert-v01@openssh.com", "casize": 1024, "keysize": 1024}], "mac": ["hmac-md5", "hmac-sha1", "umac-64@openssh.com", "hmac-ripemd160", "hmac-ripemd160@openssh.com", "hmac-sha1-96", "hmac-md5-96"]}

View File

@ -141,3 +141,6 @@
(rec) -rijndael-cbc@lysator.liu.se -- enc algorithm to remove  (rec) -rijndael-cbc@lysator.liu.se -- enc algorithm to remove 
(rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove  (rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove 
# additional info
(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>

View File

@ -0,0 +1 @@
{"banner": {"comments": null, "protocol": [2, 0], "raw": "SSH-2.0-OpenSSH_5.6", "software": "OpenSSH_5.6"}, "compression": ["none", "zlib@openssh.com"], "enc": ["aes128-ctr", "aes192-ctr", "aes256-ctr", "arcfour256", "arcfour128", "aes128-cbc", "3des-cbc", "blowfish-cbc", "cast128-cbc", "aes192-cbc", "aes256-cbc", "arcfour", "rijndael-cbc@lysator.liu.se"], "fingerprints": [{"fp": "SHA256:YZ457EBcJTSxRKI3yXRgtAj3PBf5B9/F36b1SVooml4", "type": "ssh-rsa"}], "kex": [{"algorithm": "diffie-hellman-group-exchange-sha256", "keysize": 1024}, {"algorithm": "diffie-hellman-group-exchange-sha1", "keysize": 1024}, {"algorithm": "diffie-hellman-group14-sha1"}, {"algorithm": "diffie-hellman-group1-sha1"}], "key": [{"algorithm": "ssh-rsa", "keysize": 1024}, {"algorithm": "ssh-rsa-cert-v01@openssh.com", "casize": 3072, "keysize": 1024}], "mac": ["hmac-md5", "hmac-sha1", "umac-64@openssh.com", "hmac-ripemd160", "hmac-ripemd160@openssh.com", "hmac-sha1-96", "hmac-md5-96"]}

View File

@ -141,3 +141,6 @@
(rec) -rijndael-cbc@lysator.liu.se -- enc algorithm to remove  (rec) -rijndael-cbc@lysator.liu.se -- enc algorithm to remove 
(rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove  (rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove 
# additional info
(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>

View File

@ -0,0 +1 @@
{"banner": {"comments": null, "protocol": [2, 0], "raw": "SSH-2.0-OpenSSH_5.6", "software": "OpenSSH_5.6"}, "compression": ["none", "zlib@openssh.com"], "enc": ["aes128-ctr", "aes192-ctr", "aes256-ctr", "arcfour256", "arcfour128", "aes128-cbc", "3des-cbc", "blowfish-cbc", "cast128-cbc", "aes192-cbc", "aes256-cbc", "arcfour", "rijndael-cbc@lysator.liu.se"], "fingerprints": [{"fp": "SHA256:nsWtdJ9Z67Vrf7OsUzQov7esXhsWAfVppArGh25u244", "type": "ssh-rsa"}], "kex": [{"algorithm": "diffie-hellman-group-exchange-sha256", "keysize": 1024}, {"algorithm": "diffie-hellman-group-exchange-sha1", "keysize": 1024}, {"algorithm": "diffie-hellman-group14-sha1"}, {"algorithm": "diffie-hellman-group1-sha1"}], "key": [{"algorithm": "ssh-rsa", "keysize": 3072}, {"algorithm": "ssh-rsa-cert-v01@openssh.com", "casize": 1024, "keysize": 3072}], "mac": ["hmac-md5", "hmac-sha1", "umac-64@openssh.com", "hmac-ripemd160", "hmac-ripemd160@openssh.com", "hmac-sha1-96", "hmac-md5-96"]}

View File

@ -139,3 +139,6 @@
(rec) -rijndael-cbc@lysator.liu.se -- enc algorithm to remove  (rec) -rijndael-cbc@lysator.liu.se -- enc algorithm to remove 
(rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove  (rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove 
# additional info
(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>

View File

@ -0,0 +1 @@
{"banner": {"comments": null, "protocol": [2, 0], "raw": "SSH-2.0-OpenSSH_5.6", "software": "OpenSSH_5.6"}, "compression": ["none", "zlib@openssh.com"], "enc": ["aes128-ctr", "aes192-ctr", "aes256-ctr", "arcfour256", "arcfour128", "aes128-cbc", "3des-cbc", "blowfish-cbc", "cast128-cbc", "aes192-cbc", "aes256-cbc", "arcfour", "rijndael-cbc@lysator.liu.se"], "fingerprints": [{"fp": "SHA256:nsWtdJ9Z67Vrf7OsUzQov7esXhsWAfVppArGh25u244", "type": "ssh-rsa"}], "kex": [{"algorithm": "diffie-hellman-group-exchange-sha256", "keysize": 1024}, {"algorithm": "diffie-hellman-group-exchange-sha1", "keysize": 1024}, {"algorithm": "diffie-hellman-group14-sha1"}, {"algorithm": "diffie-hellman-group1-sha1"}], "key": [{"algorithm": "ssh-rsa", "keysize": 3072}, {"algorithm": "ssh-rsa-cert-v01@openssh.com", "casize": 3072, "keysize": 3072}], "mac": ["hmac-md5", "hmac-sha1", "umac-64@openssh.com", "hmac-ripemd160", "hmac-ripemd160@openssh.com", "hmac-sha1-96", "hmac-md5-96"]}

View File

@ -137,3 +137,6 @@
(rec) -rijndael-cbc@lysator.liu.se -- enc algorithm to remove  (rec) -rijndael-cbc@lysator.liu.se -- enc algorithm to remove 
(rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove  (rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove 
# additional info
(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>

View File

@ -0,0 +1 @@
{"banner": {"comments": null, "protocol": [2, 0], "raw": "SSH-2.0-OpenSSH_8.0", "software": "OpenSSH_8.0"}, "compression": ["none", "zlib@openssh.com"], "enc": ["chacha20-poly1305@openssh.com", "aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "aes256-gcm@openssh.com"], "fingerprints": [{"fp": "SHA256:UrnXIVH+7dlw8UqYocl48yUEcKrthGDQG2CPCgp7MxU", "type": "ssh-ed25519"}, {"fp": "SHA256:nsWtdJ9Z67Vrf7OsUzQov7esXhsWAfVppArGh25u244", "type": "ssh-rsa"}], "kex": [{"algorithm": "curve25519-sha256"}, {"algorithm": "curve25519-sha256@libssh.org"}, {"algorithm": "ecdh-sha2-nistp256"}, {"algorithm": "ecdh-sha2-nistp384"}, {"algorithm": "ecdh-sha2-nistp521"}, {"algorithm": "diffie-hellman-group-exchange-sha256", "keysize": 2048}, {"algorithm": "diffie-hellman-group16-sha512"}, {"algorithm": "diffie-hellman-group18-sha512"}, {"algorithm": "diffie-hellman-group14-sha256"}, {"algorithm": "diffie-hellman-group14-sha1"}], "key": [{"algorithm": "rsa-sha2-512", "keysize": 3072}, {"algorithm": "rsa-sha2-256", "keysize": 3072}, {"algorithm": "ssh-rsa", "keysize": 3072}, {"algorithm": "ecdsa-sha2-nistp256"}, {"algorithm": "ssh-ed25519"}], "mac": ["umac-64-etm@openssh.com", "umac-128-etm@openssh.com", "hmac-sha2-256-etm@openssh.com", "hmac-sha2-512-etm@openssh.com", "hmac-sha1-etm@openssh.com", "umac-64@openssh.com", "umac-128@openssh.com", "hmac-sha2-256", "hmac-sha2-512", "hmac-sha1"]}

View File

@ -1,11 +1,11 @@
# general # general
(gen) banner: SSH-2.0-OpenSSH_8.0 (gen) banner: SSH-2.0-OpenSSH_8.0
(gen) software: OpenSSH 8.0 (gen) software: OpenSSH 8.0
(gen) compatibility: OpenSSH 7.4+, Dropbear SSH 2016.73+ (gen) compatibility: OpenSSH 7.4+, Dropbear SSH 2018.76+
(gen) compression: enabled (zlib@openssh.com) (gen) compression: enabled (zlib@openssh.com)
# key exchange algorithms # key exchange algorithms
(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4 (kex) curve25519-sha256 -- [info] available since OpenSSH 7.4, Dropbear SSH 2018.76
(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.5, Dropbear SSH 2013.62 (kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.5, Dropbear SSH 2013.62
(kex) ecdh-sha2-nistp256 -- [fail] using weak elliptic curves (kex) ecdh-sha2-nistp256 -- [fail] using weak elliptic curves
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62 `- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
@ -77,3 +77,6 @@
(rec) -umac-64-etm@openssh.com -- mac algorithm to remove  (rec) -umac-64-etm@openssh.com -- mac algorithm to remove 
(rec) -umac-64@openssh.com -- mac algorithm to remove  (rec) -umac-64@openssh.com -- mac algorithm to remove 
# additional info
(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>

View File

@ -0,0 +1 @@
{"banner": {"comments": null, "protocol": [2, 0], "raw": "SSH-2.0-OpenSSH_8.0", "software": "OpenSSH_8.0"}, "compression": ["none", "zlib@openssh.com"], "enc": ["chacha20-poly1305@openssh.com", "aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "aes256-gcm@openssh.com"], "fingerprints": [{"fp": "SHA256:UrnXIVH+7dlw8UqYocl48yUEcKrthGDQG2CPCgp7MxU", "type": "ssh-ed25519"}], "kex": [{"algorithm": "curve25519-sha256"}, {"algorithm": "curve25519-sha256@libssh.org"}, {"algorithm": "ecdh-sha2-nistp256"}, {"algorithm": "ecdh-sha2-nistp384"}, {"algorithm": "ecdh-sha2-nistp521"}, {"algorithm": "diffie-hellman-group-exchange-sha256", "keysize": 2048}, {"algorithm": "diffie-hellman-group16-sha512"}, {"algorithm": "diffie-hellman-group18-sha512"}, {"algorithm": "diffie-hellman-group14-sha256"}, {"algorithm": "diffie-hellman-group14-sha1"}], "key": [{"algorithm": "ssh-ed25519"}, {"algorithm": "ssh-ed25519-cert-v01@openssh.com"}], "mac": ["umac-64-etm@openssh.com", "umac-128-etm@openssh.com", "hmac-sha2-256-etm@openssh.com", "hmac-sha2-512-etm@openssh.com", "hmac-sha1-etm@openssh.com", "umac-64@openssh.com", "umac-128@openssh.com", "hmac-sha2-256", "hmac-sha2-512", "hmac-sha1"]}

View File

@ -1,11 +1,11 @@
# general # general
(gen) banner: SSH-2.0-OpenSSH_8.0 (gen) banner: SSH-2.0-OpenSSH_8.0
(gen) software: OpenSSH 8.0 (gen) software: OpenSSH 8.0
(gen) compatibility: OpenSSH 7.4+, Dropbear SSH 2016.73+ (gen) compatibility: OpenSSH 7.4+, Dropbear SSH 2018.76+
(gen) compression: enabled (zlib@openssh.com) (gen) compression: enabled (zlib@openssh.com)
# key exchange algorithms # key exchange algorithms
(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4 (kex) curve25519-sha256 -- [info] available since OpenSSH 7.4, Dropbear SSH 2018.76
(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.5, Dropbear SSH 2013.62 (kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.5, Dropbear SSH 2013.62
(kex) ecdh-sha2-nistp256 -- [fail] using weak elliptic curves (kex) ecdh-sha2-nistp256 -- [fail] using weak elliptic curves
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62 `- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
@ -73,3 +73,6 @@
(rec) -umac-64-etm@openssh.com -- mac algorithm to remove  (rec) -umac-64-etm@openssh.com -- mac algorithm to remove 
(rec) -umac-64@openssh.com -- mac algorithm to remove  (rec) -umac-64@openssh.com -- mac algorithm to remove 
# additional info
(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>

View File

@ -0,0 +1 @@
{"banner": {"comments": null, "protocol": [2, 0], "raw": "SSH-2.0-OpenSSH_8.0", "software": "OpenSSH_8.0"}, "compression": ["none", "zlib@openssh.com"], "enc": ["chacha20-poly1305@openssh.com", "aes256-gcm@openssh.com", "aes128-gcm@openssh.com", "aes256-ctr", "aes192-ctr", "aes128-ctr"], "fingerprints": [{"fp": "SHA256:UrnXIVH+7dlw8UqYocl48yUEcKrthGDQG2CPCgp7MxU", "type": "ssh-ed25519"}], "kex": [{"algorithm": "curve25519-sha256"}, {"algorithm": "curve25519-sha256@libssh.org"}, {"algorithm": "diffie-hellman-group-exchange-sha256", "keysize": 2048}], "key": [{"algorithm": "ssh-ed25519"}], "mac": ["hmac-sha2-256-etm@openssh.com", "hmac-sha2-512-etm@openssh.com", "umac-128-etm@openssh.com"]}

View File

@ -1,11 +1,11 @@
# general # general
(gen) banner: SSH-2.0-OpenSSH_8.0 (gen) banner: SSH-2.0-OpenSSH_8.0
(gen) software: OpenSSH 8.0 (gen) software: OpenSSH 8.0
(gen) compatibility: OpenSSH 7.4+, Dropbear SSH 2013.62+ (gen) compatibility: OpenSSH 7.4+, Dropbear SSH 2018.76+
(gen) compression: enabled (zlib@openssh.com) (gen) compression: enabled (zlib@openssh.com)
# key exchange algorithms # key exchange algorithms
(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4 (kex) curve25519-sha256 -- [info] available since OpenSSH 7.4, Dropbear SSH 2018.76
(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.5, Dropbear SSH 2013.62 (kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.5, Dropbear SSH 2013.62
(kex) diffie-hellman-group-exchange-sha256 (2048-bit) -- [info] available since OpenSSH 4.4 (kex) diffie-hellman-group-exchange-sha256 (2048-bit) -- [info] available since OpenSSH 4.4

View File

@ -0,0 +1 @@
{"banner": {"comments": "", "protocol": [2, 0], "raw": "", "software": "tinyssh_noversion"}, "compression": ["none"], "enc": ["chacha20-poly1305@openssh.com"], "fingerprints": [{"fp": "SHA256:89ocln1x7KNqnMgWffGoYtD70ksJ4FrH7BMJHa7SrwU", "type": "ssh-ed25519"}], "kex": [{"algorithm": "curve25519-sha256"}, {"algorithm": "curve25519-sha256@libssh.org"}, {"algorithm": "sntrup4591761x25519-sha512@tinyssh.org"}], "key": [{"algorithm": "ssh-ed25519"}], "mac": ["hmac-sha2-256"]}

View File

@ -1,10 +1,10 @@
# general # general
(gen) software: TinySSH noversion (gen) software: TinySSH noversion
(gen) compatibility: OpenSSH 8.0+, Dropbear SSH 2013.62+ (gen) compatibility: OpenSSH 8.0+, Dropbear SSH 2018.76+
(gen) compression: disabled (gen) compression: disabled
# key exchange algorithms # key exchange algorithms
(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4 (kex) curve25519-sha256 -- [info] available since OpenSSH 7.4, Dropbear SSH 2018.76
(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.5, Dropbear SSH 2013.62 (kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.5, Dropbear SSH 2013.62
(kex) sntrup4591761x25519-sha512@tinyssh.org -- [warn] using experimental algorithm (kex) sntrup4591761x25519-sha512@tinyssh.org -- [warn] using experimental algorithm
`- [info] available since OpenSSH 8.0 `- [info] available since OpenSSH 8.0