mirror of
https://github.com/jtesta/ssh-audit.git
synced 2025-07-06 14:02:49 -05:00
Compare commits
53 Commits
Author | SHA1 | Date | |
---|---|---|---|
e447c42a79 | |||
5292066e66 | |||
c043570879 | |||
a04c96c5b2 | |||
c9a2f2955c | |||
99ae10440b | |||
8cafcd4eb5 | |||
262e9b1826 | |||
06f868d76f | |||
8e3e8aa423 | |||
96b6a62f05 | |||
229a4f2af9 | |||
5c63f907f7 | |||
cba89f70e3 | |||
dc36622b50 | |||
8e0b83176a | |||
a16eb2d6cb | |||
2848c1fb16 | |||
2cff202b32 | |||
dae92513fd | |||
e101e22720 | |||
b3a46e8318 | |||
3606863ebf | |||
cf1e069db4 | |||
1e1220807f | |||
0263769243 | |||
e6c31ee4f5 | |||
14e0ed0e00 | |||
0f21f2131c | |||
b6c64d296b | |||
1ec13c653e | |||
a401afd099 | |||
8a3ae321f1 | |||
e62b548677 | |||
fd85e247e7 | |||
e3a59a3e21 | |||
4ebefdf894 | |||
83544836c9 | |||
4c9b871f5c | |||
1d707276d7 | |||
166c93ace4 | |||
9759480ae4 | |||
fd3a1f7d41 | |||
08677d65b1 | |||
8c5493ae3e | |||
14af53cf04 | |||
bbf6204ce1 | |||
0df63c20ac | |||
209bcab427 | |||
eac81455a9 | |||
bce9e2b152 | |||
f5431559ff | |||
f2e6f1a71c |
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,8 +1,13 @@
|
|||||||
*~
|
*~
|
||||||
*.pyc
|
*.pyc
|
||||||
|
*.exe
|
||||||
|
*.asc
|
||||||
venv*/
|
venv*/
|
||||||
.cache/
|
.cache/
|
||||||
.tox
|
.tox
|
||||||
.coverage*
|
.coverage*
|
||||||
reports/
|
reports/
|
||||||
.scannerwork/
|
.scannerwork/
|
||||||
|
pypi/sshaudit/LICENSE
|
||||||
|
pypi/sshaudit/README.md
|
||||||
|
pypi/sshaudit/sshaudit.py
|
||||||
|
51
README.md
51
README.md
@ -5,10 +5,11 @@
|
|||||||
[](https://codecov.io/gh/arthepsy/ssh-audit)
|
[](https://codecov.io/gh/arthepsy/ssh-audit)
|
||||||
[](https://sq.evolutiongaming.com/dashboard?id=arthepsy-github%3Assh-audit%3Adevelop)
|
[](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,12 @@
|
|||||||
- 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;
|
- runs on Linux and Windows;
|
||||||
|
- 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 +30,55 @@ 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
|
||||||

|
Below is a screen shot of the server-auditing output when connecting to an unhardened OpenSSH v5.3 service:
|
||||||
|

|
||||||
|
|
||||||
|
### Client Audit Example
|
||||||
|
Below is a screen shot of the client-auditing output when an unhardened OpenSSH v7.2 client connects:
|
||||||
|

|
||||||
|
|
||||||
|
### 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.2.0 (2020-03-11)
|
||||||
|
- Marked host key type `ssh-rsa` as weak due to [practical SHA-1 collisions](https://eprint.iacr.org/2020/014.pdf).
|
||||||
|
- Added Windows builds.
|
||||||
|
- Added 10 new host key types: `ecdsa-sha2-1.3.132.0.10`, `x509v3-sign-dss`, `x509v3-sign-rsa`, `x509v3-sign-rsa-sha256@ssh.com`, `x509v3-ssh-dss`, `x509v3-ssh-rsa`, `sk-ecdsa-sha2-nistp256-cert-v01@openssh.com`, `sk-ecdsa-sha2-nistp256@openssh.com`, `sk-ssh-ed25519-cert-v01@openssh.com`, and `sk-ssh-ed25519@openssh.com`.
|
||||||
|
- Added 18 new key exchanges: `diffie-hellman-group14-sha256@ssh.com`, `diffie-hellman-group15-sha256@ssh.com`, `diffie-hellman-group15-sha384@ssh.com`, `diffie-hellman-group16-sha384@ssh.com`, `diffie-hellman-group16-sha512@ssh.com`, `diffie-hellman-group18-sha512@ssh.com`, `ecdh-sha2-curve25519`, `ecdh-sha2-nistb233`, `ecdh-sha2-nistb409`, `ecdh-sha2-nistk163`, `ecdh-sha2-nistk233`, `ecdh-sha2-nistk283`, `ecdh-sha2-nistk409`, `ecdh-sha2-nistp192`, `ecdh-sha2-nistp224`, `ecdh-sha2-nistt571`, `gss-gex-sha1-`, and `gss-group1-sha1-`.
|
||||||
|
- Added 9 new ciphers: `camellia128-cbc`, `camellia128-ctr`, `camellia192-cbc`, `camellia192-ctr`, `camellia256-cbc`, `camellia256-ctr`, `aes128-gcm`, `aes256-gcm`, and `chacha20-poly1305`.
|
||||||
|
- Added 2 new MACs: `aes128-gcm` and `aes256-gcm`.
|
||||||
|
|
||||||
|
### 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 +88,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)
|
||||||
|
@ -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,14 +420,21 @@ 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
|
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 -t 0 $cid > /dev/null
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
docker container stop $cid > /dev/null
|
./ssh-audit.py -j localhost:2222 > $test_result_json
|
||||||
|
if [[ $? != 0 ]]; then
|
||||||
|
echo -e "${REDB}Failed to run ssh-audit.py! (exit code: $?)${CLR}"
|
||||||
|
docker container stop -t 0 $cid > /dev/null
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker container stop -t 0 $cid > /dev/null
|
||||||
if [[ $? != 0 ]]; then
|
if [[ $? != 0 ]]; then
|
||||||
echo -e "${REDB}Failed to stop docker container ${cid}! (exit code: $?)${CLR}"
|
echo -e "${REDB}Failed to stop docker container ${cid}! (exit code: $?)${CLR}"
|
||||||
exit 1
|
exit 1
|
||||||
@ -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
1
pypi/MANIFEST.in
Normal file
@ -0,0 +1 @@
|
|||||||
|
include sshaudit/LICENSE
|
14
pypi/Makefile
Normal file
14
pypi/Makefile
Normal 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
17
pypi/notes.txt
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
To create package and upload to test server:
|
||||||
|
|
||||||
|
# apt install virtualenv
|
||||||
|
$ virtualenv -p /usr/bin/python3 /tmp/pypi_upload
|
||||||
|
$ cd /tmp/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 /tmp/pypi_test
|
||||||
|
$ cd /tmp/pypi_test; source bin/activate
|
||||||
|
$ pip3 install --index-url https://test.pypi.org/simple ssh-audit
|
38
pypi/setup.py
Normal file
38
pypi/setup.py
Normal 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 & client 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"
|
||||||
|
])
|
0
pypi/sshaudit/__init__.py
Normal file
0
pypi/sshaudit/__init__.py
Normal file
4
pypi/sshaudit/__main__.py
Normal file
4
pypi/sshaudit/__main__.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from .sshaudit import main
|
||||||
|
main()
|
484
ssh-audit.py
484
ssh-audit.py
@ -3,7 +3,7 @@
|
|||||||
"""
|
"""
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (C) 2017-2019 Joe Testa (jtesta@positronsecurity.com)
|
Copyright (C) 2017-2020 Joe Testa (jtesta@positronsecurity.com)
|
||||||
Copyright (C) 2017 Andris Raugulis (moo@arthepsy.eu)
|
Copyright (C) 2017 Andris Raugulis (moo@arthepsy.eu)
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
@ -25,9 +25,11 @@
|
|||||||
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.2.0'
|
||||||
|
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")
|
||||||
@ -49,7 +51,7 @@ except ImportError: # pragma: nocover
|
|||||||
pass
|
pass
|
||||||
try: # pragma: nocover
|
try: # pragma: nocover
|
||||||
from colorama import init as colorama_init
|
from colorama import init as colorama_init
|
||||||
colorama_init() # pragma: nocover
|
colorama_init(strip=False) # pragma: nocover
|
||||||
except ImportError: # pragma: nocover
|
except ImportError: # pragma: nocover
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -61,7 +63,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 +71,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 +90,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 +151,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 +175,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 +189,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 +230,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 +321,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,43 +331,79 @@ 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-gex-sha1-': [[], [], [WARN_HASH_WEAK]],
|
||||||
|
'gss-group1-sha1-': [[], [], [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-group14-sha256@ssh.com': [[]],
|
||||||
|
'diffie-hellman-group15-sha256': [[]],
|
||||||
|
'diffie-hellman-group15-sha256@ssh.com': [[]],
|
||||||
|
'diffie-hellman-group15-sha384@ssh.com': [[]],
|
||||||
'diffie-hellman-group15-sha512': [[]],
|
'diffie-hellman-group15-sha512': [[]],
|
||||||
'diffie-hellman-group16-sha256': [[]],
|
'diffie-hellman-group16-sha256': [[]],
|
||||||
|
'diffie-hellman-group16-sha384@ssh.com': [[]],
|
||||||
'diffie-hellman-group16-sha512': [['7.3,d2016.73']],
|
'diffie-hellman-group16-sha512': [['7.3,d2016.73']],
|
||||||
|
'diffie-hellman-group16-sha512@ssh.com': [[]],
|
||||||
'diffie-hellman-group17-sha512': [[]],
|
'diffie-hellman-group17-sha512': [[]],
|
||||||
'diffie-hellman-group18-sha512': [['7.3']],
|
'diffie-hellman-group18-sha512': [['7.3']],
|
||||||
|
'diffie-hellman-group18-sha512@ssh.com': [[]],
|
||||||
'diffie-hellman-group-exchange-sha1': [['2.3.0', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_HASH_WEAK]],
|
'diffie-hellman-group-exchange-sha1': [['2.3.0', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_HASH_WEAK]],
|
||||||
'diffie-hellman-group-exchange-sha256': [['4.4']],
|
'diffie-hellman-group-exchange-sha256': [['4.4']],
|
||||||
'diffie-hellman-group-exchange-sha256@ssh.com': [[]],
|
'diffie-hellman-group-exchange-sha256@ssh.com': [[]],
|
||||||
'diffie-hellman-group-exchange-sha512@ssh.com': [[]],
|
'diffie-hellman-group-exchange-sha512@ssh.com': [[]],
|
||||||
|
'ecdh-sha2-curve25519': [[], []],
|
||||||
|
'ecdh-sha2-nistb233': [[], [WARN_CURVES_WEAK]],
|
||||||
|
'ecdh-sha2-nistb409': [[], [WARN_CURVES_WEAK]],
|
||||||
|
'ecdh-sha2-nistk163': [[], [WARN_CURVES_WEAK]],
|
||||||
|
'ecdh-sha2-nistk233': [[], [WARN_CURVES_WEAK]],
|
||||||
|
'ecdh-sha2-nistk283': [[], [WARN_CURVES_WEAK]],
|
||||||
|
'ecdh-sha2-nistk409': [[], [WARN_CURVES_WEAK]],
|
||||||
|
'ecdh-sha2-nistp192': [[], [WARN_CURVES_WEAK]],
|
||||||
|
'ecdh-sha2-nistp224': [[], [WARN_CURVES_WEAK]],
|
||||||
'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-nistt571': [[], [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']],
|
||||||
'rsa-sha2-512': [['7.2']],
|
'rsa-sha2-512': [['7.2']],
|
||||||
'ssh-ed25519': [['6.5,l10.7.0']],
|
'ssh-ed25519': [['6.5,l10.7.0']],
|
||||||
'ssh-ed25519-cert-v01@openssh.com': [['6.5']],
|
'ssh-ed25519-cert-v01@openssh.com': [['6.5']],
|
||||||
'ssh-rsa': [['2.5.0,d0.28,l10.2']],
|
'ssh-rsa': [['2.5.0,d0.28,l10.2'], [WARN_HASH_WEAK]],
|
||||||
'ssh-dss': [['2.1.0,d0.28,l10.2', '6.9'], [FAIL_OPENSSH70_WEAK], [WARN_MODULUS_SIZE, WARN_RNDSIG_KEY]],
|
'ssh-dss': [['2.1.0,d0.28,l10.2', '6.9'], [FAIL_OPENSSH70_WEAK], [WARN_MODULUS_SIZE, WARN_RNDSIG_KEY]],
|
||||||
'ecdsa-sha2-nistp256': [['5.7,d2013.62,l10.6.4'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
|
'ecdsa-sha2-nistp256': [['5.7,d2013.62,l10.6.4'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
|
||||||
'ecdsa-sha2-nistp384': [['5.7,d2013.62,l10.6.4'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
|
'ecdsa-sha2-nistp384': [['5.7,d2013.62,l10.6.4'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
|
||||||
'ecdsa-sha2-nistp521': [['5.7,d2013.62,l10.6.4'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
|
'ecdsa-sha2-nistp521': [['5.7,d2013.62,l10.6.4'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
|
||||||
|
'ecdsa-sha2-1.3.132.0.10': [[], [], [WARN_RNDSIG_KEY]], # ECDSA over secp256k1 (i.e.: the Bitcoin curve)
|
||||||
|
'x509v3-sign-dss': [[], [FAIL_OPENSSH70_WEAK], [WARN_MODULUS_SIZE, WARN_RNDSIG_KEY]],
|
||||||
|
'x509v3-sign-rsa': [[], [], [WARN_HASH_WEAK]],
|
||||||
|
'x509v3-sign-rsa-sha256@ssh.com': [[]],
|
||||||
|
'x509v3-ssh-dss': [[], [FAIL_OPENSSH70_WEAK], [WARN_MODULUS_SIZE, WARN_RNDSIG_KEY]],
|
||||||
|
'x509v3-ssh-rsa': [[], [], [WARN_HASH_WEAK]],
|
||||||
'ssh-rsa-cert-v00@openssh.com': [['5.4', '6.9'], [FAIL_OPENSSH70_LEGACY], []],
|
'ssh-rsa-cert-v00@openssh.com': [['5.4', '6.9'], [FAIL_OPENSSH70_LEGACY], []],
|
||||||
'ssh-dss-cert-v00@openssh.com': [['5.4', '6.9'], [FAIL_OPENSSH70_LEGACY], [WARN_MODULUS_SIZE, WARN_RNDSIG_KEY]],
|
'ssh-dss-cert-v00@openssh.com': [['5.4', '6.9'], [FAIL_OPENSSH70_LEGACY], [WARN_MODULUS_SIZE, WARN_RNDSIG_KEY]],
|
||||||
'ssh-rsa-cert-v01@openssh.com': [['5.6']],
|
'ssh-rsa-cert-v01@openssh.com': [['5.6']],
|
||||||
@ -356,12 +411,21 @@ 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)
|
||||||
|
'sk-ecdsa-sha2-nistp256-cert-v01@openssh.com': [['8.2'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
|
||||||
|
'sk-ecdsa-sha2-nistp256@openssh.com': [['8.2'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
|
||||||
|
'sk-ssh-ed25519-cert-v01@openssh.com': [['8.2']],
|
||||||
|
'sk-ssh-ed25519@openssh.com': [['8.2']],
|
||||||
},
|
},
|
||||||
'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 +438,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]],
|
||||||
@ -393,9 +461,18 @@ class SSH2(object): # pylint: disable=too-few-public-methods
|
|||||||
'aes128-ctr': [['3.7,d0.52,l10.4.1']],
|
'aes128-ctr': [['3.7,d0.52,l10.4.1']],
|
||||||
'aes192-ctr': [['3.7,l10.4.1']],
|
'aes192-ctr': [['3.7,l10.4.1']],
|
||||||
'aes256-ctr': [['3.7,d0.52,l10.4.1']],
|
'aes256-ctr': [['3.7,d0.52,l10.4.1']],
|
||||||
|
'aes128-gcm': [[]],
|
||||||
|
'aes256-gcm': [[]],
|
||||||
'aes128-gcm@openssh.com': [['6.2']],
|
'aes128-gcm@openssh.com': [['6.2']],
|
||||||
'aes256-gcm@openssh.com': [['6.2']],
|
'aes256-gcm@openssh.com': [['6.2']],
|
||||||
|
'chacha20-poly1305': [[], [], [], [INFO_OPENSSH69_CHACHA]],
|
||||||
'chacha20-poly1305@openssh.com': [['6.5'], [], [], [INFO_OPENSSH69_CHACHA]],
|
'chacha20-poly1305@openssh.com': [['6.5'], [], [], [INFO_OPENSSH69_CHACHA]],
|
||||||
|
'camellia128-cbc': [[], [], [WARN_CIPHER_MODE]],
|
||||||
|
'camellia128-ctr': [[]],
|
||||||
|
'camellia192-cbc': [[], [], [WARN_CIPHER_MODE]],
|
||||||
|
'camellia192-ctr': [[]],
|
||||||
|
'camellia256-cbc': [[], [], [WARN_CIPHER_MODE]],
|
||||||
|
'camellia256-ctr': [[]],
|
||||||
},
|
},
|
||||||
'mac': {
|
'mac': {
|
||||||
'none': [['d2013.56'], [FAIL_PLAINTEXT]],
|
'none': [['d2013.56'], [FAIL_PLAINTEXT]],
|
||||||
@ -412,24 +489,32 @@ 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']],
|
||||||
|
'aes128-gcm': [[]],
|
||||||
|
'aes256-gcm': [[]],
|
||||||
}
|
}
|
||||||
} # type: Dict[str, Dict[str, List[List[Optional[str]]]]]
|
} # type: Dict[str, Dict[str, List[List[Optional[str]]]]]
|
||||||
|
|
||||||
@ -1193,6 +1278,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 +1293,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 +1497,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):
|
||||||
@ -1711,25 +1801,27 @@ class SSH(object): # pylint: disable=too-few-public-methods
|
|||||||
rec[sshv][alg_type] = {'add': {}, 'del': {}, 'chg': {}}
|
rec[sshv][alg_type] = {'add': {}, 'del': {}, 'chg': {}}
|
||||||
for n, alg_desc in alg_db[alg_type].items():
|
for n, alg_desc in alg_db[alg_type].items():
|
||||||
versions = alg_desc[0]
|
versions = alg_desc[0]
|
||||||
|
empty_version = False
|
||||||
if len(versions) == 0 or versions[0] is None:
|
if len(versions) == 0 or versions[0] is None:
|
||||||
continue
|
empty_version = True
|
||||||
matches = False
|
if not empty_version:
|
||||||
if unknown_software:
|
matches = False
|
||||||
matches = True
|
if unknown_software:
|
||||||
for v in versions[0].split(','):
|
matches = True
|
||||||
ssh_prefix, ssh_version, is_cli = SSH.Algorithm.get_ssh_version(v)
|
for v in versions[0].split(','):
|
||||||
if not ssh_version:
|
ssh_prefix, ssh_version, is_cli = SSH.Algorithm.get_ssh_version(v)
|
||||||
|
if not ssh_version:
|
||||||
|
continue
|
||||||
|
if (software is not None) and (ssh_prefix != software.product):
|
||||||
|
continue
|
||||||
|
if is_cli and for_server:
|
||||||
|
continue
|
||||||
|
if (software is not None) and (software.compare_version(ssh_version) < 0):
|
||||||
|
continue
|
||||||
|
matches = True
|
||||||
|
break
|
||||||
|
if not matches:
|
||||||
continue
|
continue
|
||||||
if (software is not None) and (ssh_prefix != software.product):
|
|
||||||
continue
|
|
||||||
if is_cli and for_server:
|
|
||||||
continue
|
|
||||||
if (software is not None) and (software.compare_version(ssh_version) < 0):
|
|
||||||
continue
|
|
||||||
matches = True
|
|
||||||
break
|
|
||||||
if not matches:
|
|
||||||
continue
|
|
||||||
adl, faults = len(alg_desc), 0
|
adl, faults = len(alg_desc), 0
|
||||||
for i in range(1, 3):
|
for i in range(1, 3):
|
||||||
if not adl > i:
|
if not adl > i:
|
||||||
@ -1738,13 +1830,13 @@ class SSH(object): # pylint: disable=too-few-public-methods
|
|||||||
if fc > 0:
|
if fc > 0:
|
||||||
faults += pow(10, 2 - i) * fc
|
faults += pow(10, 2 - i) * fc
|
||||||
if n not in alg_list:
|
if n not in alg_list:
|
||||||
if faults > 0 or (alg_type == 'key' and '-cert-' in n):
|
if faults > 0 or (alg_type == 'key' and '-cert-' in n) or empty_version:
|
||||||
continue
|
continue
|
||||||
rec[sshv][alg_type]['add'][n] = 0
|
rec[sshv][alg_type]['add'][n] = 0
|
||||||
else:
|
else:
|
||||||
if faults == 0:
|
if faults == 0:
|
||||||
continue
|
continue
|
||||||
if n in ['diffie-hellman-group-exchange-sha256', 'ssh-rsa', 'rsa-sha2-256', 'rsa-sha2-512', 'ssh-rsa-cert-v01@openssh.com']:
|
if n in ['diffie-hellman-group-exchange-sha256', 'rsa-sha2-256', 'rsa-sha2-512', 'ssh-rsa-cert-v01@openssh.com']:
|
||||||
rec[sshv][alg_type]['chg'][n] = faults
|
rec[sshv][alg_type]['chg'][n] = faults
|
||||||
else:
|
else:
|
||||||
rec[sshv][alg_type]['del'][n] = faults
|
rec[sshv][alg_type]['del'][n] = faults
|
||||||
@ -1900,7 +1992,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 +2024,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 +2045,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 +2073,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 +2158,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 +2323,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 +2372,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 +2694,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 +2805,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 +2836,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 +2847,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 +2867,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 +2918,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 +2968,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 +2988,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 +3030,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 +3043,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 +3066,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 +3196,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 +3279,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 +3324,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()
|
||||||
|
1
test/docker/expected_results/dropbear_2019.78_test1.json
Normal file
1
test/docker/expected_results/dropbear_2019.78_test1.json
Normal 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"]}
|
@ -1,11 +1,11 @@
|
|||||||
[0;36m# general[0m
|
[0;36m# general[0m
|
||||||
[0;32m(gen) banner: SSH-2.0-dropbear_2019.78[0m
|
[0;32m(gen) banner: SSH-2.0-dropbear_2019.78[0m
|
||||||
[0;32m(gen) software: Dropbear SSH 2019.78[0m
|
[0;32m(gen) software: Dropbear SSH 2019.78[0m
|
||||||
[0;32m(gen) compatibility: OpenSSH 7.4+ (some functionality from 6.6), Dropbear SSH 2016.73+[0m
|
[0;32m(gen) compatibility: OpenSSH 7.4+ (some functionality from 6.6), Dropbear SSH 2018.76+[0m
|
||||||
[0;32m(gen) compression: enabled (zlib@openssh.com)[0m
|
[0;32m(gen) compression: enabled (zlib@openssh.com)[0m
|
||||||
|
|
||||||
[0;36m# key exchange algorithms[0m
|
[0;36m# key exchange algorithms[0m
|
||||||
[0;32m(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4[0m
|
[0;32m(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4, Dropbear SSH 2018.76[0m
|
||||||
[0;32m(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.5, Dropbear SSH 2013.62[0m
|
[0;32m(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.5, Dropbear SSH 2013.62[0m
|
||||||
[0;31m(kex) ecdh-sha2-nistp521 -- [fail] using weak elliptic curves[0m
|
[0;31m(kex) ecdh-sha2-nistp521 -- [fail] using weak elliptic curves[0m
|
||||||
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
||||||
@ -22,7 +22,8 @@
|
|||||||
[0;31m(key) ecdsa-sha2-nistp256 -- [fail] using weak elliptic curves[0m
|
[0;31m(key) ecdsa-sha2-nistp256 -- [fail] using weak elliptic curves[0m
|
||||||
[0;33m `- [warn] using weak random number generator could reveal the key[0m
|
[0;33m `- [warn] using weak random number generator could reveal the key[0m
|
||||||
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
||||||
[0;31m(key) ssh-rsa (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
[0;31m(key) ssh-rsa (1024-bit) -- [fail] using weak hashing algorithm[0m
|
||||||
|
[0;33m `- [warn] using small 1024-bit modulus[0m
|
||||||
`- [info] available since OpenSSH 2.5.0, Dropbear SSH 0.28
|
`- [info] available since OpenSSH 2.5.0, Dropbear SSH 0.28
|
||||||
[0;31m(key) ssh-dss -- [fail] removed (in server) and disabled (in client) since OpenSSH 7.0, weak algorithm[0m
|
[0;31m(key) ssh-dss -- [fail] removed (in server) and disabled (in client) since OpenSSH 7.0, weak algorithm[0m
|
||||||
[0;33m `- [warn] using small 1024-bit modulus[0m
|
[0;33m `- [warn] using small 1024-bit modulus[0m
|
||||||
@ -63,7 +64,6 @@
|
|||||||
[0;32m(fin) ssh-rsa: SHA256:CDfAU12pjQS7/91kg7gYacza0U/6PDbE04Ic3IpYxkM[0m
|
[0;32m(fin) ssh-rsa: SHA256:CDfAU12pjQS7/91kg7gYacza0U/6PDbE04Ic3IpYxkM[0m
|
||||||
|
|
||||||
[0;36m# algorithm recommendations (for Dropbear SSH 2019.78)[0m
|
[0;36m# algorithm recommendations (for Dropbear SSH 2019.78)[0m
|
||||||
[0;31m(rec) !ssh-rsa -- key algorithm to change (increase modulus size to 2048 bits or larger) [0m
|
|
||||||
[0;31m(rec) -3des-cbc -- enc algorithm to remove [0m
|
[0;31m(rec) -3des-cbc -- enc algorithm to remove [0m
|
||||||
[0;31m(rec) -3des-ctr -- enc algorithm to remove [0m
|
[0;31m(rec) -3des-ctr -- enc algorithm to remove [0m
|
||||||
[0;31m(rec) -aes128-cbc -- enc algorithm to remove [0m
|
[0;31m(rec) -aes128-cbc -- enc algorithm to remove [0m
|
||||||
@ -71,7 +71,6 @@
|
|||||||
[0;31m(rec) -ecdh-sha2-nistp256 -- kex algorithm to remove [0m
|
[0;31m(rec) -ecdh-sha2-nistp256 -- kex algorithm to remove [0m
|
||||||
[0;31m(rec) -ecdh-sha2-nistp384 -- kex algorithm to remove [0m
|
[0;31m(rec) -ecdh-sha2-nistp384 -- kex algorithm to remove [0m
|
||||||
[0;31m(rec) -ecdh-sha2-nistp521 -- kex algorithm to remove [0m
|
[0;31m(rec) -ecdh-sha2-nistp521 -- kex algorithm to remove [0m
|
||||||
[0;31m(rec) -ecdsa-sha2-nistp256 -- key algorithm to remove [0m
|
|
||||||
[0;31m(rec) -hmac-sha1-96 -- mac algorithm to remove [0m
|
[0;31m(rec) -hmac-sha1-96 -- mac algorithm to remove [0m
|
||||||
[0;31m(rec) -ssh-dss -- key algorithm to remove [0m
|
[0;31m(rec) -ssh-dss -- key algorithm to remove [0m
|
||||||
[0;32m(rec) +diffie-hellman-group16-sha512 -- kex algorithm to append [0m
|
[0;32m(rec) +diffie-hellman-group16-sha512 -- kex algorithm to append [0m
|
||||||
@ -80,3 +79,6 @@
|
|||||||
[0;33m(rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove [0m
|
[0;33m(rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove [0m
|
||||||
[0;33m(rec) -hmac-sha1 -- mac algorithm to remove [0m
|
[0;33m(rec) -hmac-sha1 -- mac algorithm to remove [0m
|
||||||
|
|
||||||
|
[0;36m# additional info[0m
|
||||||
|
[0;33m(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>[0m
|
||||||
|
|
||||||
|
1
test/docker/expected_results/openssh_4.0p1_test1.json
Normal file
1
test/docker/expected_results/openssh_4.0p1_test1.json
Normal 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"]}
|
@ -38,7 +38,8 @@
|
|||||||
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
||||||
|
|
||||||
[0;36m# host-key algorithms[0m
|
[0;36m# host-key algorithms[0m
|
||||||
[0;31m(key) ssh-rsa (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
[0;31m(key) ssh-rsa (1024-bit) -- [fail] using weak hashing algorithm[0m
|
||||||
|
[0;33m `- [warn] using small 1024-bit modulus[0m
|
||||||
`- [info] available since OpenSSH 2.5.0, Dropbear SSH 0.28
|
`- [info] available since OpenSSH 2.5.0, Dropbear SSH 0.28
|
||||||
[0;31m(key) ssh-dss -- [fail] removed (in server) and disabled (in client) since OpenSSH 7.0, weak algorithm[0m
|
[0;31m(key) ssh-dss -- [fail] removed (in server) and disabled (in client) since OpenSSH 7.0, weak algorithm[0m
|
||||||
[0;33m `- [warn] using small 1024-bit modulus[0m
|
[0;33m `- [warn] using small 1024-bit modulus[0m
|
||||||
@ -116,7 +117,6 @@
|
|||||||
[0;32m(fin) ssh-rsa: SHA256:YZ457EBcJTSxRKI3yXRgtAj3PBf5B9/F36b1SVooml4[0m
|
[0;32m(fin) ssh-rsa: SHA256:YZ457EBcJTSxRKI3yXRgtAj3PBf5B9/F36b1SVooml4[0m
|
||||||
|
|
||||||
[0;36m# algorithm recommendations (for OpenSSH 4.0)[0m
|
[0;36m# algorithm recommendations (for OpenSSH 4.0)[0m
|
||||||
[0;31m(rec) !ssh-rsa -- key algorithm to change (increase modulus size to 2048 bits or larger) [0m
|
|
||||||
[0;31m(rec) -3des-cbc -- enc algorithm to remove [0m
|
[0;31m(rec) -3des-cbc -- enc algorithm to remove [0m
|
||||||
[0;31m(rec) -aes128-cbc -- enc algorithm to remove [0m
|
[0;31m(rec) -aes128-cbc -- enc algorithm to remove [0m
|
||||||
[0;31m(rec) -aes192-cbc -- enc algorithm to remove [0m
|
[0;31m(rec) -aes192-cbc -- enc algorithm to remove [0m
|
||||||
@ -134,3 +134,6 @@
|
|||||||
[0;31m(rec) -rijndael-cbc@lysator.liu.se -- enc algorithm to remove [0m
|
[0;31m(rec) -rijndael-cbc@lysator.liu.se -- enc algorithm to remove [0m
|
||||||
[0;31m(rec) -ssh-dss -- key algorithm to remove [0m
|
[0;31m(rec) -ssh-dss -- key algorithm to remove [0m
|
||||||
|
|
||||||
|
[0;36m# additional info[0m
|
||||||
|
[0;33m(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>[0m
|
||||||
|
|
||||||
|
1
test/docker/expected_results/openssh_5.6p1_test1.json
Normal file
1
test/docker/expected_results/openssh_5.6p1_test1.json
Normal 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"]}
|
@ -32,7 +32,8 @@
|
|||||||
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
||||||
|
|
||||||
[0;36m# host-key algorithms[0m
|
[0;36m# host-key algorithms[0m
|
||||||
[0;31m(key) ssh-rsa (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
[0;31m(key) ssh-rsa (1024-bit) -- [fail] using weak hashing algorithm[0m
|
||||||
|
[0;33m `- [warn] using small 1024-bit modulus[0m
|
||||||
`- [info] available since OpenSSH 2.5.0, Dropbear SSH 0.28
|
`- [info] available since OpenSSH 2.5.0, Dropbear SSH 0.28
|
||||||
[0;31m(key) ssh-dss -- [fail] removed (in server) and disabled (in client) since OpenSSH 7.0, weak algorithm[0m
|
[0;31m(key) ssh-dss -- [fail] removed (in server) and disabled (in client) since OpenSSH 7.0, weak algorithm[0m
|
||||||
[0;33m `- [warn] using small 1024-bit modulus[0m
|
[0;33m `- [warn] using small 1024-bit modulus[0m
|
||||||
@ -122,7 +123,6 @@
|
|||||||
|
|
||||||
[0;36m# algorithm recommendations (for OpenSSH 5.6)[0m
|
[0;36m# algorithm recommendations (for OpenSSH 5.6)[0m
|
||||||
[0;31m(rec) !diffie-hellman-group-exchange-sha256 -- kex algorithm to change (increase modulus size to 2048 bits or larger) [0m
|
[0;31m(rec) !diffie-hellman-group-exchange-sha256 -- kex algorithm to change (increase modulus size to 2048 bits or larger) [0m
|
||||||
[0;31m(rec) !ssh-rsa -- key algorithm to change (increase modulus size to 2048 bits or larger) [0m
|
|
||||||
[0;31m(rec) -3des-cbc -- enc algorithm to remove [0m
|
[0;31m(rec) -3des-cbc -- enc algorithm to remove [0m
|
||||||
[0;31m(rec) -aes128-cbc -- enc algorithm to remove [0m
|
[0;31m(rec) -aes128-cbc -- enc algorithm to remove [0m
|
||||||
[0;31m(rec) -aes192-cbc -- enc algorithm to remove [0m
|
[0;31m(rec) -aes192-cbc -- enc algorithm to remove [0m
|
||||||
@ -143,3 +143,6 @@
|
|||||||
[0;31m(rec) -ssh-dss -- key algorithm to remove [0m
|
[0;31m(rec) -ssh-dss -- key algorithm to remove [0m
|
||||||
[0;33m(rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove [0m
|
[0;33m(rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove [0m
|
||||||
|
|
||||||
|
[0;36m# additional info[0m
|
||||||
|
[0;33m(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>[0m
|
||||||
|
|
||||||
|
1
test/docker/expected_results/openssh_5.6p1_test2.json
Normal file
1
test/docker/expected_results/openssh_5.6p1_test2.json
Normal 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"]}
|
@ -32,7 +32,8 @@
|
|||||||
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
||||||
|
|
||||||
[0;36m# host-key algorithms[0m
|
[0;36m# host-key algorithms[0m
|
||||||
[0;31m(key) ssh-rsa (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
[0;31m(key) ssh-rsa (1024-bit) -- [fail] using weak hashing algorithm[0m
|
||||||
|
[0;33m `- [warn] using small 1024-bit modulus[0m
|
||||||
`- [info] available since OpenSSH 2.5.0, Dropbear SSH 0.28
|
`- [info] available since OpenSSH 2.5.0, Dropbear SSH 0.28
|
||||||
[0;31m(key) ssh-rsa-cert-v01@openssh.com (1024-bit cert/1024-bit CA) -- [fail] using small 1024-bit modulus[0m
|
[0;31m(key) ssh-rsa-cert-v01@openssh.com (1024-bit cert/1024-bit CA) -- [fail] using small 1024-bit modulus[0m
|
||||||
`- [info] available since OpenSSH 5.6
|
`- [info] available since OpenSSH 5.6
|
||||||
@ -120,7 +121,6 @@
|
|||||||
|
|
||||||
[0;36m# algorithm recommendations (for OpenSSH 5.6)[0m
|
[0;36m# algorithm recommendations (for OpenSSH 5.6)[0m
|
||||||
[0;31m(rec) !diffie-hellman-group-exchange-sha256 -- kex algorithm to change (increase modulus size to 2048 bits or larger) [0m
|
[0;31m(rec) !diffie-hellman-group-exchange-sha256 -- kex algorithm to change (increase modulus size to 2048 bits or larger) [0m
|
||||||
[0;31m(rec) !ssh-rsa -- key algorithm to change (increase modulus size to 2048 bits or larger) [0m
|
|
||||||
[0;31m(rec) !ssh-rsa-cert-v01@openssh.com -- key algorithm to change (increase modulus size to 2048 bits or larger) [0m
|
[0;31m(rec) !ssh-rsa-cert-v01@openssh.com -- key algorithm to change (increase modulus size to 2048 bits or larger) [0m
|
||||||
[0;31m(rec) -3des-cbc -- enc algorithm to remove [0m
|
[0;31m(rec) -3des-cbc -- enc algorithm to remove [0m
|
||||||
[0;31m(rec) -aes128-cbc -- enc algorithm to remove [0m
|
[0;31m(rec) -aes128-cbc -- enc algorithm to remove [0m
|
||||||
@ -139,5 +139,9 @@
|
|||||||
[0;31m(rec) -hmac-ripemd160@openssh.com -- mac algorithm to remove [0m
|
[0;31m(rec) -hmac-ripemd160@openssh.com -- mac algorithm to remove [0m
|
||||||
[0;31m(rec) -hmac-sha1-96 -- mac algorithm to remove [0m
|
[0;31m(rec) -hmac-sha1-96 -- mac algorithm to remove [0m
|
||||||
[0;31m(rec) -rijndael-cbc@lysator.liu.se -- enc algorithm to remove [0m
|
[0;31m(rec) -rijndael-cbc@lysator.liu.se -- enc algorithm to remove [0m
|
||||||
|
[0;31m(rec) -ssh-rsa -- key algorithm to remove [0m
|
||||||
[0;33m(rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove [0m
|
[0;33m(rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove [0m
|
||||||
|
|
||||||
|
[0;36m# additional info[0m
|
||||||
|
[0;33m(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>[0m
|
||||||
|
|
||||||
|
1
test/docker/expected_results/openssh_5.6p1_test3.json
Normal file
1
test/docker/expected_results/openssh_5.6p1_test3.json
Normal 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"]}
|
@ -32,7 +32,8 @@
|
|||||||
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
||||||
|
|
||||||
[0;36m# host-key algorithms[0m
|
[0;36m# host-key algorithms[0m
|
||||||
[0;31m(key) ssh-rsa (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
[0;31m(key) ssh-rsa (1024-bit) -- [fail] using weak hashing algorithm[0m
|
||||||
|
[0;33m `- [warn] using small 1024-bit modulus[0m
|
||||||
`- [info] available since OpenSSH 2.5.0, Dropbear SSH 0.28
|
`- [info] available since OpenSSH 2.5.0, Dropbear SSH 0.28
|
||||||
[0;31m(key) ssh-rsa-cert-v01@openssh.com (1024-bit cert/3072-bit CA) -- [fail] using small 1024-bit modulus[0m
|
[0;31m(key) ssh-rsa-cert-v01@openssh.com (1024-bit cert/3072-bit CA) -- [fail] using small 1024-bit modulus[0m
|
||||||
`- [info] available since OpenSSH 5.6
|
`- [info] available since OpenSSH 5.6
|
||||||
@ -120,7 +121,6 @@
|
|||||||
|
|
||||||
[0;36m# algorithm recommendations (for OpenSSH 5.6)[0m
|
[0;36m# algorithm recommendations (for OpenSSH 5.6)[0m
|
||||||
[0;31m(rec) !diffie-hellman-group-exchange-sha256 -- kex algorithm to change (increase modulus size to 2048 bits or larger) [0m
|
[0;31m(rec) !diffie-hellman-group-exchange-sha256 -- kex algorithm to change (increase modulus size to 2048 bits or larger) [0m
|
||||||
[0;31m(rec) !ssh-rsa -- key algorithm to change (increase modulus size to 2048 bits or larger) [0m
|
|
||||||
[0;31m(rec) !ssh-rsa-cert-v01@openssh.com -- key algorithm to change (increase modulus size to 2048 bits or larger) [0m
|
[0;31m(rec) !ssh-rsa-cert-v01@openssh.com -- key algorithm to change (increase modulus size to 2048 bits or larger) [0m
|
||||||
[0;31m(rec) -3des-cbc -- enc algorithm to remove [0m
|
[0;31m(rec) -3des-cbc -- enc algorithm to remove [0m
|
||||||
[0;31m(rec) -aes128-cbc -- enc algorithm to remove [0m
|
[0;31m(rec) -aes128-cbc -- enc algorithm to remove [0m
|
||||||
@ -139,5 +139,9 @@
|
|||||||
[0;31m(rec) -hmac-ripemd160@openssh.com -- mac algorithm to remove [0m
|
[0;31m(rec) -hmac-ripemd160@openssh.com -- mac algorithm to remove [0m
|
||||||
[0;31m(rec) -hmac-sha1-96 -- mac algorithm to remove [0m
|
[0;31m(rec) -hmac-sha1-96 -- mac algorithm to remove [0m
|
||||||
[0;31m(rec) -rijndael-cbc@lysator.liu.se -- enc algorithm to remove [0m
|
[0;31m(rec) -rijndael-cbc@lysator.liu.se -- enc algorithm to remove [0m
|
||||||
|
[0;31m(rec) -ssh-rsa -- key algorithm to remove [0m
|
||||||
[0;33m(rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove [0m
|
[0;33m(rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove [0m
|
||||||
|
|
||||||
|
[0;36m# additional info[0m
|
||||||
|
[0;33m(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>[0m
|
||||||
|
|
||||||
|
1
test/docker/expected_results/openssh_5.6p1_test4.json
Normal file
1
test/docker/expected_results/openssh_5.6p1_test4.json
Normal 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"]}
|
@ -32,7 +32,8 @@
|
|||||||
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
||||||
|
|
||||||
[0;36m# host-key algorithms[0m
|
[0;36m# host-key algorithms[0m
|
||||||
[0;32m(key) ssh-rsa (3072-bit) -- [info] available since OpenSSH 2.5.0, Dropbear SSH 0.28[0m
|
[0;31m(key) ssh-rsa (3072-bit) -- [fail] using weak hashing algorithm[0m
|
||||||
|
`- [info] available since OpenSSH 2.5.0, Dropbear SSH 0.28
|
||||||
[0;31m(key) ssh-rsa-cert-v01@openssh.com (3072-bit cert/1024-bit CA) -- [fail] using small 1024-bit modulus[0m
|
[0;31m(key) ssh-rsa-cert-v01@openssh.com (3072-bit cert/1024-bit CA) -- [fail] using small 1024-bit modulus[0m
|
||||||
`- [info] available since OpenSSH 5.6
|
`- [info] available since OpenSSH 5.6
|
||||||
|
|
||||||
@ -137,5 +138,9 @@
|
|||||||
[0;31m(rec) -hmac-ripemd160@openssh.com -- mac algorithm to remove [0m
|
[0;31m(rec) -hmac-ripemd160@openssh.com -- mac algorithm to remove [0m
|
||||||
[0;31m(rec) -hmac-sha1-96 -- mac algorithm to remove [0m
|
[0;31m(rec) -hmac-sha1-96 -- mac algorithm to remove [0m
|
||||||
[0;31m(rec) -rijndael-cbc@lysator.liu.se -- enc algorithm to remove [0m
|
[0;31m(rec) -rijndael-cbc@lysator.liu.se -- enc algorithm to remove [0m
|
||||||
|
[0;31m(rec) -ssh-rsa -- key algorithm to remove [0m
|
||||||
[0;33m(rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove [0m
|
[0;33m(rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove [0m
|
||||||
|
|
||||||
|
[0;36m# additional info[0m
|
||||||
|
[0;33m(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>[0m
|
||||||
|
|
||||||
|
1
test/docker/expected_results/openssh_5.6p1_test5.json
Normal file
1
test/docker/expected_results/openssh_5.6p1_test5.json
Normal 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"]}
|
@ -32,7 +32,8 @@
|
|||||||
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
||||||
|
|
||||||
[0;36m# host-key algorithms[0m
|
[0;36m# host-key algorithms[0m
|
||||||
[0;32m(key) ssh-rsa (3072-bit) -- [info] available since OpenSSH 2.5.0, Dropbear SSH 0.28[0m
|
[0;31m(key) ssh-rsa (3072-bit) -- [fail] using weak hashing algorithm[0m
|
||||||
|
`- [info] available since OpenSSH 2.5.0, Dropbear SSH 0.28
|
||||||
[0;32m(key) ssh-rsa-cert-v01@openssh.com (3072-bit cert/3072-bit CA) -- [info] available since OpenSSH 5.6[0m
|
[0;32m(key) ssh-rsa-cert-v01@openssh.com (3072-bit cert/3072-bit CA) -- [info] available since OpenSSH 5.6[0m
|
||||||
|
|
||||||
[0;36m# encryption algorithms (ciphers)[0m
|
[0;36m# encryption algorithms (ciphers)[0m
|
||||||
@ -135,5 +136,9 @@
|
|||||||
[0;31m(rec) -hmac-ripemd160@openssh.com -- mac algorithm to remove [0m
|
[0;31m(rec) -hmac-ripemd160@openssh.com -- mac algorithm to remove [0m
|
||||||
[0;31m(rec) -hmac-sha1-96 -- mac algorithm to remove [0m
|
[0;31m(rec) -hmac-sha1-96 -- mac algorithm to remove [0m
|
||||||
[0;31m(rec) -rijndael-cbc@lysator.liu.se -- enc algorithm to remove [0m
|
[0;31m(rec) -rijndael-cbc@lysator.liu.se -- enc algorithm to remove [0m
|
||||||
|
[0;31m(rec) -ssh-rsa -- key algorithm to remove [0m
|
||||||
[0;33m(rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove [0m
|
[0;33m(rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove [0m
|
||||||
|
|
||||||
|
[0;36m# additional info[0m
|
||||||
|
[0;33m(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>[0m
|
||||||
|
|
||||||
|
1
test/docker/expected_results/openssh_8.0p1_test1.json
Normal file
1
test/docker/expected_results/openssh_8.0p1_test1.json
Normal 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"]}
|
@ -1,11 +1,11 @@
|
|||||||
[0;36m# general[0m
|
[0;36m# general[0m
|
||||||
[0;32m(gen) banner: SSH-2.0-OpenSSH_8.0[0m
|
[0;32m(gen) banner: SSH-2.0-OpenSSH_8.0[0m
|
||||||
[0;32m(gen) software: OpenSSH 8.0[0m
|
[0;32m(gen) software: OpenSSH 8.0[0m
|
||||||
[0;32m(gen) compatibility: OpenSSH 7.4+, Dropbear SSH 2016.73+[0m
|
[0;32m(gen) compatibility: OpenSSH 7.4+, Dropbear SSH 2018.76+[0m
|
||||||
[0;32m(gen) compression: enabled (zlib@openssh.com)[0m
|
[0;32m(gen) compression: enabled (zlib@openssh.com)[0m
|
||||||
|
|
||||||
[0;36m# key exchange algorithms[0m
|
[0;36m# key exchange algorithms[0m
|
||||||
[0;32m(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4[0m
|
[0;32m(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4, Dropbear SSH 2018.76[0m
|
||||||
[0;32m(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.5, Dropbear SSH 2013.62[0m
|
[0;32m(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.5, Dropbear SSH 2013.62[0m
|
||||||
[0;31m(kex) ecdh-sha2-nistp256 -- [fail] using weak elliptic curves[0m
|
[0;31m(kex) ecdh-sha2-nistp256 -- [fail] using weak elliptic curves[0m
|
||||||
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
||||||
@ -23,7 +23,8 @@
|
|||||||
[0;36m# host-key algorithms[0m
|
[0;36m# host-key algorithms[0m
|
||||||
[0;32m(key) rsa-sha2-512 (3072-bit) -- [info] available since OpenSSH 7.2[0m
|
[0;32m(key) rsa-sha2-512 (3072-bit) -- [info] available since OpenSSH 7.2[0m
|
||||||
[0;32m(key) rsa-sha2-256 (3072-bit) -- [info] available since OpenSSH 7.2[0m
|
[0;32m(key) rsa-sha2-256 (3072-bit) -- [info] available since OpenSSH 7.2[0m
|
||||||
[0;32m(key) ssh-rsa (3072-bit) -- [info] available since OpenSSH 2.5.0, Dropbear SSH 0.28[0m
|
[0;31m(key) ssh-rsa (3072-bit) -- [fail] using weak hashing algorithm[0m
|
||||||
|
`- [info] available since OpenSSH 2.5.0, Dropbear SSH 0.28
|
||||||
[0;31m(key) ecdsa-sha2-nistp256 -- [fail] using weak elliptic curves[0m
|
[0;31m(key) ecdsa-sha2-nistp256 -- [fail] using weak elliptic curves[0m
|
||||||
[0;33m `- [warn] using weak random number generator could reveal the key[0m
|
[0;33m `- [warn] using weak random number generator could reveal the key[0m
|
||||||
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
||||||
@ -68,6 +69,7 @@
|
|||||||
[0;31m(rec) -ecdh-sha2-nistp384 -- kex algorithm to remove [0m
|
[0;31m(rec) -ecdh-sha2-nistp384 -- kex algorithm to remove [0m
|
||||||
[0;31m(rec) -ecdh-sha2-nistp521 -- kex algorithm to remove [0m
|
[0;31m(rec) -ecdh-sha2-nistp521 -- kex algorithm to remove [0m
|
||||||
[0;31m(rec) -ecdsa-sha2-nistp256 -- key algorithm to remove [0m
|
[0;31m(rec) -ecdsa-sha2-nistp256 -- key algorithm to remove [0m
|
||||||
|
[0;31m(rec) -ssh-rsa -- key algorithm to remove [0m
|
||||||
[0;33m(rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove [0m
|
[0;33m(rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove [0m
|
||||||
[0;33m(rec) -hmac-sha1 -- mac algorithm to remove [0m
|
[0;33m(rec) -hmac-sha1 -- mac algorithm to remove [0m
|
||||||
[0;33m(rec) -hmac-sha1-etm@openssh.com -- mac algorithm to remove [0m
|
[0;33m(rec) -hmac-sha1-etm@openssh.com -- mac algorithm to remove [0m
|
||||||
@ -77,3 +79,6 @@
|
|||||||
[0;33m(rec) -umac-64-etm@openssh.com -- mac algorithm to remove [0m
|
[0;33m(rec) -umac-64-etm@openssh.com -- mac algorithm to remove [0m
|
||||||
[0;33m(rec) -umac-64@openssh.com -- mac algorithm to remove [0m
|
[0;33m(rec) -umac-64@openssh.com -- mac algorithm to remove [0m
|
||||||
|
|
||||||
|
[0;36m# additional info[0m
|
||||||
|
[0;33m(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>[0m
|
||||||
|
|
||||||
|
1
test/docker/expected_results/openssh_8.0p1_test2.json
Normal file
1
test/docker/expected_results/openssh_8.0p1_test2.json
Normal 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"]}
|
@ -1,11 +1,11 @@
|
|||||||
[0;36m# general[0m
|
[0;36m# general[0m
|
||||||
[0;32m(gen) banner: SSH-2.0-OpenSSH_8.0[0m
|
[0;32m(gen) banner: SSH-2.0-OpenSSH_8.0[0m
|
||||||
[0;32m(gen) software: OpenSSH 8.0[0m
|
[0;32m(gen) software: OpenSSH 8.0[0m
|
||||||
[0;32m(gen) compatibility: OpenSSH 7.4+, Dropbear SSH 2016.73+[0m
|
[0;32m(gen) compatibility: OpenSSH 7.4+, Dropbear SSH 2018.76+[0m
|
||||||
[0;32m(gen) compression: enabled (zlib@openssh.com)[0m
|
[0;32m(gen) compression: enabled (zlib@openssh.com)[0m
|
||||||
|
|
||||||
[0;36m# key exchange algorithms[0m
|
[0;36m# key exchange algorithms[0m
|
||||||
[0;32m(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4[0m
|
[0;32m(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4, Dropbear SSH 2018.76[0m
|
||||||
[0;32m(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.5, Dropbear SSH 2013.62[0m
|
[0;32m(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.5, Dropbear SSH 2013.62[0m
|
||||||
[0;31m(kex) ecdh-sha2-nistp256 -- [fail] using weak elliptic curves[0m
|
[0;31m(kex) ecdh-sha2-nistp256 -- [fail] using weak elliptic curves[0m
|
||||||
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
||||||
@ -63,7 +63,6 @@
|
|||||||
[0;31m(rec) -ecdh-sha2-nistp521 -- kex algorithm to remove [0m
|
[0;31m(rec) -ecdh-sha2-nistp521 -- kex algorithm to remove [0m
|
||||||
[0;32m(rec) +rsa-sha2-256 -- key algorithm to append [0m
|
[0;32m(rec) +rsa-sha2-256 -- key algorithm to append [0m
|
||||||
[0;32m(rec) +rsa-sha2-512 -- key algorithm to append [0m
|
[0;32m(rec) +rsa-sha2-512 -- key algorithm to append [0m
|
||||||
[0;32m(rec) +ssh-rsa -- key algorithm to append [0m
|
|
||||||
[0;33m(rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove [0m
|
[0;33m(rec) -diffie-hellman-group14-sha1 -- kex algorithm to remove [0m
|
||||||
[0;33m(rec) -hmac-sha1 -- mac algorithm to remove [0m
|
[0;33m(rec) -hmac-sha1 -- mac algorithm to remove [0m
|
||||||
[0;33m(rec) -hmac-sha1-etm@openssh.com -- mac algorithm to remove [0m
|
[0;33m(rec) -hmac-sha1-etm@openssh.com -- mac algorithm to remove [0m
|
||||||
@ -73,3 +72,6 @@
|
|||||||
[0;33m(rec) -umac-64-etm@openssh.com -- mac algorithm to remove [0m
|
[0;33m(rec) -umac-64-etm@openssh.com -- mac algorithm to remove [0m
|
||||||
[0;33m(rec) -umac-64@openssh.com -- mac algorithm to remove [0m
|
[0;33m(rec) -umac-64@openssh.com -- mac algorithm to remove [0m
|
||||||
|
|
||||||
|
[0;36m# additional info[0m
|
||||||
|
[0;33m(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>[0m
|
||||||
|
|
||||||
|
1
test/docker/expected_results/openssh_8.0p1_test3.json
Normal file
1
test/docker/expected_results/openssh_8.0p1_test3.json
Normal 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"]}
|
@ -1,11 +1,11 @@
|
|||||||
[0;36m# general[0m
|
[0;36m# general[0m
|
||||||
[0;32m(gen) banner: SSH-2.0-OpenSSH_8.0[0m
|
[0;32m(gen) banner: SSH-2.0-OpenSSH_8.0[0m
|
||||||
[0;32m(gen) software: OpenSSH 8.0[0m
|
[0;32m(gen) software: OpenSSH 8.0[0m
|
||||||
[0;32m(gen) compatibility: OpenSSH 7.4+, Dropbear SSH 2013.62+[0m
|
[0;32m(gen) compatibility: OpenSSH 7.4+, Dropbear SSH 2018.76+[0m
|
||||||
[0;32m(gen) compression: enabled (zlib@openssh.com)[0m
|
[0;32m(gen) compression: enabled (zlib@openssh.com)[0m
|
||||||
|
|
||||||
[0;36m# key exchange algorithms[0m
|
[0;36m# key exchange algorithms[0m
|
||||||
[0;32m(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4[0m
|
[0;32m(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4, Dropbear SSH 2018.76[0m
|
||||||
[0;32m(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.5, Dropbear SSH 2013.62[0m
|
[0;32m(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.5, Dropbear SSH 2013.62[0m
|
||||||
[0;32m(kex) diffie-hellman-group-exchange-sha256 (2048-bit) -- [info] available since OpenSSH 4.4[0m
|
[0;32m(kex) diffie-hellman-group-exchange-sha256 (2048-bit) -- [info] available since OpenSSH 4.4[0m
|
||||||
|
|
||||||
@ -35,5 +35,4 @@
|
|||||||
[0;32m(rec) +diffie-hellman-group18-sha512 -- kex algorithm to append [0m
|
[0;32m(rec) +diffie-hellman-group18-sha512 -- kex algorithm to append [0m
|
||||||
[0;32m(rec) +rsa-sha2-256 -- key algorithm to append [0m
|
[0;32m(rec) +rsa-sha2-256 -- key algorithm to append [0m
|
||||||
[0;32m(rec) +rsa-sha2-512 -- key algorithm to append [0m
|
[0;32m(rec) +rsa-sha2-512 -- key algorithm to append [0m
|
||||||
[0;32m(rec) +ssh-rsa -- key algorithm to append [0m
|
|
||||||
|
|
||||||
|
1
test/docker/expected_results/tinyssh_20190101_test1.json
Normal file
1
test/docker/expected_results/tinyssh_20190101_test1.json
Normal 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"]}
|
@ -1,10 +1,10 @@
|
|||||||
[0;36m# general[0m
|
[0;36m# general[0m
|
||||||
[0;32m(gen) software: TinySSH noversion[0m
|
[0;32m(gen) software: TinySSH noversion[0m
|
||||||
[0;32m(gen) compatibility: OpenSSH 8.0+, Dropbear SSH 2013.62+[0m
|
[0;32m(gen) compatibility: OpenSSH 8.0+, Dropbear SSH 2018.76+[0m
|
||||||
[0;32m(gen) compression: disabled[0m
|
[0;32m(gen) compression: disabled[0m
|
||||||
|
|
||||||
[0;36m# key exchange algorithms[0m
|
[0;36m# key exchange algorithms[0m
|
||||||
[0;32m(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4[0m
|
[0;32m(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4, Dropbear SSH 2018.76[0m
|
||||||
[0;32m(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.5, Dropbear SSH 2013.62[0m
|
[0;32m(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.5, Dropbear SSH 2013.62[0m
|
||||||
[0;33m(kex) sntrup4591761x25519-sha512@tinyssh.org -- [warn] using experimental algorithm[0m
|
[0;33m(kex) sntrup4591761x25519-sha512@tinyssh.org -- [warn] using experimental algorithm[0m
|
||||||
`- [info] available since OpenSSH 8.0
|
`- [info] available since OpenSSH 8.0
|
||||||
|
17
windows_build.txt
Normal file
17
windows_build.txt
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
Below are notes for creating a Windows executable.
|
||||||
|
|
||||||
|
An executable can only be made on a Windows host because the PyInstaller tool (https://www.pyinstaller.org/) does not support cross-compilation.
|
||||||
|
|
||||||
|
On a Windows machine, do the following:
|
||||||
|
|
||||||
|
1.) Install Python v3.7.x from https://www.python.org/. (As of this writing v3.8.0 isn't supported.) To make life easier, check the option to add Python to the PATH environment variable.
|
||||||
|
|
||||||
|
2.) Using pip, install pyinstaller and colorama:
|
||||||
|
|
||||||
|
pip install pyinstaller colorama
|
||||||
|
|
||||||
|
3.) Create the executable with:
|
||||||
|
|
||||||
|
pyinstaller -F --icon windows_icon.ico ssh-audit.py
|
||||||
|
|
||||||
|
4.) The 'dist' folder will have the resulting ssh-audit.exe.
|
BIN
windows_icon.ico
Normal file
BIN
windows_icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
Reference in New Issue
Block a user