4 Commits

8 changed files with 27 additions and 21 deletions
+3 -3
View File
@@ -7,12 +7,12 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13.0-rc.2"]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2 uses: actions/setup-python@v5
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- name: Install dependencies - name: Install dependencies
+4 -1
View File
@@ -214,13 +214,16 @@ For convenience, a web front-end on top of the command-line tool is available at
## ChangeLog ## ChangeLog
### v3.3.0-dev (???) ### v3.3.0-dev (???)
- Added built-in policies for Ubuntu 24.04 LTS server and client, and OpenSSH 9.8. - Added Python 3.13 support.
- Added built-in policies for Ubuntu 24.04 LTS server & client, OpenSSH 9.8, and OpenSSH 9.9.
- Added IPv6 support for DHEat and connection rate tests. - Added IPv6 support for DHEat and connection rate tests.
- Added TCP port information to JSON policy scan results; credit [Fabian Malte Kopp](https://github.com/dreizehnutters). - Added TCP port information to JSON policy scan results; credit [Fabian Malte Kopp](https://github.com/dreizehnutters).
- Added LANcom LCOS server recognition and Ed448 key extraction; credit [Daniel Lenski](https://github.com/dlenskiSB). - Added LANcom LCOS server recognition and Ed448 key extraction; credit [Daniel Lenski](https://github.com/dlenskiSB).
- Fixed crash when running with `-P` and `-T` options simultaneously. - Fixed crash when running with `-P` and `-T` options simultaneously.
- Fixed host key tests from only reporting a key type at most once despite multiple hosts supporting it; credit [Daniel Lenski](https://github.com/dlenskiSB). - Fixed host key tests from only reporting a key type at most once despite multiple hosts supporting it; credit [Daniel Lenski](https://github.com/dlenskiSB).
- Fixed DHEat connection rate testing on MacOS X and BSD platforms; credit [Drew Noel](https://github.com/drewmnoel) and [Michael Osipov](https://github.com/michael-o). - Fixed DHEat connection rate testing on MacOS X and BSD platforms; credit [Drew Noel](https://github.com/drewmnoel) and [Michael Osipov](https://github.com/michael-o).
- Fixed invalid JSON output when a socket error occurs while performing a client audit.
- When scanning multiple targets (using `-T`/`--targets`), the `-p`/`--port` option will now be used as the default port (set to 22 if `-p`/`--port` is not given). Hosts specified in the file can override this default with an explicit port number (i.e.: "host1:1234"). For example, when using `-T targets.txt -p 222`, all hosts in `targets.txt` that do not explicitly include a port number will default to 222; when using `-T targets.txt` (without `-p`), all hosts will use a default of 22.
- Added 1 new cipher: `grasshopper-ctr128`. - Added 1 new cipher: `grasshopper-ctr128`.
- Added 2 new key exchanges: `mlkem768x25519-sha256`, `sntrup761x25519-sha512`. - Added 2 new key exchanges: `mlkem768x25519-sha256`, `sntrup761x25519-sha512`.
+1
View File
@@ -24,6 +24,7 @@ classifiers =
Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11 Programming Language :: Python :: 3.11
Programming Language :: Python :: 3.12 Programming Language :: Python :: 3.12
Programming Language :: Python :: 3.13
Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: Implementation :: CPython
Programming Language :: Python :: Implementation :: PyPy Programming Language :: Python :: Implementation :: PyPy
Topic :: Security Topic :: Security
+1
View File
@@ -97,6 +97,7 @@ BUILTIN_POLICIES: Dict[str, Dict[str, Union[Optional[str], Optional[List[str]],
'Hardened OpenSSH Server v9.8 (version 1)': {'version': '1', 'changelog': 'Initial version.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256', 'ext-info-s', 'kex-strict-s-v00@openssh.com'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True}, 'Hardened OpenSSH Server v9.8 (version 1)': {'version': '1', 'changelog': 'Initial version.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256', 'ext-info-s', 'kex-strict-s-v00@openssh.com'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
'Hardened OpenSSH Server v9.9 (version 1)': {'version': '1', 'changelog': 'Initial version.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512', 'sntrup761x25519-sha512@openssh.com', 'mlkem768x25519-sha256', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256', 'ext-info-s', 'kex-strict-s-v00@openssh.com'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
# Amazon Linux Policies # Amazon Linux Policies
+4 -4
View File
@@ -25,7 +25,7 @@
""" """
import concurrent.futures import concurrent.futures
import copy import copy
import getopt import getopt # pylint: disable=deprecated-module
import json import json
import multiprocessing import multiprocessing
import os import os
@@ -130,7 +130,7 @@ def usage(uout: OutputBuffer, err: Optional[str] = None) -> None:
uout.info(' -P, --policy=<policy.txt> run a policy test using the specified policy') uout.info(' -P, --policy=<policy.txt> run a policy test using the specified policy')
uout.info(' --skip-rate-test skip the connection rate test during standard audits\n (used to safely infer whether the DHEat attack\n is viable)') uout.info(' --skip-rate-test skip the connection rate test during standard audits\n (used to safely infer whether the DHEat attack\n is viable)')
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.info(' -T, --targets=<hosts.txt> a file containing a list of target hosts (one\n per line, format HOST[:PORT]). Use --threads\n to control concurrent scans.') uout.info(' -T, --targets=<hosts.txt> a file containing a list of target hosts (one\n per line, format HOST[:PORT]). Use -p/--port\n to set the default port for all hosts. Use\n --threads to control concurrent scans.')
uout.info(' --threads=<threads> number of threads to use when scanning multiple\n targets (-T/--targets) (default: 32)') uout.info(' --threads=<threads> number of threads to use when scanning multiple\n targets (-T/--targets) (default: 32)')
uout.info(' -v, --verbose verbose output') uout.info(' -v, --verbose verbose output')
uout.sep() uout.sep()
@@ -1587,10 +1587,10 @@ def main() -> int:
if aconf.json: if aconf.json:
print('[', end='') print('[', end='')
# Loop through each target in the list. # Loop through each target in the list. Entries can specify a port number to use, otherwise the value provided on the command line (--port=N) will be used by default (set to 22 if --port is not used).
target_servers = [] target_servers = []
for _, target in enumerate(aconf.target_list): for _, target in enumerate(aconf.target_list):
host, port = Utils.parse_host_and_port(target, default_port=22) host, port = Utils.parse_host_and_port(target, default_port=aconf.port)
target_servers.append((host, port)) target_servers.append((host, port))
# A ranked list of return codes. Those with higher indices will take precedence over lower ones. For example, if three servers are scanned, yielding WARNING, GOOD, and UNKNOWN_ERROR, the overall result will be UNKNOWN_ERROR, since its index is the highest. Errors have highest priority, followed by failures, then warnings. # A ranked list of return codes. Those with higher indices will take precedence over lower ones. For example, if three servers are scanned, yielding WARNING, GOOD, and UNKNOWN_ERROR, the overall result will be UNKNOWN_ERROR, since its index is the highest. Errors have highest priority, followed by failures, then warnings.
+3 -3
View File
@@ -108,7 +108,7 @@ class SSH_Socket(ReadBuf, WriteBuf):
s.listen() s.listen()
self.__sock_map[s.fileno()] = s self.__sock_map[s.fileno()] = s
except Exception as e: except Exception as e:
print("Warning: failed to listen on any IPv4 interfaces: %s" % str(e)) print("Warning: failed to listen on any IPv4 interfaces: %s" % str(e), file=sys.stderr)
try: try:
# Socket to listen on all IPv6 addresses. # Socket to listen on all IPv6 addresses.
@@ -119,11 +119,11 @@ class SSH_Socket(ReadBuf, WriteBuf):
s.listen() s.listen()
self.__sock_map[s.fileno()] = s self.__sock_map[s.fileno()] = s
except Exception as e: except Exception as e:
print("Warning: failed to listen on any IPv6 interfaces: %s" % str(e)) print("Warning: failed to listen on any IPv6 interfaces: %s" % str(e), file=sys.stderr)
# If we failed to listen on any interfaces, terminate. # If we failed to listen on any interfaces, terminate.
if len(self.__sock_map.keys()) == 0: if len(self.__sock_map.keys()) == 0:
print("Error: failed to listen on any IPv4 and IPv6 interfaces!") print("Error: failed to listen on any IPv4 and IPv6 interfaces!", file=sys.stderr)
sys.exit(exitcodes.CONNECTION_ERROR) sys.exit(exitcodes.CONNECTION_ERROR)
# Wait for an incoming connection. If a timeout was explicitly # Wait for an incoming connection. If a timeout was explicitly
+2 -2
View File
@@ -1,4 +1,4 @@
.TH SSH-AUDIT 1 "April 18, 2024" .TH SSH-AUDIT 1 "September 24, 2024"
.SH NAME .SH NAME
\fBssh-audit\fP \- SSH server & client configuration auditor \fBssh-audit\fP \- SSH server & client configuration auditor
.SH SYNOPSIS .SH SYNOPSIS
@@ -149,7 +149,7 @@ The timeout, in seconds, for creating connections and reading data from the sock
.TP .TP
.B -T, \-\-targets=<hosts.txt> .B -T, \-\-targets=<hosts.txt>
.br .br
A file containing a list of target hosts. Each line must have one host, in the format of HOST[:PORT]. Use --threads to control concurrent scans. A file containing a list of target hosts. Each line must have one host, in the format of HOST[:PORT]. Use -p/--port to set the default port for all hosts. Use --threads to control concurrent scans.
.TP .TP
.B \-\-threads=<threads> .B \-\-threads=<threads>
+9 -8
View File
@@ -1,7 +1,7 @@
[tox] [tox]
envlist = envlist =
py{py3}-{test,pylint,flake8} py{py3}-{test,pylint,flake8}
py{38,39,310,311,312}-{test,mypy,pylint,flake8} py{38,39,310,311,312,313}-{test,mypy,pylint,flake8}
cov cov
skip_missing_interpreters = true skip_missing_interpreters = true
@@ -9,10 +9,10 @@ skip_missing_interpreters = true
deps = deps =
test: pytest test: pytest
test,cov: {[testenv:cov]deps} test,cov: {[testenv:cov]deps}
test,py{38,39,310,311,312}-{type,mypy}: colorama test,py{38,39,310,311,312,313}-{type,mypy}: colorama
py{38,39,310,311,312}-{type,mypy}: {[testenv:mypy]deps} py{38,39,310,311,312,313}-{type,mypy}: {[testenv:mypy]deps}
py{py3,38,39,310,311,312}-{lint,pylint},lint: {[testenv:pylint]deps} py{py3,38,39,310,311,312,313}-{lint,pylint},lint: {[testenv:pylint]deps}
py{py3,38,39,310,311,312}-{lint,flake8},lint: {[testenv:flake8]deps} py{py3,38,39,310,311,312,313}-{lint,flake8},lint: {[testenv:flake8]deps}
setenv = setenv =
SSHAUDIT = {toxinidir}/src SSHAUDIT = {toxinidir}/src
test: COVERAGE_FILE = {toxinidir}/.coverage.{envname} test: COVERAGE_FILE = {toxinidir}/.coverage.{envname}
@@ -24,9 +24,9 @@ commands =
test: coverage combine test: coverage combine
test: coverage report --show-missing test: coverage report --show-missing
test: coverage html -d {toxinidir}/reports/html/coverage.{envname} test: coverage html -d {toxinidir}/reports/html/coverage.{envname}
py{38,39,310,311,312}-{type,mypy}: {[testenv:mypy]commands} py{38,39,310,311,312,313}-{type,mypy}: {[testenv:mypy]commands}
py{py3,38,39,310,311,312}-{lint,pylint},lint: {[testenv:pylint]commands} py{py3,38,39,310,311,312,313}-{lint,pylint},lint: {[testenv:pylint]commands}
py{py3,38,39,310,311,312}-{lint,flake8},lint: {[testenv:flake8]commands} py{py3,38,39,310,311,312,313}-{lint,flake8},lint: {[testenv:flake8]commands}
#ignore_outcome = #ignore_outcome =
# type: true # type: true
@@ -96,6 +96,7 @@ disable =
too-many-lines, too-many-lines,
too-many-locals, too-many-locals,
too-many-nested-blocks, too-many-nested-blocks,
too-many-positional-arguments,
too-many-return-statements, too-many-return-statements,
too-many-statements, too-many-statements,
consider-using-f-string consider-using-f-string