mirror of
https://github.com/jtesta/ssh-audit.git
synced 2026-05-25 15:31:23 +02:00
Compare commits
16 Commits
v3.3.0
..
4845a8fdee
| Author | SHA1 | Date | |
|---|---|---|---|
| 4845a8fdee | |||
| 11a902cb14 | |||
| b456bb31b9 | |||
| 32085b2fa5 | |||
| 5ddd8cca5b | |||
| b90db2c1af | |||
| 68c827c239 | |||
| e318787a5c | |||
| d9c703c777 | |||
| 28a1e23986 | |||
| a01baadfa8 | |||
| 45abc3aaf4 | |||
| 99c64787d9 | |||
| 3fa62c3ac5 | |||
| d7fff591fa | |||
| 84647ecb32 |
@@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (C) 2017-2024 Joe Testa (jtesta@positronsecurity.com)
|
Copyright (C) 2017-2025 Joe Testa (jtesta@positronsecurity.com)
|
||||||
Copyright (C) 2017 Andris Raugulis (moo@arthepsy.eu)
|
Copyright (C) 2017 Andris Raugulis (moo@arthepsy.eu)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+9
-9
@@ -15,10 +15,10 @@ An executable can only be made on a Windows host because the PyInstaller tool (h
|
|||||||
|
|
||||||
# PyPI
|
# PyPI
|
||||||
|
|
||||||
To create package and upload to test server (hint: use username '\_\_token\_\_' and API token for test.pypi.org):
|
To create package and upload to test server (hint: use API token for test.pypi.org):
|
||||||
|
|
||||||
```
|
```
|
||||||
$ sudo apt install python3-virtualenv python3.10-venv
|
$ sudo apt install python3-virtualenv python3.12-venv
|
||||||
$ make -f Makefile.pypi
|
$ make -f Makefile.pypi
|
||||||
$ make -f Makefile.pypi uploadtest
|
$ make -f Makefile.pypi uploadtest
|
||||||
```
|
```
|
||||||
@@ -26,12 +26,12 @@ To create package and upload to test server (hint: use username '\_\_token\_\_'
|
|||||||
To download from test server and verify:
|
To download from test server and verify:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ virtualenv -p /usr/bin/python3 /tmp/pypi_test
|
$ virtualenv /tmp/pypi_test
|
||||||
$ cd /tmp/pypi_test; source bin/activate
|
$ cd /tmp/pypi_test; source bin/activate
|
||||||
$ pip3 install --index-url https://test.pypi.org/simple ssh-audit
|
$ pip3 install --index-url https://test.pypi.org/simple ssh-audit
|
||||||
```
|
```
|
||||||
|
|
||||||
To upload to production server (hint: use username '\_\_token\_\_' and API token for production pypi.org):
|
To upload to production server (hint: use API token for production pypi.org):
|
||||||
|
|
||||||
```
|
```
|
||||||
$ make -f Makefile.pypi uploadprod
|
$ make -f Makefile.pypi uploadprod
|
||||||
@@ -40,7 +40,7 @@ To upload to production server (hint: use username '\_\_token\_\_' and API token
|
|||||||
To download from production server and verify:
|
To download from production server and verify:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ virtualenv -p /usr/bin/python3 /tmp/pypi_prod
|
$ virtualenv /tmp/pypi_prod
|
||||||
$ cd /tmp/pypi_prod; source bin/activate
|
$ cd /tmp/pypi_prod; source bin/activate
|
||||||
$ pip3 install ssh-audit
|
$ pip3 install ssh-audit
|
||||||
```
|
```
|
||||||
@@ -48,14 +48,14 @@ To download from production server and verify:
|
|||||||
|
|
||||||
# Snap
|
# Snap
|
||||||
|
|
||||||
To create the snap package, run a fully-updated Ubuntu Server 22.04 VM.
|
To create the Snap package, run a fully-updated Ubuntu Server 24.04 VM.
|
||||||
|
|
||||||
Create the snap package with:
|
Create the Snap package with:
|
||||||
```
|
```
|
||||||
$ ./build_snap.sh
|
$ ./build_snap.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Upload the snap with:
|
Upload the Snap with:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ snapcraft export-login ~/snap_creds.txt
|
$ snapcraft export-login ~/snap_creds.txt
|
||||||
@@ -68,7 +68,7 @@ Upload the snap with:
|
|||||||
|
|
||||||
# Docker
|
# Docker
|
||||||
|
|
||||||
Ensure that the buildx plugin is available by following the installation instructions available at: https://docs.docker.com/engine/install/ubuntu/
|
Ensure that the `buildx` plugin is available by following the installation instructions available at: https://docs.docker.com/engine/install/ubuntu/
|
||||||
|
|
||||||
Build a local image with:
|
Build a local image with:
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,7 @@
|
|||||||
- [ChangeLog](#changelog)
|
- [ChangeLog](#changelog)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
- SSH1 and SSH2 protocol server support;
|
- analyze SSH both server and client configuration;
|
||||||
- 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 security information (available since, removed/disabled, unsafe/weak/legacy, etc);
|
- output algorithm security information (available since, removed/disabled, unsafe/weak/legacy, etc);
|
||||||
@@ -41,64 +40,59 @@
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
```
|
```
|
||||||
usage: ssh-audit.py [options] <host>
|
usage: ssh-audit.py [-h] [-4] [-6] [-b] [-c] [-d]
|
||||||
|
[-g <min1:pref1:max1[,min2:pref2:max2,...]> / <x-y[:step]>] [-j] [-l {info,warn,fail}] [-L]
|
||||||
|
[-M custom_policy.txt] [-m] [-n] [-P "Built-In Policy Name" / custom_policy.txt] [-p N]
|
||||||
|
[-T targets.txt] [-t N] [-v] [--conn-rate-test N[:max_rate]] [--dheat N[:kex[:e_len]]]
|
||||||
|
[--lookup alg1[,alg2,...]] [--skip-rate-test] [--threads N]
|
||||||
|
[host]
|
||||||
|
|
||||||
-h, --help print this help
|
positional arguments:
|
||||||
-1, --ssh1 force ssh version 1 only
|
host target hostname or IPv4/IPv6 address
|
||||||
-2, --ssh2 force ssh version 2 only
|
|
||||||
-4, --ipv4 enable IPv4 (order of precedence)
|
optional arguments:
|
||||||
-6, --ipv6 enable IPv6 (order of precedence)
|
-h, --help show this help message and exit
|
||||||
-b, --batch batch output
|
-4, --ipv4 enable IPv4 (order of precedence)
|
||||||
-c, --client-audit starts a server on port 2222 to audit client
|
-6, --ipv6 enable IPv6 (order of precedence)
|
||||||
software config (use -p to change port;
|
-b, --batch batch output
|
||||||
use -t to change timeout)
|
-c, --client-audit starts a server on port 2222 to audit client software config (use -p to change port; use -t
|
||||||
--conn-rate-test=N[:max_rate] perform a connection rate test (useful
|
to change timeout)
|
||||||
for collecting metrics related to
|
-d, --debug enable debugging output
|
||||||
susceptibility of the DHEat vuln).
|
-g <min1:pref1:max1[,min2:pref2:max2,...]> / <x-y[:step]>, --gex-test <min1:pref1:max1[,min2:pref2:max2,...]> / <x-y[:step]>
|
||||||
Testing is conducted with N concurrent
|
conducts a very customized Diffie-Hellman GEX modulus size test. Tests an array of minimum,
|
||||||
sockets with an optional maximum rate
|
preferred, and maximum values, or a range of values with an optional incremental step amount
|
||||||
of connections per second.
|
-j, --json enable JSON output (use -jj to enable indentation for better readability)
|
||||||
-d, --debug Enable debug output.
|
-l {info,warn,fail}, --level {info,warn,fail}
|
||||||
--dheat=N[:kex[:e_len]] continuously perform the DHEat DoS attack
|
minimum output level (default: info)
|
||||||
(CVE-2002-20001) against the target using N
|
-L, --list-policies list all the official, built-in policies. Combine with -v to view policy change logs
|
||||||
concurrent sockets. Optionally, a specific
|
-M custom_policy.txt, --make-policy custom_policy.txt
|
||||||
key exchange algorithm can be specified
|
creates a policy based on the target server (i.e.: the target server has the ideal
|
||||||
instead of allowing it to be automatically
|
configuration that other servers should adhere to), and stores it in the file path specified
|
||||||
chosen. Additionally, a small length of
|
-m, --manual print the man page (Docker, PyPI, Snap, and Windows builds only)
|
||||||
the fake e value sent to the server can
|
-n, --no-colors disable colors (automatic when the NO_COLOR environment variable is set)
|
||||||
be chosen for a more efficient attack (such
|
-P "Built-In Policy Name" / custom_policy.txt, --policy "Built-In Policy Name" / custom_policy.txt
|
||||||
as 4).
|
run a policy test using the specified policy (use -L to see built-in policies, or specify
|
||||||
-g, --gex-test=<x[,y,...]> dh gex modulus size test
|
filesystem path to custom policy created by -M)
|
||||||
<min1:pref1:max1[,min2:pref2:max2,...]>
|
-p N, --port N the TCP port to connect to (or to listen on when -c is used)
|
||||||
<x-y[:step]>
|
-T targets.txt, --targets targets.txt
|
||||||
-j, --json JSON output (use -jj to enable indents)
|
a file containing a list of target hosts (one per line, format HOST[:PORT]). Use -p/--port
|
||||||
-l, --level=<level> minimum output level (info|warn|fail)
|
to set the default port for all hosts. Use --threads to control concurrent scans
|
||||||
-L, --list-policies list all the official, built-in policies. Use with -v
|
-t N, --timeout N timeout (in seconds) for connection and reading (default: 5)
|
||||||
to view policy change logs.
|
-v, --verbose enable verbose output
|
||||||
--lookup=<alg1,alg2,...> looks up an algorithm(s) without
|
--conn-rate-test N[:max_rate]
|
||||||
connecting to a server
|
perform a connection rate test (useful for collecting metrics related to susceptibility of
|
||||||
-m, --manual print the man page (Docker, PyPI, Snap, and Windows
|
the DHEat vuln). Testing is conducted with N concurrent sockets with an optional maximum
|
||||||
builds only)
|
rate of connections per second
|
||||||
-M, --make-policy=<policy.txt> creates a policy based on the target server
|
--dheat N[:kex[:e_len]]
|
||||||
(i.e.: the target server has the ideal
|
continuously perform the DHEat DoS attack (CVE-2002-20001) against the target using N
|
||||||
configuration that other servers should
|
concurrent sockets. Optionally, a specific key exchange algorithm can be specified instead
|
||||||
adhere to)
|
of allowing it to be automatically chosen. Additionally, a small length of the fake e value
|
||||||
-n, --no-colors disable colors
|
sent to the server can be chosen for a more efficient attack (such as 4).
|
||||||
-p, --port=<port> port to connect
|
--lookup alg1[,alg2,...]
|
||||||
-P, --policy=<"policy name" | policy.txt> run a policy test using the
|
looks up an algorithm(s) without connecting to a server.
|
||||||
specified policy
|
--skip-rate-test skip the connection rate test during standard audits (used to safely infer whether the DHEat
|
||||||
--skip-rate-test skip the connection rate test during standard audits
|
attack is viable)
|
||||||
(used to safely infer whether the DHEat attack
|
--threads N number of threads to use when scanning multiple targets (-T/--targets) (default: 32)
|
||||||
is viable)
|
|
||||||
-t, --timeout=<secs> timeout (in seconds) for connection and reading
|
|
||||||
(default: 5)
|
|
||||||
-T, --targets=<hosts.txt> a file containing a list of target hosts (one
|
|
||||||
per line, format HOST[:PORT]). Use -p/--port
|
|
||||||
to set the default port for all hosts. Use
|
|
||||||
--threads to control concurrent scans.
|
|
||||||
--threads=<threads> number of threads to use when scanning multiple
|
|
||||||
targets (-T/--targets) (default: 32)
|
|
||||||
-v, --verbose verbose output
|
|
||||||
```
|
```
|
||||||
* 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).
|
||||||
@@ -219,6 +213,16 @@ For convenience, a web front-end on top of the command-line tool is available at
|
|||||||
|
|
||||||
## ChangeLog
|
## ChangeLog
|
||||||
|
|
||||||
|
### v3.4.0-dev
|
||||||
|
- BIG THANKS to [realmiwi](https://github.com/realmiwi) for being the project's *very first sponsor!!*
|
||||||
|
- Added warning to all key exchanges that do not include protections against quantum attacks due to the Harvest Now, Decrypt Later strategy (see https://en.wikipedia.org/wiki/Harvest_now,_decrypt_later).
|
||||||
|
- Removed SSHv1 support (rationale is documented in: https://github.com/jtesta/ssh-audit/issues/298).
|
||||||
|
- Migrated from deprecated `getopt` module to `argparse`; partial credit [oam7575](https://github.com/oam7575).
|
||||||
|
- When running against multiple hosts, now prints each target host regardless of output level.
|
||||||
|
- Batch mode (`-b`) no longer automatically enables verbose mode, due to sometimes confusing results; users can still explicitly enable verbose mode using the `-v` flag.
|
||||||
|
- Added 2 new key exchanges: `mlkem768nistp256-sha256`, `mlkem1024nistp384-sha384`.
|
||||||
|
- Added 2 new ciphers: `AEAD_CAMELLIA_128_GCM`, `AEAD_CAMELLIA_256_GCM`.
|
||||||
|
|
||||||
### v3.3.0 (2024-10-15)
|
### v3.3.0 (2024-10-15)
|
||||||
- Added Python 3.13 support.
|
- Added Python 3.13 support.
|
||||||
- Added built-in policies for Ubuntu 24.04 LTS server & client, OpenSSH 9.8, and OpenSSH 9.9.
|
- Added built-in policies for Ubuntu 24.04 LTS server & client, OpenSSH 9.8, and OpenSSH 9.9.
|
||||||
|
|||||||
+1
-10
@@ -111,18 +111,9 @@ echo "Processing man page at ${MAN_PAGE} and placing output into ${GLOBALS_PY}..
|
|||||||
# * 'MAN_KEEP_FORMATTING' preserves the backspace-overwrite sequence when
|
# * 'MAN_KEEP_FORMATTING' preserves the backspace-overwrite sequence when
|
||||||
# redirected to a file or a pipe.
|
# redirected to a file or a pipe.
|
||||||
# * sed converts unicode hyphens into an ASCI equivalent.
|
# * sed converts unicode hyphens into an ASCI equivalent.
|
||||||
# * The 'ul' command converts the backspace-overwrite sequence to an ANSI
|
|
||||||
# escape sequence. Not required under Cygwin because man outputs ANSI escape
|
|
||||||
# codes automatically.
|
|
||||||
|
|
||||||
echo BUILTIN_MAN_PAGE = '"""' >> "${GLOBALS_PY}"
|
echo BUILTIN_MAN_PAGE = '"""' >> "${GLOBALS_PY}"
|
||||||
|
MANWIDTH=80 MAN_KEEP_FORMATTING=1 man "${MAN_PAGE}" | sed $'s/\u2010/-/g' >> "${GLOBALS_PY}"
|
||||||
if [[ "${PLATFORM}" == CYGWIN* ]]; then
|
|
||||||
MANWIDTH=80 MAN_KEEP_FORMATTING=1 man "${MAN_PAGE}" | sed $'s/\u2010/-/g' >> "${GLOBALS_PY}"
|
|
||||||
else
|
|
||||||
MANWIDTH=80 MAN_KEEP_FORMATTING=1 man "${MAN_PAGE}" | ul | sed $'s/\u2010/-/g' >> "${GLOBALS_PY}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo '"""' >> "${GLOBALS_PY}"
|
echo '"""' >> "${GLOBALS_PY}"
|
||||||
|
|
||||||
echo "Done."
|
echo "Done."
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (C) 2017-2021 Joe Testa (jtesta@positronsecurity.com)
|
Copyright (C) 2017-2025 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
|
||||||
@@ -29,8 +29,6 @@ from typing import Callable, Optional, Union, Any # noqa: F401
|
|||||||
from ssh_audit.algorithm import Algorithm
|
from ssh_audit.algorithm import Algorithm
|
||||||
from ssh_audit.product import Product
|
from ssh_audit.product import Product
|
||||||
from ssh_audit.software import Software
|
from ssh_audit.software import Software
|
||||||
from ssh_audit.ssh1_kexdb import SSH1_KexDB
|
|
||||||
from ssh_audit.ssh1_publickeymessage import SSH1_PublicKeyMessage
|
|
||||||
from ssh_audit.ssh2_kex import SSH2_Kex
|
from ssh_audit.ssh2_kex import SSH2_Kex
|
||||||
from ssh_audit.ssh2_kexdb import SSH2_KexDB
|
from ssh_audit.ssh2_kexdb import SSH2_KexDB
|
||||||
from ssh_audit.timeframe import Timeframe
|
from ssh_audit.timeframe import Timeframe
|
||||||
@@ -38,28 +36,13 @@ from ssh_audit.utils import Utils
|
|||||||
|
|
||||||
|
|
||||||
class Algorithms:
|
class Algorithms:
|
||||||
def __init__(self, pkm: Optional[SSH1_PublicKeyMessage], kex: Optional[SSH2_Kex]) -> None:
|
def __init__(self, kex: Optional[SSH2_Kex]) -> None:
|
||||||
self.__ssh1kex = pkm
|
|
||||||
self.__ssh2kex = kex
|
self.__ssh2kex = kex
|
||||||
|
|
||||||
@property
|
|
||||||
def ssh1kex(self) -> Optional[SSH1_PublicKeyMessage]:
|
|
||||||
return self.__ssh1kex
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ssh2kex(self) -> Optional[SSH2_Kex]:
|
def ssh2kex(self) -> Optional[SSH2_Kex]:
|
||||||
return self.__ssh2kex
|
return self.__ssh2kex
|
||||||
|
|
||||||
@property
|
|
||||||
def ssh1(self) -> Optional['Algorithms.Item']:
|
|
||||||
if self.ssh1kex is None:
|
|
||||||
return None
|
|
||||||
item = Algorithms.Item(1, SSH1_KexDB.get_db())
|
|
||||||
item.add('key', ['ssh-rsa1'])
|
|
||||||
item.add('enc', self.ssh1kex.supported_ciphers)
|
|
||||||
item.add('aut', self.ssh1kex.supported_authentications)
|
|
||||||
return item
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ssh2(self) -> Optional['Algorithms.Item']:
|
def ssh2(self) -> Optional['Algorithms.Item']:
|
||||||
if self.ssh2kex is None:
|
if self.ssh2kex is None:
|
||||||
@@ -73,7 +56,7 @@ class Algorithms:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def values(self) -> Iterable['Algorithms.Item']:
|
def values(self) -> Iterable['Algorithms.Item']:
|
||||||
for item in [self.ssh1, self.ssh2]:
|
for item in [self.ssh2]:
|
||||||
if item is not None:
|
if item is not None:
|
||||||
yield item
|
yield item
|
||||||
|
|
||||||
@@ -82,10 +65,6 @@ class Algorithms:
|
|||||||
def _ml(items: Sequence[str]) -> int:
|
def _ml(items: Sequence[str]) -> int:
|
||||||
return max(len(i) for i in items)
|
return max(len(i) for i in items)
|
||||||
maxlen = 0
|
maxlen = 0
|
||||||
if self.ssh1kex is not None:
|
|
||||||
maxlen = max(_ml(self.ssh1kex.supported_ciphers),
|
|
||||||
_ml(self.ssh1kex.supported_authentications),
|
|
||||||
maxlen)
|
|
||||||
if self.ssh2kex is not None:
|
if self.ssh2kex is not None:
|
||||||
maxlen = max(_ml(self.ssh2kex.kex_algorithms),
|
maxlen = max(_ml(self.ssh2kex.kex_algorithms),
|
||||||
_ml(self.ssh2kex.key_algorithms),
|
_ml(self.ssh2kex.key_algorithms),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (C) 2017-2024 Joe Testa (jtesta@positronsecurity.com)
|
Copyright (C) 2017-2025 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
|
||||||
@@ -35,8 +35,6 @@ class AuditConf:
|
|||||||
def __init__(self, host: str = '', port: int = 22) -> None:
|
def __init__(self, host: str = '', port: int = 22) -> None:
|
||||||
self.host = host
|
self.host = host
|
||||||
self.port = port
|
self.port = port
|
||||||
self.ssh1 = True
|
|
||||||
self.ssh2 = True
|
|
||||||
self.batch = False
|
self.batch = False
|
||||||
self.client_audit = False
|
self.client_audit = False
|
||||||
self.colors = True
|
self.colors = True
|
||||||
@@ -73,7 +71,7 @@ class AuditConf:
|
|||||||
|
|
||||||
def __setattr__(self, name: str, value: Union[str, int, float, bool, Sequence[int]]) -> None:
|
def __setattr__(self, name: str, value: Union[str, int, float, bool, Sequence[int]]) -> None:
|
||||||
valid = False
|
valid = False
|
||||||
if name in ['batch', 'client_audit', 'colors', 'json', 'json_print_indent', 'list_policies', 'manual', 'make_policy', 'ssh1', 'ssh2', 'timeout_set', 'verbose', 'debug', 'skip_rate_test']:
|
if name in ['batch', 'client_audit', 'colors', 'json', 'json_print_indent', 'list_policies', 'manual', 'make_policy', 'timeout_set', 'verbose', 'debug', 'skip_rate_test']:
|
||||||
valid, value = True, bool(value)
|
valid, value = True, bool(value)
|
||||||
elif name in ['ipv4', 'ipv6']:
|
elif name in ['ipv4', 'ipv6']:
|
||||||
valid, value = True, bool(value)
|
valid, value = True, bool(value)
|
||||||
|
|||||||
@@ -759,9 +759,9 @@ class DHEat:
|
|||||||
r = socket.getaddrinfo(host, 0, family, socket.SOCK_STREAM)
|
r = socket.getaddrinfo(host, 0, family, socket.SOCK_STREAM)
|
||||||
for address_family, socktype, _, _, addr in r:
|
for address_family, socktype, _, _, addr in r:
|
||||||
if socktype == socket.SOCK_STREAM:
|
if socktype == socket.SOCK_STREAM:
|
||||||
return address_family, addr[0]
|
return int(address_family), str(addr[0])
|
||||||
|
|
||||||
return -1, ''
|
return int(socket.AF_UNSPEC), ''
|
||||||
|
|
||||||
|
|
||||||
def _run(self) -> bool:
|
def _run(self) -> bool:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (C) 2017-2023 Joe Testa (jtesta@positronsecurity.com)
|
Copyright (C) 2017-2025 Joe Testa (jtesta@positronsecurity.com)
|
||||||
|
|
||||||
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
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -64,7 +64,7 @@ class GEXTest:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# Parse the server's KEX.
|
# Parse the server's KEX.
|
||||||
_, payload = s.read_packet(2)
|
_, payload = s.read_packet()
|
||||||
SSH2_Kex.parse(out, payload)
|
SSH2_Kex.parse(out, payload)
|
||||||
except (KexDHException, struct.error):
|
except (KexDHException, struct.error):
|
||||||
out.v("Failed to parse server's kex. Stack trace:\n%s" % str(traceback.format_exc()), write_now=True)
|
out.v("Failed to parse server's kex. Stack trace:\n%s" % str(traceback.format_exc()), write_now=True)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
"""
|
"""
|
||||||
# The version to display.
|
# The version to display.
|
||||||
VERSION = 'v3.3.0'
|
VERSION = 'v3.4.0-dev'
|
||||||
|
|
||||||
# SSH software to impersonate
|
# SSH software to impersonate
|
||||||
SSH_HEADER = 'SSH-{0}-OpenSSH_8.2'
|
SSH_HEADER = 'SSH-{0}-OpenSSH_8.2'
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (C) 2017-2023 Joe Testa (jtesta@positronsecurity.com)
|
Copyright (C) 2017-2025 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
|
||||||
@@ -88,11 +88,11 @@ class KexDH: # pragma: nocover
|
|||||||
self.__ca_key_type = ''
|
self.__ca_key_type = ''
|
||||||
self.__ca_n_len = 0
|
self.__ca_n_len = 0
|
||||||
|
|
||||||
packet_type, payload = s.read_packet(2)
|
packet_type, payload = s.read_packet()
|
||||||
|
|
||||||
# Skip any & all MSG_DEBUG messages.
|
# Skip any & all MSG_DEBUG messages.
|
||||||
while packet_type == Protocol.MSG_DEBUG:
|
while packet_type == Protocol.MSG_DEBUG:
|
||||||
packet_type, payload = s.read_packet(2)
|
packet_type, payload = s.read_packet()
|
||||||
|
|
||||||
if packet_type != -1 and packet_type not in [Protocol.MSG_KEXDH_REPLY, Protocol.MSG_KEXDH_GEX_REPLY]: # pylint: disable=no-else-raise
|
if packet_type != -1 and packet_type not in [Protocol.MSG_KEXDH_REPLY, Protocol.MSG_KEXDH_GEX_REPLY]: # pylint: disable=no-else-raise
|
||||||
raise KexDHException('Expected MSG_KEXDH_REPLY (%d) or MSG_KEXDH_GEX_REPLY (%d), but got %d instead.' % (Protocol.MSG_KEXDH_REPLY, Protocol.MSG_KEXDH_GEX_REPLY, packet_type))
|
raise KexDHException('Expected MSG_KEXDH_REPLY (%d) or MSG_KEXDH_GEX_REPLY (%d), but got %d instead.' % (Protocol.MSG_KEXDH_REPLY, Protocol.MSG_KEXDH_GEX_REPLY, packet_type))
|
||||||
@@ -380,13 +380,13 @@ class KexGroupExchange(KexDH):
|
|||||||
s.write_int(maxbits)
|
s.write_int(maxbits)
|
||||||
s.send_packet()
|
s.send_packet()
|
||||||
|
|
||||||
packet_type, payload = s.read_packet(2)
|
packet_type, payload = s.read_packet()
|
||||||
if packet_type not in [Protocol.MSG_KEXDH_GEX_GROUP, Protocol.MSG_DEBUG]:
|
if packet_type not in [Protocol.MSG_KEXDH_GEX_GROUP, Protocol.MSG_DEBUG]:
|
||||||
raise KexDHException('Expected MSG_KEXDH_GEX_REPLY (%d), but got %d instead.' % (Protocol.MSG_KEXDH_GEX_REPLY, packet_type))
|
raise KexDHException('Expected MSG_KEXDH_GEX_REPLY (%d), but got %d instead.' % (Protocol.MSG_KEXDH_GEX_REPLY, packet_type))
|
||||||
|
|
||||||
# Skip any & all MSG_DEBUG messages.
|
# Skip any & all MSG_DEBUG messages.
|
||||||
while packet_type == Protocol.MSG_DEBUG:
|
while packet_type == Protocol.MSG_DEBUG:
|
||||||
packet_type, payload = s.read_packet(2)
|
packet_type, payload = s.read_packet()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Parse the modulus (p) and generator (g) values from the server.
|
# Parse the modulus (p) and generator (g) values from the server.
|
||||||
|
|||||||
@@ -54,11 +54,11 @@ class OutputBuffer:
|
|||||||
self.__is_color_supported = ('colorama' in sys.modules) or (os.name == 'posix')
|
self.__is_color_supported = ('colorama' in sys.modules) or (os.name == 'posix')
|
||||||
self.line_ended = True
|
self.line_ended = True
|
||||||
|
|
||||||
def _print(self, level: str, s: str = '', line_ended: bool = True) -> None:
|
def _print(self, level: str, s: str = '', line_ended: bool = True, always_print: bool = False) -> None:
|
||||||
'''Saves output to buffer (if in buffered mode), or immediately prints to stdout otherwise.'''
|
'''Saves output to buffer (if in buffered mode), or immediately prints to stdout otherwise.'''
|
||||||
|
|
||||||
# If we're logging only 'warn' or above, and this is an 'info', ignore message.
|
# If we're logging only 'warn' or above, and this is an 'info', ignore message, unless always_print is True (useful for printing informational lines regardless of the level setting).
|
||||||
if self.get_level(level) < self.__level:
|
if (always_print is False) and (self.get_level(level) < self.__level):
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.use_colors and self.colors_supported and len(s) > 0 and level != 'info':
|
if self.use_colors and self.colors_supported and len(s) > 0 and level != 'info':
|
||||||
@@ -145,20 +145,22 @@ class OutputBuffer:
|
|||||||
self._print('head', s, line_ended)
|
self._print('head', s, line_ended)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def fail(self, s: str, line_ended: bool = True) -> 'OutputBuffer':
|
def fail(self, s: str, line_ended: bool = True, write_now: bool = False, always_print: bool = False) -> 'OutputBuffer':
|
||||||
self._print('fail', s, line_ended)
|
self._print('fail', s, line_ended, always_print=always_print)
|
||||||
|
if write_now:
|
||||||
|
self.write()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def warn(self, s: str, line_ended: bool = True) -> 'OutputBuffer':
|
def warn(self, s: str, line_ended: bool = True, always_print: bool = False) -> 'OutputBuffer':
|
||||||
self._print('warn', s, line_ended)
|
self._print('warn', s, line_ended, always_print=always_print)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def info(self, s: str, line_ended: bool = True) -> 'OutputBuffer':
|
def info(self, s: str, line_ended: bool = True, always_print: bool = False) -> 'OutputBuffer':
|
||||||
self._print('info', s, line_ended)
|
self._print('info', s, line_ended, always_print=always_print)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def good(self, s: str, line_ended: bool = True) -> 'OutputBuffer':
|
def good(self, s: str, line_ended: bool = True, always_print: bool = False) -> 'OutputBuffer':
|
||||||
self._print('good', s, line_ended)
|
self._print('good', s, line_ended, always_print=always_print)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def sep(self) -> 'OutputBuffer':
|
def sep(self) -> 'OutputBuffer':
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
"""
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (C) 2017 Andris Raugulis (moo@arthepsy.eu)
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
"""
|
|
||||||
# pylint: disable=unused-import
|
|
||||||
from typing import Dict, List, Set, Sequence, Tuple, Iterable # noqa: F401
|
|
||||||
from typing import Callable, Optional, Union, Any # noqa: F401
|
|
||||||
|
|
||||||
from ssh_audit.ssh1_crc32 import SSH1_CRC32
|
|
||||||
|
|
||||||
|
|
||||||
class SSH1:
|
|
||||||
_crc32: Optional[SSH1_CRC32] = None
|
|
||||||
CIPHERS = ['none', 'idea', 'des', '3des', 'tss', 'rc4', 'blowfish']
|
|
||||||
AUTHS = ['none', 'rhosts', 'rsa', 'password', 'rhosts_rsa', 'tis', 'kerberos']
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def crc32(cls, v: bytes) -> int:
|
|
||||||
if cls._crc32 is None:
|
|
||||||
cls._crc32 = SSH1_CRC32()
|
|
||||||
return cls._crc32.calc(v)
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
"""
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (C) 2017 Andris Raugulis (moo@arthepsy.eu)
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
"""
|
|
||||||
# pylint: disable=unused-import
|
|
||||||
from typing import Dict, List, Set, Sequence, Tuple, Iterable # noqa: F401
|
|
||||||
from typing import Callable, Optional, Union, Any # noqa: F401
|
|
||||||
|
|
||||||
|
|
||||||
class SSH1_CRC32:
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self._table = [0] * 256
|
|
||||||
for i in range(256):
|
|
||||||
crc = 0
|
|
||||||
n = i
|
|
||||||
for _ in range(8):
|
|
||||||
x = (crc ^ n) & 1
|
|
||||||
crc = (crc >> 1) ^ (x * 0xedb88320)
|
|
||||||
n = n >> 1
|
|
||||||
self._table[i] = crc
|
|
||||||
|
|
||||||
def calc(self, v: bytes) -> int:
|
|
||||||
crc, length = 0, len(v)
|
|
||||||
for i in range(length):
|
|
||||||
n = ord(v[i:i + 1])
|
|
||||||
n = n ^ (crc & 0xff)
|
|
||||||
crc = (crc >> 8) ^ self._table[n]
|
|
||||||
return crc
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
"""
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (C) 2017 Andris Raugulis (moo@arthepsy.eu)
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
"""
|
|
||||||
# pylint: disable=unused-import
|
|
||||||
import copy
|
|
||||||
import threading
|
|
||||||
|
|
||||||
from typing import Dict, List, Set, Sequence, Tuple, Iterable # noqa: F401
|
|
||||||
from typing import Callable, Optional, Union, Any # noqa: F401
|
|
||||||
|
|
||||||
|
|
||||||
class SSH1_KexDB: # pylint: disable=too-few-public-methods
|
|
||||||
|
|
||||||
FAIL_PLAINTEXT = 'no encryption/integrity'
|
|
||||||
FAIL_OPENSSH37_REMOVE = 'removed since OpenSSH 3.7'
|
|
||||||
FAIL_NA_BROKEN = 'not implemented in OpenSSH, broken algorithm'
|
|
||||||
FAIL_NA_UNSAFE = 'not implemented in OpenSSH (server), unsafe algorithm'
|
|
||||||
TEXT_CIPHER_IDEA = 'cipher used by commercial SSH'
|
|
||||||
|
|
||||||
DB_PER_THREAD: Dict[int, Dict[str, Dict[str, List[List[Optional[str]]]]]] = {}
|
|
||||||
|
|
||||||
MASTER_DB: Dict[str, Dict[str, List[List[Optional[str]]]]] = {
|
|
||||||
'key': {
|
|
||||||
'ssh-rsa1': [['1.2.2']],
|
|
||||||
},
|
|
||||||
'enc': {
|
|
||||||
'none': [['1.2.2'], [FAIL_PLAINTEXT]],
|
|
||||||
'idea': [[None], [], [], [TEXT_CIPHER_IDEA]],
|
|
||||||
'des': [['2.3.0C'], [FAIL_NA_UNSAFE]],
|
|
||||||
'3des': [['1.2.2']],
|
|
||||||
'tss': [[''], [FAIL_NA_BROKEN]],
|
|
||||||
'rc4': [[], [FAIL_NA_BROKEN]],
|
|
||||||
'blowfish': [['1.2.2']],
|
|
||||||
},
|
|
||||||
'aut': {
|
|
||||||
'rhosts': [['1.2.2', '3.6'], [FAIL_OPENSSH37_REMOVE]],
|
|
||||||
'rsa': [['1.2.2']],
|
|
||||||
'password': [['1.2.2']],
|
|
||||||
'rhosts_rsa': [['1.2.2']],
|
|
||||||
'tis': [['1.2.2']],
|
|
||||||
'kerberos': [['1.2.2', '3.6'], [FAIL_OPENSSH37_REMOVE]],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_db() -> Dict[str, Dict[str, List[List[Optional[str]]]]]:
|
|
||||||
'''Returns a copy of the MASTER_DB that is private to the calling thread. This prevents multiple threads from polluting the results of other threads.'''
|
|
||||||
calling_thread_id = threading.get_ident()
|
|
||||||
|
|
||||||
if calling_thread_id not in SSH1_KexDB.DB_PER_THREAD:
|
|
||||||
SSH1_KexDB.DB_PER_THREAD[calling_thread_id] = copy.deepcopy(SSH1_KexDB.MASTER_DB)
|
|
||||||
|
|
||||||
return SSH1_KexDB.DB_PER_THREAD[calling_thread_id]
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def thread_exit() -> None:
|
|
||||||
'''Deletes the calling thread's copy of the MASTER_DB. This is needed because, in rare circumstances, a terminated thread's ID can be re-used by new threads.'''
|
|
||||||
|
|
||||||
calling_thread_id = threading.get_ident()
|
|
||||||
|
|
||||||
if calling_thread_id in SSH1_KexDB.DB_PER_THREAD:
|
|
||||||
del SSH1_KexDB.DB_PER_THREAD[calling_thread_id]
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
"""
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (C) 2017 Andris Raugulis (moo@arthepsy.eu)
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
"""
|
|
||||||
# pylint: disable=unused-import
|
|
||||||
from typing import Dict, List, Set, Sequence, Tuple, Iterable # noqa: F401
|
|
||||||
from typing import Callable, Optional, Union, Any # noqa: F401
|
|
||||||
|
|
||||||
from ssh_audit.ssh1 import SSH1
|
|
||||||
from ssh_audit.readbuf import ReadBuf
|
|
||||||
from ssh_audit.utils import Utils
|
|
||||||
from ssh_audit.writebuf import WriteBuf
|
|
||||||
|
|
||||||
|
|
||||||
class SSH1_PublicKeyMessage:
|
|
||||||
def __init__(self, cookie: bytes, skey: Tuple[int, int, int], hkey: Tuple[int, int, int], pflags: int, cmask: int, amask: int) -> None:
|
|
||||||
if len(skey) != 3:
|
|
||||||
raise ValueError('invalid server key pair: {}'.format(skey))
|
|
||||||
if len(hkey) != 3:
|
|
||||||
raise ValueError('invalid host key pair: {}'.format(hkey))
|
|
||||||
self.__cookie = cookie
|
|
||||||
self.__server_key = skey
|
|
||||||
self.__host_key = hkey
|
|
||||||
self.__protocol_flags = pflags
|
|
||||||
self.__supported_ciphers_mask = cmask
|
|
||||||
self.__supported_authentications_mask = amask
|
|
||||||
|
|
||||||
@property
|
|
||||||
def cookie(self) -> bytes:
|
|
||||||
return self.__cookie
|
|
||||||
|
|
||||||
@property
|
|
||||||
def server_key_bits(self) -> int:
|
|
||||||
return self.__server_key[0]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def server_key_public_exponent(self) -> int:
|
|
||||||
return self.__server_key[1]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def server_key_public_modulus(self) -> int:
|
|
||||||
return self.__server_key[2]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def host_key_bits(self) -> int:
|
|
||||||
return self.__host_key[0]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def host_key_public_exponent(self) -> int:
|
|
||||||
return self.__host_key[1]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def host_key_public_modulus(self) -> int:
|
|
||||||
return self.__host_key[2]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def host_key_fingerprint_data(self) -> bytes:
|
|
||||||
# pylint: disable=protected-access
|
|
||||||
mod = WriteBuf._create_mpint(self.host_key_public_modulus, False)
|
|
||||||
e = WriteBuf._create_mpint(self.host_key_public_exponent, False)
|
|
||||||
return mod + e
|
|
||||||
|
|
||||||
@property
|
|
||||||
def protocol_flags(self) -> int:
|
|
||||||
return self.__protocol_flags
|
|
||||||
|
|
||||||
@property
|
|
||||||
def supported_ciphers_mask(self) -> int:
|
|
||||||
return self.__supported_ciphers_mask
|
|
||||||
|
|
||||||
@property
|
|
||||||
def supported_ciphers(self) -> List[str]:
|
|
||||||
ciphers = []
|
|
||||||
for i in range(len(SSH1.CIPHERS)): # pylint: disable=consider-using-enumerate
|
|
||||||
if self.__supported_ciphers_mask & (1 << i) != 0:
|
|
||||||
ciphers.append(Utils.to_text(SSH1.CIPHERS[i]))
|
|
||||||
return ciphers
|
|
||||||
|
|
||||||
@property
|
|
||||||
def supported_authentications_mask(self) -> int:
|
|
||||||
return self.__supported_authentications_mask
|
|
||||||
|
|
||||||
@property
|
|
||||||
def supported_authentications(self) -> List[str]:
|
|
||||||
auths = []
|
|
||||||
for i in range(1, len(SSH1.AUTHS)):
|
|
||||||
if self.__supported_authentications_mask & (1 << i) != 0:
|
|
||||||
auths.append(Utils.to_text(SSH1.AUTHS[i]))
|
|
||||||
return auths
|
|
||||||
|
|
||||||
def write(self, wbuf: 'WriteBuf') -> None:
|
|
||||||
wbuf.write(self.cookie)
|
|
||||||
wbuf.write_int(self.server_key_bits)
|
|
||||||
wbuf.write_mpint1(self.server_key_public_exponent)
|
|
||||||
wbuf.write_mpint1(self.server_key_public_modulus)
|
|
||||||
wbuf.write_int(self.host_key_bits)
|
|
||||||
wbuf.write_mpint1(self.host_key_public_exponent)
|
|
||||||
wbuf.write_mpint1(self.host_key_public_modulus)
|
|
||||||
wbuf.write_int(self.protocol_flags)
|
|
||||||
wbuf.write_int(self.supported_ciphers_mask)
|
|
||||||
wbuf.write_int(self.supported_authentications_mask)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def payload(self) -> bytes:
|
|
||||||
wbuf = WriteBuf()
|
|
||||||
self.write(wbuf)
|
|
||||||
return wbuf.write_flush()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def parse(cls, payload: bytes) -> 'SSH1_PublicKeyMessage':
|
|
||||||
buf = ReadBuf(payload)
|
|
||||||
cookie = buf.read(8)
|
|
||||||
server_key_bits = buf.read_int()
|
|
||||||
server_key_exponent = buf.read_mpint1()
|
|
||||||
server_key_modulus = buf.read_mpint1()
|
|
||||||
skey = (server_key_bits, server_key_exponent, server_key_modulus)
|
|
||||||
host_key_bits = buf.read_int()
|
|
||||||
host_key_exponent = buf.read_mpint1()
|
|
||||||
host_key_modulus = buf.read_mpint1()
|
|
||||||
hkey = (host_key_bits, host_key_exponent, host_key_modulus)
|
|
||||||
pflags = buf.read_int()
|
|
||||||
cmask = buf.read_int()
|
|
||||||
amask = buf.read_int()
|
|
||||||
pkm = cls(cookie, skey, hkey, pflags, cmask, amask)
|
|
||||||
return pkm
|
|
||||||
+109
-102
@@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (C) 2017-2024 Joe Testa (jtesta@positronsecurity.com)
|
Copyright (C) 2017-2025 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
|
||||||
@@ -57,6 +57,7 @@ class SSH2_KexDB: # pylint: disable=too-few-public-methods
|
|||||||
WARN_CIPHER_MODE = 'using weak cipher mode'
|
WARN_CIPHER_MODE = 'using weak cipher mode'
|
||||||
WARN_ENCRYPT_AND_MAC = 'using encrypt-and-MAC mode'
|
WARN_ENCRYPT_AND_MAC = 'using encrypt-and-MAC mode'
|
||||||
WARN_EXPERIMENTAL = 'using experimental algorithm'
|
WARN_EXPERIMENTAL = 'using experimental algorithm'
|
||||||
|
WARN_NOT_PQ_SAFE = 'does not provide protection against post-quantum attacks'
|
||||||
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_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_TAG_SIZE_96 = 'using small 96-bit tag size'
|
||||||
@@ -65,12 +66,14 @@ class SSH2_KexDB: # pylint: disable=too-few-public-methods
|
|||||||
INFO_DEFAULT_OPENSSH_KEX_65_TO_73 = 'default key exchange from OpenSSH 6.5 to 7.3'
|
INFO_DEFAULT_OPENSSH_KEX_65_TO_73 = 'default key exchange from OpenSSH 6.5 to 7.3'
|
||||||
INFO_DEFAULT_OPENSSH_KEX_74_TO_89 = 'default key exchange from OpenSSH 7.4 to 8.9'
|
INFO_DEFAULT_OPENSSH_KEX_74_TO_89 = 'default key exchange from OpenSSH 7.4 to 8.9'
|
||||||
INFO_DEFAULT_OPENSSH_KEX_90_TO_98 = 'default key exchange from OpenSSH 9.0 to 9.8'
|
INFO_DEFAULT_OPENSSH_KEX_90_TO_98 = 'default key exchange from OpenSSH 9.0 to 9.8'
|
||||||
INFO_DEFAULT_OPENSSH_KEX_99 = 'default key exchange since OpenSSH 9.9'
|
INFO_DEFAULT_OPENSSH_KEX_99 = 'default key exchange in OpenSSH 9.9'
|
||||||
|
INFO_DEFAULT_OPENSSH_KEX_100 = 'default key exchange since OpenSSH 10.0'
|
||||||
INFO_DEPRECATED_IN_OPENSSH88 = 'deprecated in OpenSSH 8.8: https://www.openssh.com/txt/release-8.8'
|
INFO_DEPRECATED_IN_OPENSSH88 = 'deprecated in OpenSSH 8.8: https://www.openssh.com/txt/release-8.8'
|
||||||
INFO_DISABLED_IN_DBEAR67 = 'disabled in Dropbear SSH 2015.67'
|
INFO_DISABLED_IN_DBEAR67 = 'disabled in Dropbear SSH 2015.67'
|
||||||
INFO_DISABLED_IN_OPENSSH70 = 'disabled in OpenSSH 7.0: https://www.openssh.com/txt/release-7.0'
|
INFO_DISABLED_IN_OPENSSH70 = 'disabled in OpenSSH 7.0: https://www.openssh.com/txt/release-7.0'
|
||||||
INFO_NEVER_IMPLEMENTED_IN_OPENSSH = 'despite the @openssh.com tag, this was never implemented in OpenSSH'
|
INFO_NEVER_IMPLEMENTED_IN_OPENSSH = 'despite the @openssh.com tag, this was never implemented in OpenSSH'
|
||||||
INFO_HYBRID_PQ_X25519_KEX = 'hybrid key exchange based on post-quantum resistant algorithm and proven conventional X25519 algorithm'
|
INFO_HYBRID_PQ_X25519_KEX = 'hybrid key exchange based on post-quantum resistant algorithm and proven conventional X25519 algorithm'
|
||||||
|
INFO_HYBRID_PQ_NISTP_KEX = 'hybrid key exchange based on post-quantum resistant algorithm and a suspected back-doored NIST P-curve'
|
||||||
INFO_REMOVED_IN_OPENSSH61 = 'removed since OpenSSH 6.1, removed from specification'
|
INFO_REMOVED_IN_OPENSSH61 = 'removed since OpenSSH 6.1, removed from specification'
|
||||||
INFO_REMOVED_IN_OPENSSH69 = 'removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9'
|
INFO_REMOVED_IN_OPENSSH69 = 'removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9'
|
||||||
INFO_REMOVED_IN_OPENSSH70 = 'removed in OpenSSH 7.0: https://www.openssh.com/txt/release-7.0'
|
INFO_REMOVED_IN_OPENSSH70 = 'removed in OpenSSH 7.0: https://www.openssh.com/txt/release-7.0'
|
||||||
@@ -84,117 +87,119 @@ class SSH2_KexDB: # pylint: disable=too-few-public-methods
|
|||||||
MASTER_DB: Dict[str, Dict[str, List[List[Optional[str]]]]] = {
|
MASTER_DB: Dict[str, Dict[str, List[List[Optional[str]]]]] = {
|
||||||
# Format: 'algorithm_name': [['version_first_appeared_in'], [reason_for_failure1, reason_for_failure2, ...], [warning1, warning2, ...], [info1, info2, ...]]
|
# Format: 'algorithm_name': [['version_first_appeared_in'], [reason_for_failure1, reason_for_failure2, ...], [warning1, warning2, ...], [info1, info2, ...]]
|
||||||
'kex': {
|
'kex': {
|
||||||
'Curve25519SHA256': [[]],
|
'Curve25519SHA256': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'curve25519-sha256': [['7.4,d2018.76'], [], [], [INFO_DEFAULT_OPENSSH_KEX_74_TO_89]],
|
'curve25519-sha256': [['7.4,d2018.76'], [], [WARN_NOT_PQ_SAFE], [INFO_DEFAULT_OPENSSH_KEX_74_TO_89]],
|
||||||
'curve25519-sha256@libssh.org': [['6.4,d2013.62,l10.6.0'], [], [], [INFO_DEFAULT_OPENSSH_KEX_65_TO_73]],
|
'curve25519-sha256@libssh.org': [['6.4,d2013.62,l10.6.0'], [], [WARN_NOT_PQ_SAFE], [INFO_DEFAULT_OPENSSH_KEX_65_TO_73]],
|
||||||
'curve448-sha512': [[]],
|
'curve448-sha512': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'curve448-sha512@libssh.org': [[]],
|
'curve448-sha512@libssh.org': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'diffie-hellman-group14-sha1': [['3.9,d0.53,l10.6.0'], [FAIL_SHA1], [WARN_2048BIT_MODULUS]],
|
'diffie-hellman-group14-sha1': [['3.9,d0.53,l10.6.0'], [FAIL_SHA1], [WARN_2048BIT_MODULUS, WARN_NOT_PQ_SAFE]],
|
||||||
'diffie-hellman-group14-sha224@ssh.com': [[]],
|
'diffie-hellman-group14-sha224@ssh.com': [[], [], [WARN_2048BIT_MODULUS, WARN_NOT_PQ_SAFE]],
|
||||||
'diffie-hellman-group14-sha256': [['7.3,d2016.73'], [], [WARN_2048BIT_MODULUS]],
|
'diffie-hellman-group14-sha256': [['7.3,d2016.73'], [], [WARN_2048BIT_MODULUS, WARN_NOT_PQ_SAFE]],
|
||||||
'diffie-hellman-group14-sha256@ssh.com': [[], [], [WARN_2048BIT_MODULUS]],
|
'diffie-hellman-group14-sha256@ssh.com': [[], [], [WARN_2048BIT_MODULUS, WARN_NOT_PQ_SAFE]],
|
||||||
'diffie-hellman-group15-sha256': [[]],
|
'diffie-hellman-group15-sha256': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'diffie-hellman-group15-sha256@ssh.com': [[]],
|
'diffie-hellman-group15-sha256@ssh.com': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'diffie-hellman-group15-sha384@ssh.com': [[]],
|
'diffie-hellman-group15-sha384@ssh.com': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'diffie-hellman-group15-sha512': [[]],
|
'diffie-hellman-group15-sha512': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'diffie-hellman-group16-sha256': [[]],
|
'diffie-hellman-group16-sha256': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'diffie-hellman-group16-sha384@ssh.com': [[]],
|
'diffie-hellman-group16-sha384@ssh.com': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'diffie-hellman-group16-sha512': [['7.3,d2016.73']],
|
'diffie-hellman-group16-sha512': [['7.3,d2016.73'], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'diffie-hellman-group16-sha512@ssh.com': [[]],
|
'diffie-hellman-group16-sha512@ssh.com': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'diffie-hellman-group17-sha512': [[]],
|
'diffie-hellman-group17-sha512': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'diffie-hellman_group17-sha512': [[]],
|
'diffie-hellman_group17-sha512': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'diffie-hellman-group18-sha512': [['7.3']],
|
'diffie-hellman-group18-sha512': [['7.3'], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'diffie-hellman-group18-sha512@ssh.com': [[]],
|
'diffie-hellman-group18-sha512@ssh.com': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'diffie-hellman-group1-sha1': [['2.3.0,d0.28,l10.2', '6.6', '6.9'], [FAIL_1024BIT_MODULUS, FAIL_LOGJAM_ATTACK, FAIL_SHA1], [], [INFO_REMOVED_IN_OPENSSH69]],
|
'diffie-hellman-group1-sha1': [['2.3.0,d0.28,l10.2', '6.6', '6.9'], [FAIL_1024BIT_MODULUS, FAIL_LOGJAM_ATTACK, FAIL_SHA1], [WARN_NOT_PQ_SAFE], [INFO_REMOVED_IN_OPENSSH69]],
|
||||||
'diffie-hellman-group1-sha256': [[], [FAIL_1024BIT_MODULUS]],
|
'diffie-hellman-group1-sha256': [[], [FAIL_1024BIT_MODULUS], [WARN_NOT_PQ_SAFE]],
|
||||||
'diffie-hellman-group-exchange-sha1': [['2.3.0', '6.6', None], [FAIL_SHA1]],
|
'diffie-hellman-group-exchange-sha1': [['2.3.0', '6.6', None], [FAIL_SHA1], [WARN_NOT_PQ_SAFE]],
|
||||||
'diffie-hellman-group-exchange-sha224@ssh.com': [[]],
|
'diffie-hellman-group-exchange-sha224@ssh.com': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'diffie-hellman-group-exchange-sha256': [['4.4']],
|
'diffie-hellman-group-exchange-sha256': [['4.4'], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'diffie-hellman-group-exchange-sha256@ssh.com': [[]],
|
'diffie-hellman-group-exchange-sha256@ssh.com': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'diffie-hellman-group-exchange-sha384@ssh.com': [[]],
|
'diffie-hellman-group-exchange-sha384@ssh.com': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'diffie-hellman-group-exchange-sha512@ssh.com': [[]],
|
'diffie-hellman-group-exchange-sha512@ssh.com': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'ecdh-nistp256-kyber-512r3-sha256-d00@openquantumsafe.org': [[], [FAIL_NSA_BACKDOORED_CURVE]],
|
'ecdh-nistp256-kyber-512r3-sha256-d00@openquantumsafe.org': [[], [FAIL_NSA_BACKDOORED_CURVE]],
|
||||||
'ecdh-nistp384-kyber-768r3-sha384-d00@openquantumsafe.org': [[], [FAIL_NSA_BACKDOORED_CURVE]],
|
'ecdh-nistp384-kyber-768r3-sha384-d00@openquantumsafe.org': [[], [FAIL_NSA_BACKDOORED_CURVE]],
|
||||||
'ecdh-nistp521-kyber-1024r3-sha512-d00@openquantumsafe.org': [[], [FAIL_NSA_BACKDOORED_CURVE]],
|
'ecdh-nistp521-kyber-1024r3-sha512-d00@openquantumsafe.org': [[], [FAIL_NSA_BACKDOORED_CURVE]],
|
||||||
'ecdh-sha2-1.2.840.10045.3.1.1': [[], [FAIL_SMALL_ECC_MODULUS, FAIL_NSA_BACKDOORED_CURVE]], # NIST P-192 / secp192r1
|
'ecdh-sha2-1.2.840.10045.3.1.1': [[], [FAIL_SMALL_ECC_MODULUS, FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]], # NIST P-192 / secp192r1
|
||||||
'ecdh-sha2-1.2.840.10045.3.1.7': [[], [FAIL_NSA_BACKDOORED_CURVE]], # NIST P-256 / secp256r1
|
'ecdh-sha2-1.2.840.10045.3.1.7': [[], [FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]], # NIST P-256 / secp256r1
|
||||||
'ecdh-sha2-1.3.132.0.10': [[]], # ECDH over secp256k1 (i.e.: the Bitcoin curve)
|
'ecdh-sha2-1.3.132.0.10': [[], [], [WARN_NOT_PQ_SAFE]], # ECDH over secp256k1 (i.e.: the Bitcoin curve)
|
||||||
'ecdh-sha2-1.3.132.0.16': [[], [FAIL_UNPROVEN]], # sect283k1
|
'ecdh-sha2-1.3.132.0.16': [[], [FAIL_UNPROVEN], [WARN_NOT_PQ_SAFE]], # sect283k1
|
||||||
'ecdh-sha2-1.3.132.0.1': [[], [FAIL_UNPROVEN, FAIL_SMALL_ECC_MODULUS]], # sect163k1
|
'ecdh-sha2-1.3.132.0.1': [[], [FAIL_UNPROVEN, FAIL_SMALL_ECC_MODULUS], [WARN_NOT_PQ_SAFE]], # sect163k1
|
||||||
'ecdh-sha2-1.3.132.0.26': [[], [FAIL_UNPROVEN, FAIL_SMALL_ECC_MODULUS]], # sect233k1
|
'ecdh-sha2-1.3.132.0.26': [[], [FAIL_UNPROVEN, FAIL_SMALL_ECC_MODULUS], [WARN_NOT_PQ_SAFE]], # sect233k1
|
||||||
'ecdh-sha2-1.3.132.0.27': [[], [FAIL_SMALL_ECC_MODULUS, FAIL_NSA_BACKDOORED_CURVE]], # sect233r1
|
'ecdh-sha2-1.3.132.0.27': [[], [FAIL_SMALL_ECC_MODULUS, FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]], # sect233r1
|
||||||
'ecdh-sha2-1.3.132.0.33': [[], [FAIL_SMALL_ECC_MODULUS, FAIL_NSA_BACKDOORED_CURVE]], # NIST P-224 / secp224r1
|
'ecdh-sha2-1.3.132.0.33': [[], [FAIL_SMALL_ECC_MODULUS, FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]], # NIST P-224 / secp224r1
|
||||||
'ecdh-sha2-1.3.132.0.34': [[], [FAIL_NSA_BACKDOORED_CURVE]], # NIST P-384 / secp384r1
|
'ecdh-sha2-1.3.132.0.34': [[], [FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]], # NIST P-384 / secp384r1
|
||||||
'ecdh-sha2-1.3.132.0.35': [[], [FAIL_NSA_BACKDOORED_CURVE]], # NIST P-521 / secp521r1
|
'ecdh-sha2-1.3.132.0.35': [[], [FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]], # NIST P-521 / secp521r1
|
||||||
'ecdh-sha2-1.3.132.0.36': [[], [FAIL_UNPROVEN]], # sect409k1
|
'ecdh-sha2-1.3.132.0.36': [[], [FAIL_UNPROVEN], [WARN_NOT_PQ_SAFE]], # sect409k1
|
||||||
'ecdh-sha2-1.3.132.0.37': [[], [FAIL_NSA_BACKDOORED_CURVE]], # sect409r1
|
'ecdh-sha2-1.3.132.0.37': [[], [FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]], # sect409r1
|
||||||
'ecdh-sha2-1.3.132.0.38': [[], [FAIL_UNPROVEN]], # sect571k1
|
'ecdh-sha2-1.3.132.0.38': [[], [FAIL_UNPROVEN], [WARN_NOT_PQ_SAFE]], # sect571k1
|
||||||
|
|
||||||
# Note: the base64 strings, according to draft 6 of RFC5656, is Base64(MD5(DER(OID))). The final RFC5656 dropped the base64 strings in favor of plain OID concatenation, but apparently some SSH servers implement them anyway. See: https://datatracker.ietf.org/doc/html/draft-green-secsh-ecc-06#section-9.2
|
# Note: the base64 strings, according to draft 6 of RFC5656, is Base64(MD5(DER(OID))). The final RFC5656 dropped the base64 strings in favor of plain OID concatenation, but apparently some SSH servers implement them anyway. See: https://datatracker.ietf.org/doc/html/draft-green-secsh-ecc-06#section-9.2
|
||||||
'ecdh-sha2-4MHB+NBt3AlaSRQ7MnB4cg==': [[], [FAIL_UNPROVEN, FAIL_SMALL_ECC_MODULUS]], # sect163k1
|
'ecdh-sha2-4MHB+NBt3AlaSRQ7MnB4cg==': [[], [FAIL_UNPROVEN, FAIL_SMALL_ECC_MODULUS], [WARN_NOT_PQ_SAFE]], # sect163k1
|
||||||
'ecdh-sha2-5pPrSUQtIaTjUSt5VZNBjg==': [[], [FAIL_SMALL_ECC_MODULUS, FAIL_NSA_BACKDOORED_CURVE]], # NIST P-192 / secp192r1
|
'ecdh-sha2-5pPrSUQtIaTjUSt5VZNBjg==': [[], [FAIL_SMALL_ECC_MODULUS, FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]], # NIST P-192 / secp192r1
|
||||||
'ecdh-sha2-9UzNcgwTlEnSCECZa7V1mw==': [[], [FAIL_NSA_BACKDOORED_CURVE]], # NIST P-256 / secp256r1
|
'ecdh-sha2-9UzNcgwTlEnSCECZa7V1mw==': [[], [FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]], # NIST P-256 / secp256r1
|
||||||
'ecdh-sha2-brainpoolp256r1@genua.de': [[], [FAIL_UNPROVEN]],
|
'ecdh-sha2-brainpoolp256r1@genua.de': [[], [FAIL_UNPROVEN], [WARN_NOT_PQ_SAFE]],
|
||||||
'ecdh-sha2-brainpoolp384r1@genua.de': [[], [FAIL_UNPROVEN]],
|
'ecdh-sha2-brainpoolp384r1@genua.de': [[], [FAIL_UNPROVEN], [WARN_NOT_PQ_SAFE]],
|
||||||
'ecdh-sha2-brainpoolp521r1@genua.de': [[], [FAIL_UNPROVEN]],
|
'ecdh-sha2-brainpoolp521r1@genua.de': [[], [FAIL_UNPROVEN], [WARN_NOT_PQ_SAFE]],
|
||||||
'ecdh-sha2-curve25519': [[], []],
|
'ecdh-sha2-curve25519': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'ecdh-sha2-D3FefCjYoJ/kfXgAyLddYA==': [[], [FAIL_NSA_BACKDOORED_CURVE]], # sect409r1
|
'ecdh-sha2-D3FefCjYoJ/kfXgAyLddYA==': [[], [FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]], # sect409r1
|
||||||
'ecdh-sha2-h/SsxnLCtRBh7I9ATyeB3A==': [[], [FAIL_NSA_BACKDOORED_CURVE]], # NIST P-521 / secp521r1
|
'ecdh-sha2-h/SsxnLCtRBh7I9ATyeB3A==': [[], [FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]], # NIST P-521 / secp521r1
|
||||||
'ecdh-sha2-m/FtSAmrV4j/Wy6RVUaK7A==': [[], [FAIL_UNPROVEN]], # sect409k1
|
'ecdh-sha2-m/FtSAmrV4j/Wy6RVUaK7A==': [[], [FAIL_UNPROVEN], [WARN_NOT_PQ_SAFE]], # sect409k1
|
||||||
'ecdh-sha2-mNVwCXAoS1HGmHpLvBC94w==': [[], [FAIL_UNPROVEN]], # sect571k1
|
'ecdh-sha2-mNVwCXAoS1HGmHpLvBC94w==': [[], [FAIL_UNPROVEN], [WARN_NOT_PQ_SAFE]], # sect571k1
|
||||||
'ecdh-sha2-nistb233': [[], [FAIL_UNPROVEN, FAIL_SMALL_ECC_MODULUS]],
|
'ecdh-sha2-nistb233': [[], [FAIL_UNPROVEN, FAIL_SMALL_ECC_MODULUS], [WARN_NOT_PQ_SAFE]],
|
||||||
'ecdh-sha2-nistb409': [[], [FAIL_UNPROVEN]],
|
'ecdh-sha2-nistb409': [[], [FAIL_UNPROVEN], [WARN_NOT_PQ_SAFE]],
|
||||||
'ecdh-sha2-nistk163': [[], [FAIL_UNPROVEN, FAIL_SMALL_ECC_MODULUS]],
|
'ecdh-sha2-nistk163': [[], [FAIL_UNPROVEN, FAIL_SMALL_ECC_MODULUS], [WARN_NOT_PQ_SAFE]],
|
||||||
'ecdh-sha2-nistk233': [[], [FAIL_UNPROVEN, FAIL_SMALL_ECC_MODULUS]],
|
'ecdh-sha2-nistk233': [[], [FAIL_UNPROVEN, FAIL_SMALL_ECC_MODULUS], [WARN_NOT_PQ_SAFE]],
|
||||||
'ecdh-sha2-nistk283': [[], [FAIL_UNPROVEN]],
|
'ecdh-sha2-nistk283': [[], [FAIL_UNPROVEN], [WARN_NOT_PQ_SAFE]],
|
||||||
'ecdh-sha2-nistk409': [[], [FAIL_UNPROVEN]],
|
'ecdh-sha2-nistk409': [[], [FAIL_UNPROVEN], [WARN_NOT_PQ_SAFE]],
|
||||||
'ecdh-sha2-nistp192': [[], [FAIL_NSA_BACKDOORED_CURVE]],
|
'ecdh-sha2-nistp192': [[], [FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]],
|
||||||
'ecdh-sha2-nistp224': [[], [FAIL_NSA_BACKDOORED_CURVE]],
|
'ecdh-sha2-nistp224': [[], [FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]],
|
||||||
'ecdh-sha2-nistp256': [['5.7,d2013.62,l10.6.0'], [FAIL_NSA_BACKDOORED_CURVE]],
|
'ecdh-sha2-nistp256': [['5.7,d2013.62,l10.6.0'], [FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]],
|
||||||
'ecdh-sha2-nistp384': [['5.7,d2013.62'], [FAIL_NSA_BACKDOORED_CURVE]],
|
'ecdh-sha2-nistp384': [['5.7,d2013.62'], [FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]],
|
||||||
'ecdh-sha2-nistp521': [['5.7,d2013.62'], [FAIL_NSA_BACKDOORED_CURVE]],
|
'ecdh-sha2-nistp521': [['5.7,d2013.62'], [FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]],
|
||||||
'ecdh-sha2-nistt571': [[], [FAIL_UNPROVEN]],
|
'ecdh-sha2-nistt571': [[], [FAIL_UNPROVEN], [WARN_NOT_PQ_SAFE]],
|
||||||
'ecdh-sha2-qCbG5Cn/jjsZ7nBeR7EnOA==': [[FAIL_SMALL_ECC_MODULUS, FAIL_NSA_BACKDOORED_CURVE]], # sect233r1
|
'ecdh-sha2-qCbG5Cn/jjsZ7nBeR7EnOA==': [[], [FAIL_SMALL_ECC_MODULUS, FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]], # sect233r1
|
||||||
'ecdh-sha2-qcFQaMAMGhTziMT0z+Tuzw==': [[], [FAIL_NSA_BACKDOORED_CURVE]], # NIST P-384 / secp384r1
|
'ecdh-sha2-qcFQaMAMGhTziMT0z+Tuzw==': [[], [FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]], # NIST P-384 / secp384r1
|
||||||
'ecdh-sha2-VqBg4QRPjxx1EXZdV0GdWQ==': [[], [FAIL_NSA_BACKDOORED_CURVE, FAIL_SMALL_ECC_MODULUS]], # NIST P-224 / secp224r1
|
'ecdh-sha2-VqBg4QRPjxx1EXZdV0GdWQ==': [[], [FAIL_NSA_BACKDOORED_CURVE, FAIL_SMALL_ECC_MODULUS], [WARN_NOT_PQ_SAFE]], # NIST P-224 / secp224r1
|
||||||
'ecdh-sha2-wiRIU8TKjMZ418sMqlqtvQ==': [[], [FAIL_UNPROVEN]], # sect283k1
|
'ecdh-sha2-wiRIU8TKjMZ418sMqlqtvQ==': [[], [FAIL_UNPROVEN], [WARN_NOT_PQ_SAFE]], # sect283k1
|
||||||
'ecdh-sha2-zD/b3hu/71952ArpUG4OjQ==': [[], [FAIL_UNPROVEN, FAIL_SMALL_ECC_MODULUS]], # sect233k1
|
'ecdh-sha2-zD/b3hu/71952ArpUG4OjQ==': [[], [FAIL_UNPROVEN, FAIL_SMALL_ECC_MODULUS], [WARN_NOT_PQ_SAFE]], # sect233k1
|
||||||
'ecmqv-sha2': [[], [FAIL_UNPROVEN]],
|
'ecmqv-sha2': [[], [FAIL_UNPROVEN], [WARN_NOT_PQ_SAFE]],
|
||||||
'ext-info-c': [['7.2'], [], [], [INFO_EXTENSION_NEGOTIATION]], # Extension negotiation (RFC 8308)
|
'ext-info-c': [['7.2'], [], [], [INFO_EXTENSION_NEGOTIATION]], # Extension negotiation (RFC 8308)
|
||||||
'ext-info-s': [['9.6'], [], [], [INFO_EXTENSION_NEGOTIATION]], # Extension negotiation (RFC 8308)
|
'ext-info-s': [['9.6'], [], [], [INFO_EXTENSION_NEGOTIATION]], # Extension negotiation (RFC 8308)
|
||||||
'kex-strict-c-v00@openssh.com': [[], [], [], [INFO_STRICT_KEX]], # Strict KEX marker (countermeasure for CVE-2023-48795).
|
'kex-strict-c-v00@openssh.com': [[], [], [], [INFO_STRICT_KEX]], # Strict KEX marker (countermeasure for CVE-2023-48795).
|
||||||
'kex-strict-s-v00@openssh.com': [[], [], [], [INFO_STRICT_KEX]], # Strict KEX marker (countermeasure for CVE-2023-48795).
|
'kex-strict-s-v00@openssh.com': [[], [], [], [INFO_STRICT_KEX]], # Strict KEX marker (countermeasure for CVE-2023-48795).
|
||||||
|
|
||||||
# The GSS kex algorithms get special wildcard handling, since they include variable base64 data after their standard prefixes.
|
# The GSS kex algorithms get special wildcard handling, since they include variable base64 data after their standard prefixes.
|
||||||
'gss-13.3.132.0.10-sha256-*': [[], [FAIL_UNKNOWN]],
|
'gss-13.3.132.0.10-sha256-*': [[], [FAIL_UNKNOWN], [WARN_NOT_PQ_SAFE]],
|
||||||
'gss-curve25519-sha256-*': [[]],
|
'gss-curve25519-sha256-*': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'gss-curve448-sha512-*': [[]],
|
'gss-curve448-sha512-*': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'gss-gex-sha1-*': [[], [FAIL_SHA1]],
|
'gss-gex-sha1-*': [[], [FAIL_SHA1], [WARN_NOT_PQ_SAFE]],
|
||||||
'gss-gex-sha256-*': [[]],
|
'gss-gex-sha256-*': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'gss-group14-sha1-*': [[], [FAIL_SHA1], [WARN_2048BIT_MODULUS]],
|
'gss-group14-sha1-*': [[], [FAIL_SHA1], [WARN_2048BIT_MODULUS, WARN_NOT_PQ_SAFE]],
|
||||||
'gss-group14-sha256-*': [[], [], [WARN_2048BIT_MODULUS]],
|
'gss-group14-sha256-*': [[], [], [WARN_2048BIT_MODULUS, WARN_NOT_PQ_SAFE]],
|
||||||
'gss-group15-sha512-*': [[]],
|
'gss-group15-sha512-*': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'gss-group16-sha512-*': [[]],
|
'gss-group16-sha512-*': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'gss-group17-sha512-*': [[]],
|
'gss-group17-sha512-*': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'gss-group18-sha512-*': [[]],
|
'gss-group18-sha512-*': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'gss-group1-sha1-*': [[], [FAIL_1024BIT_MODULUS, FAIL_LOGJAM_ATTACK, FAIL_SHA1]],
|
'gss-group1-sha1-*': [[], [FAIL_1024BIT_MODULUS, FAIL_LOGJAM_ATTACK, FAIL_SHA1], [WARN_NOT_PQ_SAFE]],
|
||||||
'gss-nistp256-sha256-*': [[], [FAIL_NSA_BACKDOORED_CURVE]],
|
'gss-nistp256-sha256-*': [[], [FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]],
|
||||||
'gss-nistp384-sha256-*': [[], [FAIL_NSA_BACKDOORED_CURVE]],
|
'gss-nistp384-sha256-*': [[], [FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]],
|
||||||
'gss-nistp384-sha384-*': [[], [FAIL_NSA_BACKDOORED_CURVE]],
|
'gss-nistp384-sha384-*': [[], [FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]],
|
||||||
'gss-nistp521-sha512-*': [[], [FAIL_NSA_BACKDOORED_CURVE]],
|
'gss-nistp521-sha512-*': [[], [FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]],
|
||||||
'kexAlgoCurve25519SHA256': [[]],
|
'kexAlgoCurve25519SHA256': [[], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'kexAlgoDH14SHA1': [[], [FAIL_SHA1], [WARN_2048BIT_MODULUS]],
|
'kexAlgoDH14SHA1': [[], [FAIL_SHA1], [WARN_2048BIT_MODULUS, WARN_NOT_PQ_SAFE]],
|
||||||
'kexAlgoDH1SHA1': [[], [FAIL_1024BIT_MODULUS, FAIL_LOGJAM_ATTACK, FAIL_SHA1]],
|
'kexAlgoDH1SHA1': [[], [FAIL_1024BIT_MODULUS, FAIL_LOGJAM_ATTACK, FAIL_SHA1], [WARN_NOT_PQ_SAFE]],
|
||||||
'kexAlgoECDH256': [[], [FAIL_NSA_BACKDOORED_CURVE]],
|
'kexAlgoECDH256': [[], [FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]],
|
||||||
'kexAlgoECDH384': [[], [FAIL_NSA_BACKDOORED_CURVE]],
|
'kexAlgoECDH384': [[], [FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]],
|
||||||
'kexAlgoECDH521': [[], [FAIL_NSA_BACKDOORED_CURVE]],
|
'kexAlgoECDH521': [[], [FAIL_NSA_BACKDOORED_CURVE], [WARN_NOT_PQ_SAFE]],
|
||||||
'kexguess2@matt.ucc.asn.au': [['d2013.57']],
|
'kexguess2@matt.ucc.asn.au': [['d2013.57'], [], [WARN_NOT_PQ_SAFE]],
|
||||||
'm383-sha384@libassh.org': [[], [FAIL_UNPROVEN]],
|
'm383-sha384@libassh.org': [[], [FAIL_UNPROVEN], [WARN_NOT_PQ_SAFE]],
|
||||||
'm511-sha512@libassh.org': [[], [FAIL_UNPROVEN]],
|
'm511-sha512@libassh.org': [[], [FAIL_UNPROVEN], [WARN_NOT_PQ_SAFE]],
|
||||||
'mlkem768x25519-sha256': [['9.9'], [], [], [INFO_HYBRID_PQ_X25519_KEX]],
|
'mlkem768x25519-sha256': [['9.9'], [], [], [INFO_DEFAULT_OPENSSH_KEX_100, INFO_HYBRID_PQ_X25519_KEX]],
|
||||||
'rsa1024-sha1': [[], [FAIL_1024BIT_MODULUS, FAIL_SHA1]],
|
'mlkem768nistp256-sha256': [[], [FAIL_NSA_BACKDOORED_CURVE], [], [INFO_HYBRID_PQ_NISTP_KEX]],
|
||||||
'rsa2048-sha256': [[], [], [WARN_2048BIT_MODULUS]],
|
'mlkem1024nistp384-sha384': [[], [FAIL_NSA_BACKDOORED_CURVE], [], [INFO_HYBRID_PQ_NISTP_KEX]],
|
||||||
'sm2kep-sha2-nistp256': [[], [FAIL_NSA_BACKDOORED_CURVE, FAIL_UNTRUSTED]],
|
'rsa1024-sha1': [[], [FAIL_1024BIT_MODULUS, FAIL_SHA1], [WARN_NOT_PQ_SAFE]],
|
||||||
|
'rsa2048-sha256': [[], [], [WARN_2048BIT_MODULUS, WARN_NOT_PQ_SAFE]],
|
||||||
|
'sm2kep-sha2-nistp256': [[], [FAIL_NSA_BACKDOORED_CURVE, FAIL_UNTRUSTED], [WARN_NOT_PQ_SAFE]],
|
||||||
'sntrup4591761x25519-sha512@tinyssh.org': [['8.0', '8.4'], [], [WARN_EXPERIMENTAL], [INFO_WITHDRAWN_PQ_ALG]],
|
'sntrup4591761x25519-sha512@tinyssh.org': [['8.0', '8.4'], [], [WARN_EXPERIMENTAL], [INFO_WITHDRAWN_PQ_ALG]],
|
||||||
'sntrup761x25519-sha512': [['9.9'], [], [], [INFO_DEFAULT_OPENSSH_KEX_99, INFO_HYBRID_PQ_X25519_KEX]],
|
'sntrup761x25519-sha512': [['9.9'], [], [], [INFO_DEFAULT_OPENSSH_KEX_99, INFO_HYBRID_PQ_X25519_KEX]],
|
||||||
'sntrup761x25519-sha512@openssh.com': [['8.5'], [], [], [INFO_DEFAULT_OPENSSH_KEX_90_TO_98, INFO_HYBRID_PQ_X25519_KEX]],
|
'sntrup761x25519-sha512@openssh.com': [['8.5'], [], [], [INFO_DEFAULT_OPENSSH_KEX_90_TO_98, INFO_HYBRID_PQ_X25519_KEX]],
|
||||||
@@ -297,6 +302,8 @@ class SSH2_KexDB: # pylint: disable=too-few-public-methods
|
|||||||
'3des-ofb': [[], [FAIL_3DES], [WARN_CIPHER_MODE]],
|
'3des-ofb': [[], [FAIL_3DES], [WARN_CIPHER_MODE]],
|
||||||
'AEAD_AES_128_GCM': [[]],
|
'AEAD_AES_128_GCM': [[]],
|
||||||
'AEAD_AES_256_GCM': [[]],
|
'AEAD_AES_256_GCM': [[]],
|
||||||
|
'AEAD_CAMELLIA_128_GCM': [[]],
|
||||||
|
'AEAD_CAMELLIA_256_GCM': [[]],
|
||||||
'aes128-cbc': [['2.3.0,d0.28,l10.2', '6.6', None], [], [WARN_CIPHER_MODE]],
|
'aes128-cbc': [['2.3.0,d0.28,l10.2', '6.6', None], [], [WARN_CIPHER_MODE]],
|
||||||
'aes128-ctr': [['3.7,d0.52,l10.4.1']],
|
'aes128-ctr': [['3.7,d0.52,l10.4.1']],
|
||||||
'aes128-gcm': [[]],
|
'aes128-gcm': [[]],
|
||||||
|
|||||||
+181
-258
@@ -2,7 +2,7 @@
|
|||||||
"""
|
"""
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (C) 2017-2024 Joe Testa (jtesta@positronsecurity.com)
|
Copyright (C) 2017-2025 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
|
||||||
@@ -23,9 +23,9 @@
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
"""
|
"""
|
||||||
|
import argparse
|
||||||
import concurrent.futures
|
import concurrent.futures
|
||||||
import copy
|
import copy
|
||||||
import getopt # pylint: disable=deprecated-module
|
|
||||||
import json
|
import json
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import os
|
import os
|
||||||
@@ -33,6 +33,7 @@ import re
|
|||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-import
|
# pylint: disable=unused-import
|
||||||
from typing import Dict, List, Set, Sequence, Tuple, Iterable # noqa: F401
|
from typing import Dict, List, Set, Sequence, Tuple, Iterable # noqa: F401
|
||||||
from typing import cast, Callable, Optional, Union, Any # noqa: F401
|
from typing import cast, Callable, Optional, Union, Any # noqa: F401
|
||||||
@@ -55,8 +56,6 @@ from ssh_audit.policy import Policy
|
|||||||
from ssh_audit.product import Product
|
from ssh_audit.product import Product
|
||||||
from ssh_audit.protocol import Protocol
|
from ssh_audit.protocol import Protocol
|
||||||
from ssh_audit.software import Software
|
from ssh_audit.software import Software
|
||||||
from ssh_audit.ssh1_kexdb import SSH1_KexDB
|
|
||||||
from ssh_audit.ssh1_publickeymessage import SSH1_PublicKeyMessage
|
|
||||||
from ssh_audit.ssh2_kex import SSH2_Kex
|
from ssh_audit.ssh2_kex import SSH2_Kex
|
||||||
from ssh_audit.ssh2_kexdb import SSH2_KexDB
|
from ssh_audit.ssh2_kexdb import SSH2_KexDB
|
||||||
from ssh_audit.ssh_socket import SSH_Socket
|
from ssh_audit.ssh_socket import SSH_Socket
|
||||||
@@ -82,61 +81,6 @@ if sys.platform == 'win32':
|
|||||||
# no_idna_workaround = True
|
# no_idna_workaround = True
|
||||||
|
|
||||||
|
|
||||||
def usage(uout: OutputBuffer, err: Optional[str] = None) -> None:
|
|
||||||
retval = exitcodes.GOOD
|
|
||||||
p = os.path.basename(sys.argv[0])
|
|
||||||
uout.head('# {} {}, https://github.com/jtesta/ssh-audit\n'.format(p, VERSION))
|
|
||||||
if err is not None and len(err) > 0:
|
|
||||||
uout.fail(err + '\n')
|
|
||||||
retval = exitcodes.UNKNOWN_ERROR
|
|
||||||
uout.info('usage: {0} [options] <host>\n'.format(p))
|
|
||||||
uout.info(' -h, --help print this help')
|
|
||||||
uout.info(' -1, --ssh1 force ssh version 1 only')
|
|
||||||
uout.info(' -2, --ssh2 force ssh version 2 only')
|
|
||||||
uout.info(' -4, --ipv4 enable IPv4 (order of precedence)')
|
|
||||||
uout.info(' -6, --ipv6 enable IPv6 (order of precedence)')
|
|
||||||
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(' --conn-rate-test=N[:max_rate] perform a connection rate test (useful')
|
|
||||||
uout.info(' for collecting metrics related to')
|
|
||||||
uout.info(' susceptibility of the DHEat vuln).')
|
|
||||||
uout.info(' Testing is conducted with N concurrent')
|
|
||||||
uout.info(' sockets with an optional maximum rate')
|
|
||||||
uout.info(' of connections per second.')
|
|
||||||
uout.info(' -d, --debug debug output')
|
|
||||||
uout.info(' --dheat=N[:kex[:e_len]] continuously perform the DHEat DoS attack')
|
|
||||||
uout.info(' (CVE-2002-20001) against the target using N')
|
|
||||||
uout.info(' concurrent sockets. Optionally, a specific')
|
|
||||||
uout.info(' key exchange algorithm can be specified')
|
|
||||||
uout.info(' instead of allowing it to be automatically')
|
|
||||||
uout.info(' chosen. Additionally, a small length of')
|
|
||||||
uout.info(' the fake e value sent to the server can')
|
|
||||||
uout.info(' be chosen for a more efficient attack (such')
|
|
||||||
uout.info(' as 4).')
|
|
||||||
uout.info(' -g, --gex-test=<x[,y,...]> dh gex modulus size test')
|
|
||||||
uout.info(' <min1:pref1:max1[,min2:pref2:max2,...]>')
|
|
||||||
uout.info(' <x-y[:step]>')
|
|
||||||
uout.info(' -j, --json JSON output (use -jj to enable indents)')
|
|
||||||
uout.info(' -l, --level=<level> minimum output level (info|warn|fail)')
|
|
||||||
uout.info(' -L, --list-policies list all the official, built-in policies. Use with -v')
|
|
||||||
uout.info(' to view policy change logs.')
|
|
||||||
uout.info(' --lookup=<alg1,alg2,...> looks up an algorithm(s) without\n connecting to a server')
|
|
||||||
uout.info(' -M, --make-policy=<policy.txt> creates a policy based on the target server\n (i.e.: the target server has the ideal\n configuration that other servers should\n adhere to)')
|
|
||||||
uout.info(' -m, --manual print the man page (Windows only)')
|
|
||||||
uout.info(' -n, --no-colors disable colors (automatic when the NO_COLOR')
|
|
||||||
uout.info(' environment variable is set)')
|
|
||||||
uout.info(' -p, --port=<port> port to connect')
|
|
||||||
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(' -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 -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(' -v, --verbose verbose output')
|
|
||||||
uout.sep()
|
|
||||||
uout.write()
|
|
||||||
sys.exit(retval)
|
|
||||||
|
|
||||||
|
|
||||||
def output_algorithms(out: OutputBuffer, title: str, alg_db: Dict[str, Dict[str, List[List[Optional[str]]]]], alg_type: str, algorithms: List[str], unknown_algs: List[str], is_json_output: bool, program_retval: int, maxlen: int = 0, host_keys: Optional[Dict[str, Dict[str, Union[bytes, str, int]]]] = None, dh_modulus_sizes: Optional[Dict[str, int]] = None) -> int: # pylint: disable=too-many-arguments
|
def output_algorithms(out: OutputBuffer, title: str, alg_db: Dict[str, Dict[str, List[List[Optional[str]]]]], alg_type: str, algorithms: List[str], unknown_algs: List[str], is_json_output: bool, program_retval: int, maxlen: int = 0, host_keys: Optional[Dict[str, Dict[str, Union[bytes, str, int]]]] = None, dh_modulus_sizes: Optional[Dict[str, int]] = None) -> int: # pylint: disable=too-many-arguments
|
||||||
with out:
|
with out:
|
||||||
for algorithm in algorithms:
|
for algorithm in algorithms:
|
||||||
@@ -288,11 +232,6 @@ def output_security(out: OutputBuffer, banner: Optional[Banner], padlen: int, is
|
|||||||
def output_fingerprints(out: OutputBuffer, algs: Algorithms, is_json_output: bool) -> None:
|
def output_fingerprints(out: OutputBuffer, algs: Algorithms, is_json_output: bool) -> None:
|
||||||
with out:
|
with out:
|
||||||
fps = {}
|
fps = {}
|
||||||
if algs.ssh1kex is not None:
|
|
||||||
name = 'ssh-rsa1'
|
|
||||||
fp = Fingerprint(algs.ssh1kex.host_key_fingerprint_data)
|
|
||||||
# bits = algs.ssh1kex.host_key_bits
|
|
||||||
fps[name] = fp
|
|
||||||
if algs.ssh2kex is not None:
|
if algs.ssh2kex is not None:
|
||||||
host_keys = algs.ssh2kex.host_keys()
|
host_keys = algs.ssh2kex.host_keys()
|
||||||
for host_key_type in algs.ssh2kex.host_keys():
|
for host_key_type in algs.ssh2kex.host_keys():
|
||||||
@@ -371,7 +310,7 @@ def output_recommendations(out: OutputBuffer, algs: Algorithms, algorithm_recomm
|
|||||||
notes = " (%s)" % notes
|
notes = " (%s)" % notes
|
||||||
|
|
||||||
fm = '(rec) {0}{1}{2}-- {3} algorithm to {4}{5} '
|
fm = '(rec) {0}{1}{2}-- {3} algorithm to {4}{5} '
|
||||||
fn(fm.format(sg, name, p, alg_type, an, notes))
|
fn(fm.format(sg, name, p, alg_type, an, notes)) # type: ignore[operator]
|
||||||
|
|
||||||
if not out.is_section_empty() and not is_json_output:
|
if not out.is_section_empty() and not is_json_output:
|
||||||
if software is not None:
|
if software is not None:
|
||||||
@@ -563,12 +502,11 @@ def post_process_findings(banner: Optional[Banner], algs: Algorithms, client_aud
|
|||||||
|
|
||||||
|
|
||||||
# Returns a exitcodes.* flag to denote if any failures or warnings were encountered.
|
# Returns a exitcodes.* flag to denote if any failures or warnings were encountered.
|
||||||
def output(out: OutputBuffer, aconf: AuditConf, banner: Optional[Banner], header: List[str], client_host: Optional[str] = None, kex: Optional[SSH2_Kex] = None, pkm: Optional[SSH1_PublicKeyMessage] = None, print_target: bool = False, dh_rate_test_notes: str = "") -> int:
|
def output(out: OutputBuffer, aconf: AuditConf, banner: Optional[Banner], header: List[str], client_host: Optional[str] = None, kex: Optional[SSH2_Kex] = None, print_target: bool = False, dh_rate_test_notes: str = "") -> int:
|
||||||
|
|
||||||
program_retval = exitcodes.GOOD
|
program_retval = exitcodes.GOOD
|
||||||
client_audit = client_host is not None # If set, this is a client audit.
|
client_audit = client_host is not None # If set, this is a client audit.
|
||||||
sshv = 1 if pkm is not None else 2
|
algs = Algorithms(kex)
|
||||||
algs = Algorithms(pkm, kex)
|
|
||||||
|
|
||||||
# Perform post-processing on the findings to make final adjustments before outputting the results.
|
# Perform post-processing on the findings to make final adjustments before outputting the results.
|
||||||
algorithm_recommendation_suppress_list, additional_notes = post_process_findings(banner, algs, client_audit, dh_rate_test_notes)
|
algorithm_recommendation_suppress_list, additional_notes = post_process_findings(banner, algs, client_audit, dh_rate_test_notes)
|
||||||
@@ -586,14 +524,14 @@ def output(out: OutputBuffer, aconf: AuditConf, banner: Optional[Banner], header
|
|||||||
else:
|
else:
|
||||||
host = '%s:%d' % (aconf.host, aconf.port)
|
host = '%s:%d' % (aconf.host, aconf.port)
|
||||||
|
|
||||||
out.good('(gen) target: {}'. format(host))
|
out.good('(gen) target: {}'. format(host), always_print=True)
|
||||||
if client_audit:
|
if client_audit:
|
||||||
out.good('(gen) client IP: {}'.format(client_host))
|
out.good('(gen) client IP: {}'.format(client_host), always_print=True)
|
||||||
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:
|
||||||
banner_line = '(gen) banner: {}'.format(banner)
|
banner_line = '(gen) banner: {}'.format(banner)
|
||||||
if sshv == 1 or banner.protocol[0] == 1:
|
if banner.protocol[0] == 1:
|
||||||
out.fail(banner_line)
|
out.fail(banner_line)
|
||||||
out.fail('(gen) protocol SSH1 enabled')
|
out.fail('(gen) protocol SSH1 enabled')
|
||||||
else:
|
else:
|
||||||
@@ -625,18 +563,6 @@ def output(out: OutputBuffer, aconf: AuditConf, banner: Optional[Banner], header
|
|||||||
# Filled in by output_algorithms() with unidentified algs.
|
# Filled in by output_algorithms() with unidentified algs.
|
||||||
unknown_algorithms: List[str] = []
|
unknown_algorithms: List[str] = []
|
||||||
|
|
||||||
# SSHv1
|
|
||||||
if pkm is not None:
|
|
||||||
adb = SSH1_KexDB.get_db()
|
|
||||||
ciphers = pkm.supported_ciphers
|
|
||||||
auths = pkm.supported_authentications
|
|
||||||
title, atype = 'SSH1 host-key algorithms', 'key'
|
|
||||||
program_retval = output_algorithms(out, title, adb, atype, ['ssh-rsa1'], unknown_algorithms, aconf.json, program_retval, maxlen)
|
|
||||||
title, atype = 'SSH1 encryption algorithms (ciphers)', 'enc'
|
|
||||||
program_retval = output_algorithms(out, title, adb, atype, ciphers, unknown_algorithms, aconf.json, program_retval, maxlen)
|
|
||||||
title, atype = 'SSH1 authentication types', 'aut'
|
|
||||||
program_retval = output_algorithms(out, title, adb, atype, auths, unknown_algorithms, aconf.json, program_retval, maxlen)
|
|
||||||
|
|
||||||
# SSHv2
|
# SSHv2
|
||||||
if kex is not None:
|
if kex is not None:
|
||||||
adb = SSH2_KexDB.get_db()
|
adb = SSH2_KexDB.get_db()
|
||||||
@@ -823,7 +749,7 @@ def make_policy(aconf: AuditConf, banner: Optional['Banner'], kex: Optional['SSH
|
|||||||
print(err)
|
print(err)
|
||||||
|
|
||||||
|
|
||||||
def process_commandline(out: OutputBuffer, args: List[str], usage_cb: Callable[..., None]) -> 'AuditConf': # pylint: disable=too-many-statements
|
def process_commandline(out: OutputBuffer, args: List[str]) -> 'AuditConf': # pylint: disable=too-many-statements
|
||||||
# pylint: disable=too-many-branches
|
# pylint: disable=too-many-branches
|
||||||
aconf = AuditConf()
|
aconf = AuditConf()
|
||||||
|
|
||||||
@@ -836,82 +762,87 @@ def process_commandline(out: OutputBuffer, args: List[str], usage_cb: Callable[.
|
|||||||
aconf.colors = enable_colors
|
aconf.colors = enable_colors
|
||||||
out.use_colors = enable_colors
|
out.use_colors = enable_colors
|
||||||
|
|
||||||
try:
|
|
||||||
sopts = 'h1246M:p:P:jbcnvl:t:T:Lmdg:'
|
|
||||||
lopts = ['help', 'ssh1', 'ssh2', 'ipv4', 'ipv6', 'make-policy=', 'port=', 'policy=', 'json', 'batch', 'client-audit', 'no-colors', 'verbose', 'level=', 'timeout=', 'targets=', 'list-policies', 'lookup=', 'threads=', 'manual', 'debug', 'gex-test=', 'dheat=', 'skip-rate-test', 'conn-rate-test=']
|
|
||||||
opts, args = getopt.gnu_getopt(args, sopts, lopts)
|
|
||||||
except getopt.GetoptError as err:
|
|
||||||
usage_cb(out, str(err))
|
|
||||||
aconf.ssh1, aconf.ssh2 = False, False
|
|
||||||
host: str = ''
|
host: str = ''
|
||||||
oport: Optional[str] = None
|
port: int = 22
|
||||||
port: int = 0
|
|
||||||
for o, a in opts:
|
|
||||||
if o in ('-h', '--help'):
|
|
||||||
usage_cb(out)
|
|
||||||
elif o in ('-1', '--ssh1'):
|
|
||||||
aconf.ssh1 = True
|
|
||||||
elif o in ('-2', '--ssh2'):
|
|
||||||
aconf.ssh2 = True
|
|
||||||
elif o in ('-4', '--ipv4'):
|
|
||||||
aconf.ipv4 = True
|
|
||||||
elif o in ('-6', '--ipv6'):
|
|
||||||
aconf.ipv6 = True
|
|
||||||
elif o in ('-p', '--port'):
|
|
||||||
oport = a
|
|
||||||
elif o in ('-b', '--batch'):
|
|
||||||
aconf.batch = True
|
|
||||||
aconf.verbose = True
|
|
||||||
elif o in ('-c', '--client-audit'):
|
|
||||||
aconf.client_audit = True
|
|
||||||
elif o in ('-j', '--json'):
|
|
||||||
if aconf.json: # If specified twice, enable indent printing.
|
|
||||||
aconf.json_print_indent = True
|
|
||||||
else:
|
|
||||||
aconf.json = True
|
|
||||||
elif o in ('-v', '--verbose'):
|
|
||||||
aconf.verbose = True
|
|
||||||
out.verbose = True
|
|
||||||
elif o in ('-l', '--level'):
|
|
||||||
if a not in ('info', 'warn', 'fail'):
|
|
||||||
usage_cb(out, 'level {} is not valid'.format(a))
|
|
||||||
aconf.level = a
|
|
||||||
elif o in ('-t', '--timeout'):
|
|
||||||
aconf.timeout = float(a)
|
|
||||||
aconf.timeout_set = True
|
|
||||||
elif o in ('-M', '--make-policy'):
|
|
||||||
aconf.make_policy = True
|
|
||||||
aconf.policy_file = a
|
|
||||||
elif o in ('-P', '--policy'):
|
|
||||||
aconf.policy_file = a
|
|
||||||
elif o in ('-T', '--targets'):
|
|
||||||
aconf.target_file = a
|
|
||||||
|
|
||||||
# If we're on Windows, and we can't use the idna workaround, force only one thread to be used (otherwise a crash would occur).
|
parser = argparse.ArgumentParser(description="# {} {}, https://github.com/jtesta/ssh-audit".format(os.path.basename(sys.argv[0]), VERSION), allow_abbrev=False)
|
||||||
# if no_idna_workaround:
|
|
||||||
# print("\nWARNING: the idna module was not found on this system, thus only single-threaded scanning will be done (this is a workaround for this Windows-specific crash: https://github.com/python/cpython/issues/73474). Multi-threaded scanning can be enabled by installing the idna module (pip install idna).\n")
|
# Add short options to the parser
|
||||||
# aconf.threads = 1
|
parser.add_argument("-4", "--ipv4", action="store_true", dest="ipv4", default=False, help="enable IPv4 (order of precedence)")
|
||||||
elif o == '--threads':
|
parser.add_argument("-6", "--ipv6", action="store_true", dest="ipv6", default=False, help="enable IPv6 (order of precedence)")
|
||||||
aconf.threads = int(a)
|
parser.add_argument("-b", "--batch", action="store_true", dest="batch", default=False, help="batch output")
|
||||||
# if no_idna_workaround:
|
parser.add_argument("-c", "--client-audit", action="store_true", dest="client_audit", default=False, help="starts a server on port 2222 to audit client software config (use -p to change port; use -t to change timeout)")
|
||||||
# aconf.threads = 1
|
parser.add_argument("-d", "--debug", action="store_true", dest="debug", default=False, help="enable debugging output")
|
||||||
elif o in ('-L', '--list-policies'):
|
parser.add_argument("-g", "--gex-test", action="store", dest="gex_test", metavar="<min1:pref1:max1[,min2:pref2:max2,...]> / <x-y[:step]>", type=str, default=None, help="conducts a very customized Diffie-Hellman GEX modulus size test. Tests an array of minimum, preferred, and maximum values, or a range of values with an optional incremental step amount")
|
||||||
aconf.list_policies = True
|
parser.add_argument("-j", "--json", action="count", dest="json", default=0, help="enable JSON output (use -jj to enable indentation for better readability)")
|
||||||
elif o == '--lookup':
|
parser.add_argument("-l", "--level", action="store", dest="level", type=str, choices=["info", "warn", "fail"], default="info", help="minimum output level (default: %(default)s)")
|
||||||
aconf.lookup = a
|
parser.add_argument("-L", "--list-policies", action="store_true", dest="list_policies", default=False, help="list all the official, built-in policies. Combine with -v to view policy change logs")
|
||||||
elif o in ('-m', '--manual'):
|
parser.add_argument("-M", "--make-policy", action="store", dest="make_policy", metavar="custom_policy.txt", type=str, default=None, help="creates a policy based on the target server (i.e.: the target server has the ideal configuration that other servers should adhere to), and stores it in the file path specified")
|
||||||
aconf.manual = True
|
parser.add_argument("-m", "--manual", action="store_true", dest="manual", default=False, help="print the man page (Docker, PyPI, Snap, and Windows builds only)")
|
||||||
elif o in ('-d', '--debug'):
|
parser.add_argument("-n", "--no-colors", action="store_true", dest="no_colors", default=False, help="disable colors (automatic when the NO_COLOR environment variable is set)")
|
||||||
|
parser.add_argument("-P", "--policy", action="store", dest="policy", metavar="\"Built-In Policy Name\" / custom_policy.txt", type=str, default=None, help="run a policy test using the specified policy (use -L to see built-in policies, or specify filesystem path to custom policy created by -M)")
|
||||||
|
parser.add_argument("-p", "--port", action="store", dest="oport", metavar="N", type=int, default=None, help="the TCP port to connect to (or to listen on when -c is used)")
|
||||||
|
parser.add_argument("-T", "--targets", action="store", dest="targets", metavar="targets.txt", type=str, default=None, help="a file containing a list of target hosts (one per line, format HOST[:PORT]). Use -p/--port to set the default port for all hosts. Use --threads to control concurrent scans")
|
||||||
|
parser.add_argument("-t", "--timeout", action="store", dest="timeout", metavar="N", type=int, default=5, help="timeout (in seconds) for connection and reading (default: %(default)s)")
|
||||||
|
parser.add_argument("-v", "--verbose", action="store_true", dest="verbose", default=False, help="enable verbose output")
|
||||||
|
|
||||||
|
# Add long options to the parser
|
||||||
|
parser.add_argument("--conn-rate-test", action="store", dest="conn_rate_test", metavar="N[:max_rate]", type=str, default=None, help="perform a connection rate test (useful for collecting metrics related to susceptibility of the DHEat vuln). Testing is conducted with N concurrent sockets with an optional maximum rate of connections per second")
|
||||||
|
parser.add_argument("--dheat", action="store", dest="dheat", metavar="N[:kex[:e_len]]", type=str, default=None, help="continuously perform the DHEat DoS attack (CVE-2002-20001) against the target using N concurrent sockets. Optionally, a specific key exchange algorithm can be specified instead of allowing it to be automatically chosen. Additionally, a small length of the fake e value sent to the server can be chosen for a more efficient attack (such as 4).")
|
||||||
|
parser.add_argument("--lookup", action="store", dest="lookup", metavar="alg1[,alg2,...]", type=str, default=None, help="looks up an algorithm(s) without connecting to a server.")
|
||||||
|
parser.add_argument("--skip-rate-test", action="store_true", dest="skip_rate_test", default=False, help="skip the connection rate test during standard audits (used to safely infer whether the DHEat attack is viable)")
|
||||||
|
parser.add_argument("--threads", action="store", dest="threads", metavar="N", type=int, default=32, help="number of threads to use when scanning multiple targets (-T/--targets) (default: %(default)s)")
|
||||||
|
|
||||||
|
# The mandatory target option. Or rather, mandatory when -L, -T, or --lookup are not used.
|
||||||
|
parser.add_argument("host", nargs="?", action="store", type=str, default="", help="target hostname or IPv4/IPv6 address")
|
||||||
|
|
||||||
|
# If no arguments were given, print the help and exit.
|
||||||
|
if len(args) < 1:
|
||||||
|
parser.print_help()
|
||||||
|
sys.exit(exitcodes.UNKNOWN_ERROR)
|
||||||
|
|
||||||
|
oport: Optional[int] = None
|
||||||
|
try:
|
||||||
|
argument = parser.parse_args(args=args)
|
||||||
|
|
||||||
|
# Set simple flags.
|
||||||
|
aconf.client_audit = argument.client_audit
|
||||||
|
aconf.ipv4 = argument.ipv4
|
||||||
|
aconf.ipv6 = argument.ipv6
|
||||||
|
aconf.level = argument.level
|
||||||
|
aconf.list_policies = argument.list_policies
|
||||||
|
aconf.manual = argument.manual
|
||||||
|
aconf.skip_rate_test = argument.skip_rate_test
|
||||||
|
oport = argument.oport
|
||||||
|
|
||||||
|
if argument.batch is True:
|
||||||
|
aconf.batch = True
|
||||||
|
|
||||||
|
# If one -j was given, turn on JSON output. If -jj was given, enable indentation.
|
||||||
|
aconf.json = argument.json > 0
|
||||||
|
if argument.json > 1:
|
||||||
|
aconf.json_print_indent = True
|
||||||
|
|
||||||
|
if argument.conn_rate_test is not None:
|
||||||
|
aconf.conn_rate_test = argument.conn_rate_test
|
||||||
|
|
||||||
|
if argument.debug is True:
|
||||||
aconf.debug = True
|
aconf.debug = True
|
||||||
out.debug = True
|
out.debug = True
|
||||||
elif o in ('-g', '--gex-test'):
|
|
||||||
|
if argument.dheat is not None:
|
||||||
|
aconf.dheat = argument.dheat
|
||||||
|
|
||||||
|
if argument.gex_test is not None:
|
||||||
|
dh_gex = argument.gex_test
|
||||||
permitted_syntax = get_permitted_syntax_for_gex_test()
|
permitted_syntax = get_permitted_syntax_for_gex_test()
|
||||||
|
|
||||||
if not any(re.search(regex_str, a) for regex_str in permitted_syntax.values()):
|
if not any(re.search(regex_str, dh_gex) for regex_str in permitted_syntax.values()):
|
||||||
usage_cb(out, '{} {} is not valid'.format(o, a))
|
out.fail('{} is not valid'.format(dh_gex), write_now=True)
|
||||||
|
sys.exit(exitcodes.UNKNOWN_ERROR)
|
||||||
|
|
||||||
if re.search(permitted_syntax['RANGE'], a):
|
if re.search(permitted_syntax['RANGE'], dh_gex):
|
||||||
extracted_digits = re.findall(r'\d+', a)
|
extracted_digits = re.findall(r'\d+', dh_gex)
|
||||||
bits_left_bound = int(extracted_digits[0])
|
bits_left_bound = int(extracted_digits[0])
|
||||||
bits_right_bound = int(extracted_digits[1])
|
bits_right_bound = int(extracted_digits[1])
|
||||||
|
|
||||||
@@ -920,27 +851,52 @@ def process_commandline(out: OutputBuffer, args: List[str], usage_cb: Callable[.
|
|||||||
bits_step = int(extracted_digits[2])
|
bits_step = int(extracted_digits[2])
|
||||||
|
|
||||||
if bits_step <= 0:
|
if bits_step <= 0:
|
||||||
usage_cb(out, '{} {} is not valid'.format(o, bits_step))
|
out.fail('the step field cannot be 0 or less: {}'.format(bits_step), write_now=True)
|
||||||
|
sys.exit(exitcodes.UNKNOWN_ERROR)
|
||||||
|
|
||||||
if all(x < 0 for x in (bits_left_bound, bits_right_bound)):
|
if all(x < 0 for x in (bits_left_bound, bits_right_bound)):
|
||||||
usage_cb(out, '{} {} {} is not valid'.format(o, bits_left_bound, bits_right_bound))
|
out.fail('{} {} {} is not valid'.format(dh_gex, bits_left_bound, bits_right_bound), write_now=True)
|
||||||
|
sys.exit(exitcodes.UNKNOWN_ERROR)
|
||||||
|
|
||||||
aconf.gex_test = a
|
aconf.gex_test = dh_gex
|
||||||
elif o == '--dheat':
|
|
||||||
aconf.dheat = a
|
|
||||||
elif o == '--skip-rate-test':
|
|
||||||
aconf.skip_rate_test = True
|
|
||||||
elif o == '--conn-rate-test':
|
|
||||||
aconf.conn_rate_test = a
|
|
||||||
|
|
||||||
|
if argument.lookup is not None:
|
||||||
|
aconf.lookup = argument.lookup
|
||||||
|
|
||||||
if len(args) == 0 and aconf.client_audit is False and aconf.target_file is None and aconf.list_policies is False and aconf.lookup == '' and aconf.manual is False:
|
if argument.make_policy is not None:
|
||||||
usage_cb(out)
|
aconf.make_policy = True
|
||||||
|
aconf.policy_file = argument.make_policy
|
||||||
|
|
||||||
|
if argument.policy is not None:
|
||||||
|
aconf.policy_file = argument.policy
|
||||||
|
|
||||||
|
if argument.targets is not None:
|
||||||
|
aconf.target_file = argument.targets
|
||||||
|
|
||||||
|
if argument.threads is not None:
|
||||||
|
aconf.threads = argument.threads
|
||||||
|
|
||||||
|
if argument.timeout is not None:
|
||||||
|
aconf.timeout = float(argument.timeout)
|
||||||
|
aconf.timeout_set = True
|
||||||
|
|
||||||
|
if argument.verbose is True:
|
||||||
|
aconf.verbose = True
|
||||||
|
out.verbose = True
|
||||||
|
|
||||||
|
except argparse.ArgumentError as err:
|
||||||
|
out.fail(str(err), write_now=True)
|
||||||
|
parser.print_help()
|
||||||
|
sys.exit(exitcodes.UNKNOWN_ERROR)
|
||||||
|
|
||||||
|
if argument.host == "" and argument.client_audit is False and argument.targets is None and argument.list_policies is False and argument.lookup is None and argument.manual is False:
|
||||||
|
out.fail("target host must be specified, unless -c, -m, -L, -T, or --lookup are used", write_now=True)
|
||||||
|
sys.exit(exitcodes.UNKNOWN_ERROR)
|
||||||
|
|
||||||
if aconf.manual:
|
if aconf.manual:
|
||||||
return aconf
|
return aconf
|
||||||
|
|
||||||
if aconf.lookup != '':
|
if aconf.lookup != "":
|
||||||
return aconf
|
return aconf
|
||||||
|
|
||||||
if aconf.list_policies:
|
if aconf.list_policies:
|
||||||
@@ -949,27 +905,25 @@ def process_commandline(out: OutputBuffer, args: List[str], usage_cb: Callable[.
|
|||||||
|
|
||||||
if aconf.client_audit is False and aconf.target_file is None:
|
if aconf.client_audit is False and aconf.target_file is None:
|
||||||
if oport is not None:
|
if oport is not None:
|
||||||
host = args[0]
|
host = argument.host
|
||||||
else:
|
else:
|
||||||
host, port = Utils.parse_host_and_port(args[0])
|
host, port = Utils.parse_host_and_port(argument.host)
|
||||||
if not host and aconf.target_file is None:
|
|
||||||
usage_cb(out, 'host is empty')
|
|
||||||
|
|
||||||
if port == 0 and oport is None:
|
if not host and aconf.target_file is None:
|
||||||
if aconf.client_audit: # The default port to listen on during a client audit is 2222.
|
out.fail("target host is not specified", write_now=True)
|
||||||
port = 2222
|
sys.exit(exitcodes.UNKNOWN_ERROR)
|
||||||
else:
|
|
||||||
port = 22
|
if oport is None and aconf.client_audit: # The default port to listen on during a client audit is 2222.
|
||||||
|
port = 2222
|
||||||
|
|
||||||
if oport is not None:
|
if oport is not None:
|
||||||
port = Utils.parse_int(oport)
|
port = Utils.parse_int(oport)
|
||||||
if port <= 0 or port > 65535:
|
if port < 1 or port > 65535:
|
||||||
usage_cb(out, 'port {} is not valid'.format(oport))
|
out.fail("port must be greater than 0 and less than 65535: {}".format(oport), write_now=True)
|
||||||
|
sys.exit(exitcodes.UNKNOWN_ERROR)
|
||||||
|
|
||||||
aconf.host = host
|
aconf.host = host
|
||||||
aconf.port = port
|
aconf.port = port
|
||||||
if not (aconf.ssh1 or aconf.ssh2):
|
|
||||||
aconf.ssh1, aconf.ssh2 = True, True
|
|
||||||
|
|
||||||
# If a file containing a list of targets was given, read it.
|
# If a file containing a list of targets was given, read it.
|
||||||
if aconf.target_file is not None:
|
if aconf.target_file is not None:
|
||||||
@@ -996,26 +950,23 @@ def process_commandline(out: OutputBuffer, args: List[str], usage_cb: Callable[.
|
|||||||
try:
|
try:
|
||||||
aconf.policy = Policy(policy_file=aconf.policy_file, json_output=aconf.json)
|
aconf.policy = Policy(policy_file=aconf.policy_file, json_output=aconf.json)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
out.fail("Error while loading policy file: %s: %s" % (str(e), traceback.format_exc()))
|
out.fail("Error while loading policy file: %s: %s" % (str(e), traceback.format_exc()), write_now=True)
|
||||||
out.write()
|
|
||||||
sys.exit(exitcodes.UNKNOWN_ERROR)
|
sys.exit(exitcodes.UNKNOWN_ERROR)
|
||||||
|
|
||||||
# If the user wants to do a client audit, but provided a server policy, terminate.
|
# If the user wants to do a client audit, but provided a server policy, terminate.
|
||||||
if aconf.client_audit and aconf.policy.is_server_policy():
|
if aconf.client_audit and aconf.policy.is_server_policy():
|
||||||
out.fail("Error: client audit selected, but server policy provided.")
|
out.fail("Error: client audit selected, but server policy provided.", write_now=True)
|
||||||
out.write()
|
|
||||||
sys.exit(exitcodes.UNKNOWN_ERROR)
|
sys.exit(exitcodes.UNKNOWN_ERROR)
|
||||||
|
|
||||||
# If the user wants to do a server audit, but provided a client policy, terminate.
|
# If the user wants to do a server audit, but provided a client policy, terminate.
|
||||||
if aconf.client_audit is False and aconf.policy.is_server_policy() is False:
|
if aconf.client_audit is False and aconf.policy.is_server_policy() is False:
|
||||||
out.fail("Error: server audit selected, but client policy provided.")
|
out.fail("Error: server audit selected, but client policy provided.", write_now=True)
|
||||||
out.write()
|
|
||||||
sys.exit(exitcodes.UNKNOWN_ERROR)
|
sys.exit(exitcodes.UNKNOWN_ERROR)
|
||||||
|
|
||||||
return aconf
|
return aconf
|
||||||
|
|
||||||
|
|
||||||
def build_struct(target_host: str, banner: Optional['Banner'], kex: Optional['SSH2_Kex'] = None, pkm: Optional['SSH1_PublicKeyMessage'] = None, client_host: Optional[str] = None, software: Optional[Software] = None, algorithms: Optional[Algorithms] = None, algorithm_recommendation_suppress_list: Optional[List[str]] = None, additional_notes: List[str] = []) -> Any: # pylint: disable=dangerous-default-value
|
def build_struct(target_host: str, banner: Optional['Banner'], kex: Optional['SSH2_Kex'] = None, client_host: Optional[str] = None, software: Optional[Software] = None, algorithms: Optional[Algorithms] = None, algorithm_recommendation_suppress_list: Optional[List[str]] = None, additional_notes: List[str] = []) -> Any: # pylint: disable=dangerous-default-value
|
||||||
|
|
||||||
def fetch_notes(algorithm: str, alg_type: str) -> Dict[str, List[Optional[str]]]:
|
def fetch_notes(algorithm: str, alg_type: str) -> Dict[str, List[Optional[str]]]:
|
||||||
'''Returns a dictionary containing the messages in the "fail", "warn", and "info" levels for this algorithm.'''
|
'''Returns a dictionary containing the messages in the "fail", "warn", and "info" levels for this algorithm.'''
|
||||||
@@ -1160,22 +1111,6 @@ def build_struct(target_host: str, banner: Optional['Banner'], kex: Optional['SS
|
|||||||
'hash_alg': 'MD5',
|
'hash_alg': 'MD5',
|
||||||
'hash': fp.md5[4:]
|
'hash': fp.md5[4:]
|
||||||
})
|
})
|
||||||
else:
|
|
||||||
pkm_supported_ciphers = None
|
|
||||||
pkm_supported_authentications = None
|
|
||||||
pkm_fp = None
|
|
||||||
if pkm is not None:
|
|
||||||
pkm_supported_ciphers = pkm.supported_ciphers
|
|
||||||
pkm_supported_authentications = pkm.supported_authentications
|
|
||||||
pkm_fp = Fingerprint(pkm.host_key_fingerprint_data).sha256
|
|
||||||
|
|
||||||
res['key'] = ['ssh-rsa1']
|
|
||||||
res['enc'] = pkm_supported_ciphers
|
|
||||||
res['aut'] = pkm_supported_authentications
|
|
||||||
res['fingerprints'] = [{
|
|
||||||
'type': 'ssh-rsa1',
|
|
||||||
'fp': pkm_fp,
|
|
||||||
}]
|
|
||||||
|
|
||||||
# Historically, CVE information was returned. Now we'll just return an empty dictionary so as to not break any legacy clients.
|
# Historically, CVE information was returned. Now we'll just return an empty dictionary so as to not break any legacy clients.
|
||||||
res['cves'] = []
|
res['cves'] = []
|
||||||
@@ -1190,7 +1125,7 @@ def build_struct(target_host: str, banner: Optional['Banner'], kex: Optional['SS
|
|||||||
|
|
||||||
|
|
||||||
# Returns one of the exitcodes.* flags.
|
# Returns one of the exitcodes.* flags.
|
||||||
def audit(out: OutputBuffer, aconf: AuditConf, sshv: Optional[int] = None, print_target: bool = False) -> int:
|
def audit(out: OutputBuffer, aconf: AuditConf, print_target: bool = False) -> int:
|
||||||
program_retval = exitcodes.GOOD
|
program_retval = exitcodes.GOOD
|
||||||
out.batch = aconf.batch
|
out.batch = aconf.batch
|
||||||
out.verbose = aconf.verbose
|
out.verbose = aconf.verbose
|
||||||
@@ -1216,10 +1151,8 @@ def audit(out: OutputBuffer, aconf: AuditConf, sshv: Optional[int] = None, print
|
|||||||
out.write()
|
out.write()
|
||||||
sys.exit(exitcodes.CONNECTION_ERROR)
|
sys.exit(exitcodes.CONNECTION_ERROR)
|
||||||
|
|
||||||
if sshv is None:
|
|
||||||
sshv = 2 if aconf.ssh2 else 1
|
|
||||||
err = None
|
err = None
|
||||||
banner, header, err = s.get_banner(sshv)
|
banner, header, err = s.get_banner()
|
||||||
if banner is None:
|
if banner is None:
|
||||||
if err is None:
|
if err is None:
|
||||||
err = '[exception] did not receive banner.'
|
err = '[exception] did not receive banner.'
|
||||||
@@ -1228,7 +1161,7 @@ def audit(out: OutputBuffer, aconf: AuditConf, sshv: Optional[int] = None, print
|
|||||||
if err is None:
|
if err is None:
|
||||||
s.send_kexinit() # Send the algorithms we support (except we don't since this isn't a real SSH connection).
|
s.send_kexinit() # Send the algorithms we support (except we don't since this isn't a real SSH connection).
|
||||||
|
|
||||||
packet_type, payload = s.read_packet(sshv)
|
packet_type, payload = s.read_packet()
|
||||||
if packet_type < 0:
|
if packet_type < 0:
|
||||||
try:
|
try:
|
||||||
if len(payload) > 0:
|
if len(payload) > 0:
|
||||||
@@ -1237,17 +1170,10 @@ def audit(out: OutputBuffer, aconf: AuditConf, sshv: Optional[int] = None, print
|
|||||||
payload_txt = 'empty'
|
payload_txt = 'empty'
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
payload_txt = '"{}"'.format(repr(payload).lstrip('b')[1:-1])
|
payload_txt = '"{}"'.format(repr(payload).lstrip('b')[1:-1])
|
||||||
if payload_txt == 'Protocol major versions differ.':
|
|
||||||
if sshv == 2 and aconf.ssh1:
|
|
||||||
ret = audit(out, aconf, 1)
|
|
||||||
out.write()
|
|
||||||
return ret
|
|
||||||
err = '[exception] error reading packet ({})'.format(payload_txt)
|
err = '[exception] error reading packet ({})'.format(payload_txt)
|
||||||
else:
|
else:
|
||||||
err_pair = None
|
err_pair = None
|
||||||
if sshv == 1 and packet_type != Protocol.SMSG_PUBLIC_KEY:
|
if packet_type != Protocol.MSG_KEXINIT:
|
||||||
err_pair = ('SMSG_PUBLIC_KEY', Protocol.SMSG_PUBLIC_KEY)
|
|
||||||
elif sshv == 2 and packet_type != Protocol.MSG_KEXINIT:
|
|
||||||
err_pair = ('MSG_KEXINIT', Protocol.MSG_KEXINIT)
|
err_pair = ('MSG_KEXINIT', Protocol.MSG_KEXINIT)
|
||||||
if err_pair is not None:
|
if err_pair is not None:
|
||||||
fmt = '[exception] did not receive {0} ({1}), ' + \
|
fmt = '[exception] did not receive {0} ({1}), ' + \
|
||||||
@@ -1257,52 +1183,50 @@ def audit(out: OutputBuffer, aconf: AuditConf, sshv: Optional[int] = None, print
|
|||||||
output(out, aconf, banner, header)
|
output(out, aconf, banner, header)
|
||||||
out.fail(err)
|
out.fail(err)
|
||||||
return exitcodes.CONNECTION_ERROR
|
return exitcodes.CONNECTION_ERROR
|
||||||
if sshv == 1:
|
|
||||||
program_retval = output(out, aconf, banner, header, pkm=SSH1_PublicKeyMessage.parse(payload))
|
|
||||||
elif sshv == 2:
|
|
||||||
try:
|
|
||||||
kex = SSH2_Kex.parse(out, payload)
|
|
||||||
out.d(str(kex))
|
|
||||||
except Exception:
|
|
||||||
out.fail("Failed to parse server's kex. Stack trace:\n%s" % str(traceback.format_exc()))
|
|
||||||
return exitcodes.CONNECTION_ERROR
|
|
||||||
|
|
||||||
if aconf.dheat is not None:
|
try:
|
||||||
DHEat(out, aconf, banner, kex).run()
|
kex = SSH2_Kex.parse(out, payload)
|
||||||
return exitcodes.GOOD
|
out.d(str(kex))
|
||||||
elif aconf.conn_rate_test_enabled:
|
except Exception:
|
||||||
DHEat.dh_rate_test(out, aconf, kex, 0, 0, 0)
|
out.fail("Failed to parse server's kex. Stack trace:\n%s" % str(traceback.format_exc()))
|
||||||
return exitcodes.GOOD
|
return exitcodes.CONNECTION_ERROR
|
||||||
|
|
||||||
dh_rate_test_notes = ""
|
if aconf.dheat is not None:
|
||||||
if aconf.client_audit is False:
|
DHEat(out, aconf, banner, kex).run()
|
||||||
HostKeyTest.run(out, s, kex)
|
return exitcodes.GOOD
|
||||||
if aconf.gex_test != '':
|
elif aconf.conn_rate_test_enabled:
|
||||||
return run_gex_granular_modulus_size_test(out, s, kex, aconf)
|
DHEat.dh_rate_test(out, aconf, kex, 0, 0, 0)
|
||||||
else:
|
return exitcodes.GOOD
|
||||||
GEXTest.run(out, s, banner, kex)
|
|
||||||
|
|
||||||
# Skip the rate test if the user specified "--skip-rate-test".
|
|
||||||
if aconf.skip_rate_test:
|
|
||||||
out.d("Skipping rate test due to --skip-rate-test option.")
|
|
||||||
else:
|
|
||||||
# Try to open many TCP connections against the server if any Diffie-Hellman key exchanges are present; this tests potential vulnerability to the DHEat DOS attack. Use 3 concurrent sockets over at most 1.5 seconds to open at most 38 connections (stops if 1.5 seconds elapse, or 38 connections are opened--whichever comes first). If more than 25 connections per second were observed, flag the DH algorithms with a warning about the DHEat DOS vuln.
|
|
||||||
dh_rate_test_notes = DHEat.dh_rate_test(out, aconf, kex, 1.5, 38, 3)
|
|
||||||
|
|
||||||
# This is a standard audit scan.
|
|
||||||
if (aconf.policy is None) and (aconf.make_policy is False):
|
|
||||||
program_retval = output(out, aconf, banner, header, client_host=s.client_host, kex=kex, print_target=print_target, dh_rate_test_notes=dh_rate_test_notes)
|
|
||||||
|
|
||||||
# This is a policy test.
|
|
||||||
elif (aconf.policy is not None) and (aconf.make_policy is False):
|
|
||||||
program_retval = exitcodes.GOOD if evaluate_policy(out, aconf, banner, s.client_host, kex=kex) else exitcodes.FAILURE
|
|
||||||
|
|
||||||
# A new policy should be made from this scan.
|
|
||||||
elif (aconf.policy is None) and (aconf.make_policy is True):
|
|
||||||
make_policy(aconf, banner, kex, s.client_host)
|
|
||||||
|
|
||||||
|
dh_rate_test_notes = ""
|
||||||
|
if aconf.client_audit is False:
|
||||||
|
HostKeyTest.run(out, s, kex)
|
||||||
|
if aconf.gex_test != '':
|
||||||
|
return run_gex_granular_modulus_size_test(out, s, kex, aconf)
|
||||||
else:
|
else:
|
||||||
raise RuntimeError('Internal error while handling output: %r %r' % (aconf.policy is None, aconf.make_policy))
|
GEXTest.run(out, s, banner, kex)
|
||||||
|
|
||||||
|
# Skip the rate test if the user specified "--skip-rate-test".
|
||||||
|
if aconf.skip_rate_test:
|
||||||
|
out.d("Skipping rate test due to --skip-rate-test option.")
|
||||||
|
else:
|
||||||
|
# Try to open many TCP connections against the server if any Diffie-Hellman key exchanges are present; this tests potential vulnerability to the DHEat DOS attack. Use 3 concurrent sockets over at most 1.5 seconds to open at most 38 connections (stops if 1.5 seconds elapse, or 38 connections are opened--whichever comes first). If more than 25 connections per second were observed, flag the DH algorithms with a warning about the DHEat DOS vuln.
|
||||||
|
dh_rate_test_notes = DHEat.dh_rate_test(out, aconf, kex, 1.5, 38, 3)
|
||||||
|
|
||||||
|
# This is a standard audit scan.
|
||||||
|
if (aconf.policy is None) and (aconf.make_policy is False):
|
||||||
|
program_retval = output(out, aconf, banner, header, client_host=s.client_host, kex=kex, print_target=print_target, dh_rate_test_notes=dh_rate_test_notes)
|
||||||
|
|
||||||
|
# This is a policy test.
|
||||||
|
elif (aconf.policy is not None) and (aconf.make_policy is False):
|
||||||
|
program_retval = exitcodes.GOOD if evaluate_policy(out, aconf, banner, s.client_host, kex=kex) else exitcodes.FAILURE
|
||||||
|
|
||||||
|
# A new policy should be made from this scan.
|
||||||
|
elif (aconf.policy is None) and (aconf.make_policy is True):
|
||||||
|
make_policy(aconf, banner, kex, s.client_host)
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise RuntimeError('Internal error while handling output: %r %r' % (aconf.policy is None, aconf.make_policy))
|
||||||
|
|
||||||
return program_retval
|
return program_retval
|
||||||
|
|
||||||
@@ -1499,7 +1423,7 @@ def run_gex_granular_modulus_size_test(out: OutputBuffer, s: 'SSH_Socket', kex:
|
|||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
out = OutputBuffer()
|
out = OutputBuffer()
|
||||||
aconf = process_commandline(out, sys.argv[1:], usage)
|
aconf = process_commandline(out, sys.argv[1:])
|
||||||
|
|
||||||
# If we're on Windows, but the colorama module could not be imported, print a warning if we're in verbose mode.
|
# If we're on Windows, but the colorama module could not be imported, print a warning if we're in verbose mode.
|
||||||
if (sys.platform == 'win32') and ('colorama' not in sys.modules):
|
if (sys.platform == 'win32') and ('colorama' not in sys.modules):
|
||||||
@@ -1569,7 +1493,6 @@ def main() -> int:
|
|||||||
print(']')
|
print(']')
|
||||||
|
|
||||||
# Send notification that this thread is exiting. This deletes the thread's local copy of the algorithm databases.
|
# Send notification that this thread is exiting. This deletes the thread's local copy of the algorithm databases.
|
||||||
SSH1_KexDB.thread_exit()
|
|
||||||
SSH2_KexDB.thread_exit()
|
SSH2_KexDB.thread_exit()
|
||||||
|
|
||||||
else: # Just a scan against a single target.
|
else: # Just a scan against a single target.
|
||||||
|
|||||||
+13
-34
@@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (C) 2017-2021 Joe Testa (jtesta@positronsecurity.com)
|
Copyright (C) 2017-2025 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
|
||||||
@@ -39,7 +39,6 @@ from ssh_audit.globals import SSH_HEADER
|
|||||||
from ssh_audit.outputbuffer import OutputBuffer
|
from ssh_audit.outputbuffer import OutputBuffer
|
||||||
from ssh_audit.protocol import Protocol
|
from ssh_audit.protocol import Protocol
|
||||||
from ssh_audit.readbuf import ReadBuf
|
from ssh_audit.readbuf import ReadBuf
|
||||||
from ssh_audit.ssh1 import SSH1
|
|
||||||
from ssh_audit.ssh2_kex import SSH2_Kex
|
from ssh_audit.ssh2_kex import SSH2_Kex
|
||||||
from ssh_audit.ssh2_kexparty import SSH2_KexParty
|
from ssh_audit.ssh2_kexparty import SSH2_KexParty
|
||||||
from ssh_audit.utils import Utils
|
from ssh_audit.utils import Utils
|
||||||
@@ -173,7 +172,7 @@ class SSH_Socket(ReadBuf, WriteBuf):
|
|||||||
errm = 'cannot connect to {} port {}: {}'.format(*errt)
|
errm = 'cannot connect to {} port {}: {}'.format(*errt)
|
||||||
return '[exception] {}'.format(errm)
|
return '[exception] {}'.format(errm)
|
||||||
|
|
||||||
def get_banner(self, sshv: int = 2) -> Tuple[Optional['Banner'], List[str], Optional[str]]:
|
def get_banner(self) -> Tuple[Optional['Banner'], List[str], Optional[str]]:
|
||||||
self.__outputbuffer.d('Getting banner...', write_now=True)
|
self.__outputbuffer.d('Getting banner...', write_now=True)
|
||||||
|
|
||||||
if self.__sock is None:
|
if self.__sock is None:
|
||||||
@@ -181,7 +180,7 @@ class SSH_Socket(ReadBuf, WriteBuf):
|
|||||||
if self.__banner is not None:
|
if self.__banner is not None:
|
||||||
return self.__banner, self.__header, None
|
return self.__banner, self.__header, None
|
||||||
|
|
||||||
banner = SSH_HEADER.format('1.5' if sshv == 1 else '2.0')
|
banner = SSH_HEADER.format('2.0')
|
||||||
if self.__state < self.SM_BANNER_SENT:
|
if self.__state < self.SM_BANNER_SENT:
|
||||||
self.send_banner(banner)
|
self.send_banner(banner)
|
||||||
|
|
||||||
@@ -254,47 +253,27 @@ class SSH_Socket(ReadBuf, WriteBuf):
|
|||||||
if s < 0:
|
if s < 0:
|
||||||
raise SSH_Socket.InsufficientReadException(e)
|
raise SSH_Socket.InsufficientReadException(e)
|
||||||
|
|
||||||
def read_packet(self, sshv: int = 2) -> Tuple[int, bytes]:
|
def read_packet(self) -> Tuple[int, bytes]:
|
||||||
try:
|
try:
|
||||||
header = WriteBuf()
|
header = WriteBuf()
|
||||||
self.ensure_read(4)
|
self.ensure_read(4)
|
||||||
packet_length = self.read_int()
|
packet_length = self.read_int()
|
||||||
header.write_int(packet_length)
|
header.write_int(packet_length)
|
||||||
# XXX: validate length
|
# XXX: validate length
|
||||||
if sshv == 1:
|
self.ensure_read(1)
|
||||||
padding_length = 8 - packet_length % 8
|
padding_length = self.read_byte()
|
||||||
self.ensure_read(padding_length)
|
header.write_byte(padding_length)
|
||||||
padding = self.read(padding_length)
|
payload_length = packet_length - padding_length - 1
|
||||||
header.write(padding)
|
check_size = 4 + 1 + payload_length + padding_length
|
||||||
payload_length = packet_length
|
|
||||||
check_size = padding_length + payload_length
|
|
||||||
else:
|
|
||||||
self.ensure_read(1)
|
|
||||||
padding_length = self.read_byte()
|
|
||||||
header.write_byte(padding_length)
|
|
||||||
payload_length = packet_length - padding_length - 1
|
|
||||||
check_size = 4 + 1 + payload_length + padding_length
|
|
||||||
if check_size % self.__block_size != 0:
|
if check_size % self.__block_size != 0:
|
||||||
self.__outputbuffer.fail('[exception] invalid ssh packet (block size)').write()
|
self.__outputbuffer.fail('[exception] invalid ssh packet (block size)').write()
|
||||||
sys.exit(exitcodes.CONNECTION_ERROR)
|
sys.exit(exitcodes.CONNECTION_ERROR)
|
||||||
self.ensure_read(payload_length)
|
self.ensure_read(payload_length)
|
||||||
if sshv == 1:
|
payload = self.read(payload_length)
|
||||||
payload = self.read(payload_length - 4)
|
header.write(payload)
|
||||||
header.write(payload)
|
|
||||||
crc = self.read_int()
|
|
||||||
header.write_int(crc)
|
|
||||||
else:
|
|
||||||
payload = self.read(payload_length)
|
|
||||||
header.write(payload)
|
|
||||||
packet_type = ord(payload[0:1])
|
packet_type = ord(payload[0:1])
|
||||||
if sshv == 1:
|
self.ensure_read(padding_length)
|
||||||
rcrc = SSH1.crc32(padding + payload)
|
_ = self.read(padding_length)
|
||||||
if crc != rcrc:
|
|
||||||
self.__outputbuffer.fail('[exception] packet checksum CRC32 mismatch.').write()
|
|
||||||
sys.exit(exitcodes.CONNECTION_ERROR)
|
|
||||||
else:
|
|
||||||
self.ensure_read(padding_length)
|
|
||||||
padding = self.read(padding_length)
|
|
||||||
payload = payload[1:]
|
payload = payload[1:]
|
||||||
return packet_type, payload
|
return packet_type, payload
|
||||||
except SSH_Socket.InsufficientReadException as ex:
|
except SSH_Socket.InsufficientReadException as ex:
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ class Utils:
|
|||||||
return -1.0
|
return -1.0
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_host_and_port(host_and_port: str, default_port: int = 0) -> Tuple[str, int]:
|
def parse_host_and_port(host_and_port: str, default_port: int = 22) -> Tuple[str, int]:
|
||||||
'''Parses a string into a tuple of its host and port. The port is 0 if not specified.'''
|
'''Parses a string into a tuple of its host and port. The port is 0 if not specified.'''
|
||||||
host = host_and_port
|
host = host_and_port
|
||||||
port = default_port
|
port = default_port
|
||||||
|
|||||||
+1
-11
@@ -1,4 +1,4 @@
|
|||||||
.TH SSH-AUDIT 1 "September 24, 2024"
|
.TH SSH-AUDIT 1 "July 26, 2025"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
\fBssh-audit\fP \- SSH server & client configuration auditor
|
\fBssh-audit\fP \- SSH server & client configuration auditor
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@@ -16,16 +16,6 @@ See <https://www.ssh\-audit.com/> for official hardening guides for common platf
|
|||||||
.br
|
.br
|
||||||
Print short summary of options.
|
Print short summary of options.
|
||||||
|
|
||||||
.TP
|
|
||||||
.B -1, \-\-ssh1
|
|
||||||
.br
|
|
||||||
Only perform an audit using SSH protocol version 1.
|
|
||||||
|
|
||||||
.TP
|
|
||||||
.B -2, \-\-ssh2
|
|
||||||
.br
|
|
||||||
Only perform an audit using SSH protocol version 2.
|
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B -4, \-\-ipv4
|
.B -4, \-\-ipv4
|
||||||
.br
|
.br
|
||||||
|
|||||||
@@ -116,6 +116,9 @@
|
|||||||
"info": [
|
"info": [
|
||||||
"default key exchange from OpenSSH 7.4 to 8.9",
|
"default key exchange from OpenSSH 7.4 to 8.9",
|
||||||
"available since OpenSSH 7.4, Dropbear SSH 2018.76"
|
"available since OpenSSH 7.4, Dropbear SSH 2018.76"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -125,6 +128,9 @@
|
|||||||
"info": [
|
"info": [
|
||||||
"default key exchange from OpenSSH 6.5 to 7.3",
|
"default key exchange from OpenSSH 6.5 to 7.3",
|
||||||
"available since OpenSSH 6.4, Dropbear SSH 2013.62"
|
"available since OpenSSH 6.4, Dropbear SSH 2013.62"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -136,6 +142,9 @@
|
|||||||
],
|
],
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 5.7, Dropbear SSH 2013.62"
|
"available since OpenSSH 5.7, Dropbear SSH 2013.62"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -147,6 +156,9 @@
|
|||||||
],
|
],
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 5.7, Dropbear SSH 2013.62"
|
"available since OpenSSH 5.7, Dropbear SSH 2013.62"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -158,6 +170,9 @@
|
|||||||
],
|
],
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 5.7, Dropbear SSH 2013.62"
|
"available since OpenSSH 5.7, Dropbear SSH 2013.62"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -168,7 +183,8 @@
|
|||||||
"available since OpenSSH 7.3, Dropbear SSH 2016.73"
|
"available since OpenSSH 7.3, Dropbear SSH 2016.73"
|
||||||
],
|
],
|
||||||
"warn": [
|
"warn": [
|
||||||
"2048-bit modulus only provides 112-bits of symmetric strength"
|
"2048-bit modulus only provides 112-bits of symmetric strength",
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -182,7 +198,8 @@
|
|||||||
"available since OpenSSH 3.9, Dropbear SSH 0.53"
|
"available since OpenSSH 3.9, Dropbear SSH 0.53"
|
||||||
],
|
],
|
||||||
"warn": [
|
"warn": [
|
||||||
"2048-bit modulus only provides 112-bits of symmetric strength"
|
"2048-bit modulus only provides 112-bits of symmetric strength",
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -191,6 +208,9 @@
|
|||||||
"notes": {
|
"notes": {
|
||||||
"info": [
|
"info": [
|
||||||
"available since Dropbear SSH 2013.57"
|
"available since Dropbear SSH 2013.57"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -349,12 +369,6 @@
|
|||||||
"name": "twofish256-ctr",
|
"name": "twofish256-ctr",
|
||||||
"notes": ""
|
"notes": ""
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"kex": [
|
|
||||||
{
|
|
||||||
"name": "diffie-hellman-group16-sha512",
|
|
||||||
"notes": ""
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -371,9 +385,21 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"kex": [
|
"kex": [
|
||||||
|
{
|
||||||
|
"name": "curve25519-sha256",
|
||||||
|
"notes": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "curve25519-sha256@libssh.org",
|
||||||
|
"notes": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "diffie-hellman-group14-sha256",
|
"name": "diffie-hellman-group14-sha256",
|
||||||
"notes": ""
|
"notes": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kexguess2@matt.ucc.asn.au",
|
||||||
|
"notes": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"mac": [
|
"mac": [
|
||||||
|
|||||||
@@ -5,22 +5,30 @@
|
|||||||
[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, Dropbear SSH 2018.76[0m
|
[0;33m(kex) curve25519-sha256 -- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
[0;32m `- [info] default key exchange from OpenSSH 7.4 to 8.9[0m
|
`- [info] available since OpenSSH 7.4, Dropbear SSH 2018.76
|
||||||
[0;32m(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.4, Dropbear SSH 2013.62[0m
|
`- [info] default key exchange from OpenSSH 7.4 to 8.9
|
||||||
[0;32m `- [info] default key exchange from OpenSSH 6.5 to 7.3[0m
|
[0;33m(kex) curve25519-sha256@libssh.org -- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
|
`- [info] available since OpenSSH 6.4, Dropbear SSH 2013.62
|
||||||
|
`- [info] default key exchange from OpenSSH 6.5 to 7.3
|
||||||
[0;31m(kex) ecdh-sha2-nistp521 -- [fail] using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency[0m
|
[0;31m(kex) ecdh-sha2-nistp521 -- [fail] using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
||||||
[0;31m(kex) ecdh-sha2-nistp384 -- [fail] using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency[0m
|
[0;31m(kex) ecdh-sha2-nistp384 -- [fail] using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
||||||
[0;31m(kex) ecdh-sha2-nistp256 -- [fail] using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency[0m
|
[0;31m(kex) ecdh-sha2-nistp256 -- [fail] using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
||||||
[0;33m(kex) diffie-hellman-group14-sha256 -- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
[0;33m(kex) diffie-hellman-group14-sha256 -- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 7.3, Dropbear SSH 2016.73
|
`- [info] available since OpenSSH 7.3, Dropbear SSH 2016.73
|
||||||
[0;31m(kex) diffie-hellman-group14-sha1 -- [fail] using broken SHA-1 hash algorithm[0m
|
[0;31m(kex) diffie-hellman-group14-sha1 -- [fail] using broken SHA-1 hash algorithm[0m
|
||||||
[0;33m `- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
[0;33m `- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 3.9, Dropbear SSH 0.53
|
`- [info] available since OpenSSH 3.9, Dropbear SSH 0.53
|
||||||
[0;32m(kex) kexguess2@matt.ucc.asn.au -- [info] available since Dropbear SSH 2013.57[0m
|
[0;33m(kex) kexguess2@matt.ucc.asn.au -- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
|
`- [info] available since Dropbear SSH 2013.57
|
||||||
|
|
||||||
[0;36m# host-key algorithms[0m
|
[0;36m# host-key algorithms[0m
|
||||||
[0;31m(key) ecdsa-sha2-nistp256 -- [fail] using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency[0m
|
[0;31m(key) ecdsa-sha2-nistp256 -- [fail] using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency[0m
|
||||||
@@ -74,13 +82,15 @@
|
|||||||
[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;31m(rec) -ssh-rsa -- key algorithm to remove [0m
|
[0;31m(rec) -ssh-rsa -- key algorithm to remove [0m
|
||||||
[0;32m(rec) +diffie-hellman-group16-sha512 -- kex algorithm to append [0m
|
|
||||||
[0;32m(rec) +twofish128-ctr -- enc algorithm to append [0m
|
[0;32m(rec) +twofish128-ctr -- enc algorithm to append [0m
|
||||||
[0;32m(rec) +twofish256-ctr -- enc algorithm to append [0m
|
[0;32m(rec) +twofish256-ctr -- enc algorithm to append [0m
|
||||||
[0;33m(rec) -aes128-cbc -- enc algorithm to remove [0m
|
[0;33m(rec) -aes128-cbc -- enc algorithm to remove [0m
|
||||||
[0;33m(rec) -aes256-cbc -- enc algorithm to remove [0m
|
[0;33m(rec) -aes256-cbc -- enc algorithm to remove [0m
|
||||||
|
[0;33m(rec) -curve25519-sha256 -- kex algorithm to remove [0m
|
||||||
|
[0;33m(rec) -curve25519-sha256@libssh.org -- kex algorithm to remove [0m
|
||||||
[0;33m(rec) -diffie-hellman-group14-sha256 -- kex algorithm to remove [0m
|
[0;33m(rec) -diffie-hellman-group14-sha256 -- kex algorithm to remove [0m
|
||||||
[0;33m(rec) -hmac-sha2-256 -- mac algorithm to remove [0m
|
[0;33m(rec) -hmac-sha2-256 -- mac algorithm to remove [0m
|
||||||
|
[0;33m(rec) -kexguess2@matt.ucc.asn.au -- kex algorithm to remove [0m
|
||||||
|
|
||||||
[0;36m# additional info[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
|
[0;33m(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>[0m
|
||||||
|
|||||||
@@ -173,6 +173,9 @@
|
|||||||
],
|
],
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 2.3.0"
|
"available since OpenSSH 2.3.0"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -186,7 +189,8 @@
|
|||||||
"available since OpenSSH 3.9, Dropbear SSH 0.53"
|
"available since OpenSSH 3.9, Dropbear SSH 0.53"
|
||||||
],
|
],
|
||||||
"warn": [
|
"warn": [
|
||||||
"2048-bit modulus only provides 112-bits of symmetric strength"
|
"2048-bit modulus only provides 112-bits of symmetric strength",
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -201,6 +205,9 @@
|
|||||||
"info": [
|
"info": [
|
||||||
"removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9",
|
"removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9",
|
||||||
"available since OpenSSH 2.3.0, Dropbear SSH 0.28"
|
"available since OpenSSH 2.3.0, Dropbear SSH 0.28"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,13 +10,16 @@
|
|||||||
|
|
||||||
[0;36m# key exchange algorithms[0m
|
[0;36m# key exchange algorithms[0m
|
||||||
[0;31m(kex) diffie-hellman-group-exchange-sha1 (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
[0;31m(kex) diffie-hellman-group-exchange-sha1 (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 2.3.0
|
`- [info] available since OpenSSH 2.3.0
|
||||||
[0;31m(kex) diffie-hellman-group14-sha1 -- [fail] using broken SHA-1 hash algorithm[0m
|
[0;31m(kex) diffie-hellman-group14-sha1 -- [fail] using broken SHA-1 hash algorithm[0m
|
||||||
[0;33m `- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
[0;33m `- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 3.9, Dropbear SSH 0.53
|
`- [info] available since OpenSSH 3.9, Dropbear SSH 0.53
|
||||||
[0;31m(kex) diffie-hellman-group1-sha1 -- [fail] using small 1024-bit modulus[0m
|
[0;31m(kex) diffie-hellman-group1-sha1 -- [fail] using small 1024-bit modulus[0m
|
||||||
[0;31m `- [fail] vulnerable to the Logjam attack: https://en.wikipedia.org/wiki/Logjam_(computer_security)[0m
|
[0;31m `- [fail] vulnerable to the Logjam attack: https://en.wikipedia.org/wiki/Logjam_(computer_security)[0m
|
||||||
[0;31m `- [fail] using broken SHA-1 hash algorithm[0m
|
[0;31m `- [fail] using broken SHA-1 hash algorithm[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
||||||
`- [info] removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9
|
`- [info] removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9
|
||||||
|
|
||||||
|
|||||||
@@ -195,6 +195,9 @@
|
|||||||
],
|
],
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 4.4"
|
"available since OpenSSH 4.4"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -207,6 +210,9 @@
|
|||||||
],
|
],
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 2.3.0"
|
"available since OpenSSH 2.3.0"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -220,7 +226,8 @@
|
|||||||
"available since OpenSSH 3.9, Dropbear SSH 0.53"
|
"available since OpenSSH 3.9, Dropbear SSH 0.53"
|
||||||
],
|
],
|
||||||
"warn": [
|
"warn": [
|
||||||
"2048-bit modulus only provides 112-bits of symmetric strength"
|
"2048-bit modulus only provides 112-bits of symmetric strength",
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -235,6 +242,9 @@
|
|||||||
"info": [
|
"info": [
|
||||||
"removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9",
|
"removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9",
|
||||||
"available since OpenSSH 2.3.0, Dropbear SSH 0.28"
|
"available since OpenSSH 2.3.0, Dropbear SSH 0.28"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,15 +6,19 @@
|
|||||||
|
|
||||||
[0;36m# key exchange algorithms[0m
|
[0;36m# key exchange algorithms[0m
|
||||||
[0;31m(kex) diffie-hellman-group-exchange-sha256 (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
[0;31m(kex) diffie-hellman-group-exchange-sha256 (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 4.4
|
`- [info] available since OpenSSH 4.4
|
||||||
[0;31m(kex) diffie-hellman-group-exchange-sha1 (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
[0;31m(kex) diffie-hellman-group-exchange-sha1 (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 2.3.0
|
`- [info] available since OpenSSH 2.3.0
|
||||||
[0;31m(kex) diffie-hellman-group14-sha1 -- [fail] using broken SHA-1 hash algorithm[0m
|
[0;31m(kex) diffie-hellman-group14-sha1 -- [fail] using broken SHA-1 hash algorithm[0m
|
||||||
[0;33m `- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
[0;33m `- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 3.9, Dropbear SSH 0.53
|
`- [info] available since OpenSSH 3.9, Dropbear SSH 0.53
|
||||||
[0;31m(kex) diffie-hellman-group1-sha1 -- [fail] using small 1024-bit modulus[0m
|
[0;31m(kex) diffie-hellman-group1-sha1 -- [fail] using small 1024-bit modulus[0m
|
||||||
[0;31m `- [fail] vulnerable to the Logjam attack: https://en.wikipedia.org/wiki/Logjam_(computer_security)[0m
|
[0;31m `- [fail] vulnerable to the Logjam attack: https://en.wikipedia.org/wiki/Logjam_(computer_security)[0m
|
||||||
[0;31m `- [fail] using broken SHA-1 hash algorithm[0m
|
[0;31m `- [fail] using broken SHA-1 hash algorithm[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
||||||
`- [info] removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9
|
`- [info] removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9
|
||||||
|
|
||||||
|
|||||||
@@ -185,6 +185,9 @@
|
|||||||
],
|
],
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 4.4"
|
"available since OpenSSH 4.4"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -197,6 +200,9 @@
|
|||||||
],
|
],
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 2.3.0"
|
"available since OpenSSH 2.3.0"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -210,7 +216,8 @@
|
|||||||
"available since OpenSSH 3.9, Dropbear SSH 0.53"
|
"available since OpenSSH 3.9, Dropbear SSH 0.53"
|
||||||
],
|
],
|
||||||
"warn": [
|
"warn": [
|
||||||
"2048-bit modulus only provides 112-bits of symmetric strength"
|
"2048-bit modulus only provides 112-bits of symmetric strength",
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -225,6 +232,9 @@
|
|||||||
"info": [
|
"info": [
|
||||||
"removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9",
|
"removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9",
|
||||||
"available since OpenSSH 2.3.0, Dropbear SSH 0.28"
|
"available since OpenSSH 2.3.0, Dropbear SSH 0.28"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,15 +6,19 @@
|
|||||||
|
|
||||||
[0;36m# key exchange algorithms[0m
|
[0;36m# key exchange algorithms[0m
|
||||||
[0;31m(kex) diffie-hellman-group-exchange-sha256 (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
[0;31m(kex) diffie-hellman-group-exchange-sha256 (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 4.4
|
`- [info] available since OpenSSH 4.4
|
||||||
[0;31m(kex) diffie-hellman-group-exchange-sha1 (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
[0;31m(kex) diffie-hellman-group-exchange-sha1 (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 2.3.0
|
`- [info] available since OpenSSH 2.3.0
|
||||||
[0;31m(kex) diffie-hellman-group14-sha1 -- [fail] using broken SHA-1 hash algorithm[0m
|
[0;31m(kex) diffie-hellman-group14-sha1 -- [fail] using broken SHA-1 hash algorithm[0m
|
||||||
[0;33m `- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
[0;33m `- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 3.9, Dropbear SSH 0.53
|
`- [info] available since OpenSSH 3.9, Dropbear SSH 0.53
|
||||||
[0;31m(kex) diffie-hellman-group1-sha1 -- [fail] using small 1024-bit modulus[0m
|
[0;31m(kex) diffie-hellman-group1-sha1 -- [fail] using small 1024-bit modulus[0m
|
||||||
[0;31m `- [fail] vulnerable to the Logjam attack: https://en.wikipedia.org/wiki/Logjam_(computer_security)[0m
|
[0;31m `- [fail] vulnerable to the Logjam attack: https://en.wikipedia.org/wiki/Logjam_(computer_security)[0m
|
||||||
[0;31m `- [fail] using broken SHA-1 hash algorithm[0m
|
[0;31m `- [fail] using broken SHA-1 hash algorithm[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
||||||
`- [info] removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9
|
`- [info] removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9
|
||||||
|
|
||||||
|
|||||||
@@ -185,6 +185,9 @@
|
|||||||
],
|
],
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 4.4"
|
"available since OpenSSH 4.4"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -197,6 +200,9 @@
|
|||||||
],
|
],
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 2.3.0"
|
"available since OpenSSH 2.3.0"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -210,7 +216,8 @@
|
|||||||
"available since OpenSSH 3.9, Dropbear SSH 0.53"
|
"available since OpenSSH 3.9, Dropbear SSH 0.53"
|
||||||
],
|
],
|
||||||
"warn": [
|
"warn": [
|
||||||
"2048-bit modulus only provides 112-bits of symmetric strength"
|
"2048-bit modulus only provides 112-bits of symmetric strength",
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -225,6 +232,9 @@
|
|||||||
"info": [
|
"info": [
|
||||||
"removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9",
|
"removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9",
|
||||||
"available since OpenSSH 2.3.0, Dropbear SSH 0.28"
|
"available since OpenSSH 2.3.0, Dropbear SSH 0.28"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,15 +6,19 @@
|
|||||||
|
|
||||||
[0;36m# key exchange algorithms[0m
|
[0;36m# key exchange algorithms[0m
|
||||||
[0;31m(kex) diffie-hellman-group-exchange-sha256 (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
[0;31m(kex) diffie-hellman-group-exchange-sha256 (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 4.4
|
`- [info] available since OpenSSH 4.4
|
||||||
[0;31m(kex) diffie-hellman-group-exchange-sha1 (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
[0;31m(kex) diffie-hellman-group-exchange-sha1 (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 2.3.0
|
`- [info] available since OpenSSH 2.3.0
|
||||||
[0;31m(kex) diffie-hellman-group14-sha1 -- [fail] using broken SHA-1 hash algorithm[0m
|
[0;31m(kex) diffie-hellman-group14-sha1 -- [fail] using broken SHA-1 hash algorithm[0m
|
||||||
[0;33m `- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
[0;33m `- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 3.9, Dropbear SSH 0.53
|
`- [info] available since OpenSSH 3.9, Dropbear SSH 0.53
|
||||||
[0;31m(kex) diffie-hellman-group1-sha1 -- [fail] using small 1024-bit modulus[0m
|
[0;31m(kex) diffie-hellman-group1-sha1 -- [fail] using small 1024-bit modulus[0m
|
||||||
[0;31m `- [fail] vulnerable to the Logjam attack: https://en.wikipedia.org/wiki/Logjam_(computer_security)[0m
|
[0;31m `- [fail] vulnerable to the Logjam attack: https://en.wikipedia.org/wiki/Logjam_(computer_security)[0m
|
||||||
[0;31m `- [fail] using broken SHA-1 hash algorithm[0m
|
[0;31m `- [fail] using broken SHA-1 hash algorithm[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
||||||
`- [info] removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9
|
`- [info] removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9
|
||||||
|
|
||||||
|
|||||||
@@ -185,6 +185,9 @@
|
|||||||
],
|
],
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 4.4"
|
"available since OpenSSH 4.4"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -197,6 +200,9 @@
|
|||||||
],
|
],
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 2.3.0"
|
"available since OpenSSH 2.3.0"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -210,7 +216,8 @@
|
|||||||
"available since OpenSSH 3.9, Dropbear SSH 0.53"
|
"available since OpenSSH 3.9, Dropbear SSH 0.53"
|
||||||
],
|
],
|
||||||
"warn": [
|
"warn": [
|
||||||
"2048-bit modulus only provides 112-bits of symmetric strength"
|
"2048-bit modulus only provides 112-bits of symmetric strength",
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -225,6 +232,9 @@
|
|||||||
"info": [
|
"info": [
|
||||||
"removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9",
|
"removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9",
|
||||||
"available since OpenSSH 2.3.0, Dropbear SSH 0.28"
|
"available since OpenSSH 2.3.0, Dropbear SSH 0.28"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,15 +6,19 @@
|
|||||||
|
|
||||||
[0;36m# key exchange algorithms[0m
|
[0;36m# key exchange algorithms[0m
|
||||||
[0;31m(kex) diffie-hellman-group-exchange-sha256 (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
[0;31m(kex) diffie-hellman-group-exchange-sha256 (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 4.4
|
`- [info] available since OpenSSH 4.4
|
||||||
[0;31m(kex) diffie-hellman-group-exchange-sha1 (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
[0;31m(kex) diffie-hellman-group-exchange-sha1 (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 2.3.0
|
`- [info] available since OpenSSH 2.3.0
|
||||||
[0;31m(kex) diffie-hellman-group14-sha1 -- [fail] using broken SHA-1 hash algorithm[0m
|
[0;31m(kex) diffie-hellman-group14-sha1 -- [fail] using broken SHA-1 hash algorithm[0m
|
||||||
[0;33m `- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
[0;33m `- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 3.9, Dropbear SSH 0.53
|
`- [info] available since OpenSSH 3.9, Dropbear SSH 0.53
|
||||||
[0;31m(kex) diffie-hellman-group1-sha1 -- [fail] using small 1024-bit modulus[0m
|
[0;31m(kex) diffie-hellman-group1-sha1 -- [fail] using small 1024-bit modulus[0m
|
||||||
[0;31m `- [fail] vulnerable to the Logjam attack: https://en.wikipedia.org/wiki/Logjam_(computer_security)[0m
|
[0;31m `- [fail] vulnerable to the Logjam attack: https://en.wikipedia.org/wiki/Logjam_(computer_security)[0m
|
||||||
[0;31m `- [fail] using broken SHA-1 hash algorithm[0m
|
[0;31m `- [fail] using broken SHA-1 hash algorithm[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
||||||
`- [info] removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9
|
`- [info] removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9
|
||||||
|
|
||||||
|
|||||||
@@ -185,6 +185,9 @@
|
|||||||
],
|
],
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 4.4"
|
"available since OpenSSH 4.4"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -197,6 +200,9 @@
|
|||||||
],
|
],
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 2.3.0"
|
"available since OpenSSH 2.3.0"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -210,7 +216,8 @@
|
|||||||
"available since OpenSSH 3.9, Dropbear SSH 0.53"
|
"available since OpenSSH 3.9, Dropbear SSH 0.53"
|
||||||
],
|
],
|
||||||
"warn": [
|
"warn": [
|
||||||
"2048-bit modulus only provides 112-bits of symmetric strength"
|
"2048-bit modulus only provides 112-bits of symmetric strength",
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -225,6 +232,9 @@
|
|||||||
"info": [
|
"info": [
|
||||||
"removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9",
|
"removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9",
|
||||||
"available since OpenSSH 2.3.0, Dropbear SSH 0.28"
|
"available since OpenSSH 2.3.0, Dropbear SSH 0.28"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,15 +6,19 @@
|
|||||||
|
|
||||||
[0;36m# key exchange algorithms[0m
|
[0;36m# key exchange algorithms[0m
|
||||||
[0;31m(kex) diffie-hellman-group-exchange-sha256 (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
[0;31m(kex) diffie-hellman-group-exchange-sha256 (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 4.4
|
`- [info] available since OpenSSH 4.4
|
||||||
[0;31m(kex) diffie-hellman-group-exchange-sha1 (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
[0;31m(kex) diffie-hellman-group-exchange-sha1 (1024-bit) -- [fail] using small 1024-bit modulus[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 2.3.0
|
`- [info] available since OpenSSH 2.3.0
|
||||||
[0;31m(kex) diffie-hellman-group14-sha1 -- [fail] using broken SHA-1 hash algorithm[0m
|
[0;31m(kex) diffie-hellman-group14-sha1 -- [fail] using broken SHA-1 hash algorithm[0m
|
||||||
[0;33m `- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
[0;33m `- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 3.9, Dropbear SSH 0.53
|
`- [info] available since OpenSSH 3.9, Dropbear SSH 0.53
|
||||||
[0;31m(kex) diffie-hellman-group1-sha1 -- [fail] using small 1024-bit modulus[0m
|
[0;31m(kex) diffie-hellman-group1-sha1 -- [fail] using small 1024-bit modulus[0m
|
||||||
[0;31m `- [fail] vulnerable to the Logjam attack: https://en.wikipedia.org/wiki/Logjam_(computer_security)[0m
|
[0;31m `- [fail] vulnerable to the Logjam attack: https://en.wikipedia.org/wiki/Logjam_(computer_security)[0m
|
||||||
[0;31m `- [fail] using broken SHA-1 hash algorithm[0m
|
[0;31m `- [fail] using broken SHA-1 hash algorithm[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
`- [info] available since OpenSSH 2.3.0, Dropbear SSH 0.28
|
||||||
`- [info] removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9
|
`- [info] removed in OpenSSH 6.9: https://www.openssh.com/txt/release-6.9
|
||||||
|
|
||||||
|
|||||||
@@ -104,6 +104,9 @@
|
|||||||
"info": [
|
"info": [
|
||||||
"default key exchange from OpenSSH 7.4 to 8.9",
|
"default key exchange from OpenSSH 7.4 to 8.9",
|
||||||
"available since OpenSSH 7.4, Dropbear SSH 2018.76"
|
"available since OpenSSH 7.4, Dropbear SSH 2018.76"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -113,6 +116,9 @@
|
|||||||
"info": [
|
"info": [
|
||||||
"default key exchange from OpenSSH 6.5 to 7.3",
|
"default key exchange from OpenSSH 6.5 to 7.3",
|
||||||
"available since OpenSSH 6.4, Dropbear SSH 2013.62"
|
"available since OpenSSH 6.4, Dropbear SSH 2013.62"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -124,6 +130,9 @@
|
|||||||
],
|
],
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 5.7, Dropbear SSH 2013.62"
|
"available since OpenSSH 5.7, Dropbear SSH 2013.62"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -135,6 +144,9 @@
|
|||||||
],
|
],
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 5.7, Dropbear SSH 2013.62"
|
"available since OpenSSH 5.7, Dropbear SSH 2013.62"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -146,6 +158,9 @@
|
|||||||
],
|
],
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 5.7, Dropbear SSH 2013.62"
|
"available since OpenSSH 5.7, Dropbear SSH 2013.62"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -156,6 +171,9 @@
|
|||||||
"info": [
|
"info": [
|
||||||
"OpenSSH's GEX fallback mechanism was triggered during testing. Very old SSH clients will still be able to create connections using a 2048-bit modulus, though modern clients will use 4096. This can only be disabled by recompiling the code (see https://github.com/openssh/openssh-portable/blob/V_9_4/dh.c#L477).",
|
"OpenSSH's GEX fallback mechanism was triggered during testing. Very old SSH clients will still be able to create connections using a 2048-bit modulus, though modern clients will use 4096. This can only be disabled by recompiling the code (see https://github.com/openssh/openssh-portable/blob/V_9_4/dh.c#L477).",
|
||||||
"available since OpenSSH 4.4"
|
"available since OpenSSH 4.4"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -164,6 +182,9 @@
|
|||||||
"notes": {
|
"notes": {
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 7.3, Dropbear SSH 2016.73"
|
"available since OpenSSH 7.3, Dropbear SSH 2016.73"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -172,6 +193,9 @@
|
|||||||
"notes": {
|
"notes": {
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 7.3"
|
"available since OpenSSH 7.3"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -182,7 +206,8 @@
|
|||||||
"available since OpenSSH 7.3, Dropbear SSH 2016.73"
|
"available since OpenSSH 7.3, Dropbear SSH 2016.73"
|
||||||
],
|
],
|
||||||
"warn": [
|
"warn": [
|
||||||
"2048-bit modulus only provides 112-bits of symmetric strength"
|
"2048-bit modulus only provides 112-bits of symmetric strength",
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -196,7 +221,8 @@
|
|||||||
"available since OpenSSH 3.9, Dropbear SSH 0.53"
|
"available since OpenSSH 3.9, Dropbear SSH 0.53"
|
||||||
],
|
],
|
||||||
"warn": [
|
"warn": [
|
||||||
"2048-bit modulus only provides 112-bits of symmetric strength"
|
"2048-bit modulus only provides 112-bits of symmetric strength",
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -407,6 +433,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"warning": {
|
"warning": {
|
||||||
|
"chg": {
|
||||||
|
"kex": [
|
||||||
|
{
|
||||||
|
"name": "diffie-hellman-group-exchange-sha256",
|
||||||
|
"notes": "increase modulus size to 3072 bits or larger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"del": {
|
"del": {
|
||||||
"enc": [
|
"enc": [
|
||||||
{
|
{
|
||||||
@@ -415,9 +449,25 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"kex": [
|
"kex": [
|
||||||
|
{
|
||||||
|
"name": "curve25519-sha256",
|
||||||
|
"notes": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "curve25519-sha256@libssh.org",
|
||||||
|
"notes": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "diffie-hellman-group14-sha256",
|
"name": "diffie-hellman-group14-sha256",
|
||||||
"notes": ""
|
"notes": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "diffie-hellman-group16-sha512",
|
||||||
|
"notes": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "diffie-hellman-group18-sha512",
|
||||||
|
"notes": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"mac": [
|
"mac": [
|
||||||
|
|||||||
@@ -5,24 +5,34 @@
|
|||||||
[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, Dropbear SSH 2018.76[0m
|
[0;33m(kex) curve25519-sha256 -- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
[0;32m `- [info] default key exchange from OpenSSH 7.4 to 8.9[0m
|
`- [info] available since OpenSSH 7.4, Dropbear SSH 2018.76
|
||||||
[0;32m(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.4, Dropbear SSH 2013.62[0m
|
`- [info] default key exchange from OpenSSH 7.4 to 8.9
|
||||||
[0;32m `- [info] default key exchange from OpenSSH 6.5 to 7.3[0m
|
[0;33m(kex) curve25519-sha256@libssh.org -- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
|
`- [info] available since OpenSSH 6.4, Dropbear SSH 2013.62
|
||||||
|
`- [info] default key exchange from OpenSSH 6.5 to 7.3
|
||||||
[0;31m(kex) ecdh-sha2-nistp256 -- [fail] using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency[0m
|
[0;31m(kex) ecdh-sha2-nistp256 -- [fail] using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
||||||
[0;31m(kex) ecdh-sha2-nistp384 -- [fail] using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency[0m
|
[0;31m(kex) ecdh-sha2-nistp384 -- [fail] using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
||||||
[0;31m(kex) ecdh-sha2-nistp521 -- [fail] using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency[0m
|
[0;31m(kex) ecdh-sha2-nistp521 -- [fail] using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
||||||
[0;32m(kex) diffie-hellman-group-exchange-sha256 (4096-bit) -- [info] available since OpenSSH 4.4[0m
|
[0;33m(kex) diffie-hellman-group-exchange-sha256 (4096-bit) -- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
[0;32m `- [info] OpenSSH's GEX fallback mechanism was triggered during testing. Very old SSH clients will still be able to create connections using a 2048-bit modulus, though modern clients will use 4096. This can only be disabled by recompiling the code (see https://github.com/openssh/openssh-portable/blob/V_9_4/dh.c#L477).[0m
|
`- [info] available since OpenSSH 4.4
|
||||||
[0;32m(kex) diffie-hellman-group16-sha512 -- [info] available since OpenSSH 7.3, Dropbear SSH 2016.73[0m
|
`- [info] OpenSSH's GEX fallback mechanism was triggered during testing. Very old SSH clients will still be able to create connections using a 2048-bit modulus, though modern clients will use 4096. This can only be disabled by recompiling the code (see https://github.com/openssh/openssh-portable/blob/V_9_4/dh.c#L477).
|
||||||
[0;32m(kex) diffie-hellman-group18-sha512 -- [info] available since OpenSSH 7.3[0m
|
[0;33m(kex) diffie-hellman-group16-sha512 -- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
|
`- [info] available since OpenSSH 7.3, Dropbear SSH 2016.73
|
||||||
|
[0;33m(kex) diffie-hellman-group18-sha512 -- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
|
`- [info] available since OpenSSH 7.3
|
||||||
[0;33m(kex) diffie-hellman-group14-sha256 -- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
[0;33m(kex) diffie-hellman-group14-sha256 -- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 7.3, Dropbear SSH 2016.73
|
`- [info] available since OpenSSH 7.3, Dropbear SSH 2016.73
|
||||||
[0;31m(kex) diffie-hellman-group14-sha1 -- [fail] using broken SHA-1 hash algorithm[0m
|
[0;31m(kex) diffie-hellman-group14-sha1 -- [fail] using broken SHA-1 hash algorithm[0m
|
||||||
[0;33m `- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
[0;33m `- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 3.9, Dropbear SSH 0.53
|
`- [info] available since OpenSSH 3.9, Dropbear SSH 0.53
|
||||||
|
|
||||||
[0;36m# host-key algorithms[0m
|
[0;36m# host-key algorithms[0m
|
||||||
@@ -80,8 +90,13 @@
|
|||||||
[0;31m(rec) -hmac-sha1 -- mac algorithm to remove [0m
|
[0;31m(rec) -hmac-sha1 -- mac algorithm to remove [0m
|
||||||
[0;31m(rec) -hmac-sha1-etm@openssh.com -- mac algorithm to remove [0m
|
[0;31m(rec) -hmac-sha1-etm@openssh.com -- mac algorithm to remove [0m
|
||||||
[0;31m(rec) -ssh-rsa -- key algorithm to remove [0m
|
[0;31m(rec) -ssh-rsa -- key algorithm to remove [0m
|
||||||
|
[0;33m(rec) !diffie-hellman-group-exchange-sha256 -- kex algorithm to change (increase modulus size to 3072 bits or larger) [0m
|
||||||
[0;33m(rec) -chacha20-poly1305@openssh.com -- enc algorithm to remove [0m
|
[0;33m(rec) -chacha20-poly1305@openssh.com -- enc algorithm to remove [0m
|
||||||
|
[0;33m(rec) -curve25519-sha256 -- kex algorithm to remove [0m
|
||||||
|
[0;33m(rec) -curve25519-sha256@libssh.org -- kex algorithm to remove [0m
|
||||||
[0;33m(rec) -diffie-hellman-group14-sha256 -- kex algorithm to remove [0m
|
[0;33m(rec) -diffie-hellman-group14-sha256 -- kex algorithm to remove [0m
|
||||||
|
[0;33m(rec) -diffie-hellman-group16-sha512 -- kex algorithm to remove [0m
|
||||||
|
[0;33m(rec) -diffie-hellman-group18-sha512 -- kex algorithm to remove [0m
|
||||||
[0;33m(rec) -hmac-sha2-256 -- mac algorithm to remove [0m
|
[0;33m(rec) -hmac-sha2-256 -- mac algorithm to remove [0m
|
||||||
[0;33m(rec) -hmac-sha2-512 -- mac algorithm to remove [0m
|
[0;33m(rec) -hmac-sha2-512 -- mac algorithm to remove [0m
|
||||||
[0;33m(rec) -umac-128@openssh.com -- mac algorithm to remove [0m
|
[0;33m(rec) -umac-128@openssh.com -- mac algorithm to remove [0m
|
||||||
|
|||||||
@@ -84,6 +84,9 @@
|
|||||||
"info": [
|
"info": [
|
||||||
"default key exchange from OpenSSH 7.4 to 8.9",
|
"default key exchange from OpenSSH 7.4 to 8.9",
|
||||||
"available since OpenSSH 7.4, Dropbear SSH 2018.76"
|
"available since OpenSSH 7.4, Dropbear SSH 2018.76"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -93,6 +96,9 @@
|
|||||||
"info": [
|
"info": [
|
||||||
"default key exchange from OpenSSH 6.5 to 7.3",
|
"default key exchange from OpenSSH 6.5 to 7.3",
|
||||||
"available since OpenSSH 6.4, Dropbear SSH 2013.62"
|
"available since OpenSSH 6.4, Dropbear SSH 2013.62"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -104,6 +110,9 @@
|
|||||||
],
|
],
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 5.7, Dropbear SSH 2013.62"
|
"available since OpenSSH 5.7, Dropbear SSH 2013.62"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -115,6 +124,9 @@
|
|||||||
],
|
],
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 5.7, Dropbear SSH 2013.62"
|
"available since OpenSSH 5.7, Dropbear SSH 2013.62"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -126,6 +138,9 @@
|
|||||||
],
|
],
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 5.7, Dropbear SSH 2013.62"
|
"available since OpenSSH 5.7, Dropbear SSH 2013.62"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -136,6 +151,9 @@
|
|||||||
"info": [
|
"info": [
|
||||||
"OpenSSH's GEX fallback mechanism was triggered during testing. Very old SSH clients will still be able to create connections using a 2048-bit modulus, though modern clients will use 4096. This can only be disabled by recompiling the code (see https://github.com/openssh/openssh-portable/blob/V_9_4/dh.c#L477).",
|
"OpenSSH's GEX fallback mechanism was triggered during testing. Very old SSH clients will still be able to create connections using a 2048-bit modulus, though modern clients will use 4096. This can only be disabled by recompiling the code (see https://github.com/openssh/openssh-portable/blob/V_9_4/dh.c#L477).",
|
||||||
"available since OpenSSH 4.4"
|
"available since OpenSSH 4.4"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -144,6 +162,9 @@
|
|||||||
"notes": {
|
"notes": {
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 7.3, Dropbear SSH 2016.73"
|
"available since OpenSSH 7.3, Dropbear SSH 2016.73"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -152,6 +173,9 @@
|
|||||||
"notes": {
|
"notes": {
|
||||||
"info": [
|
"info": [
|
||||||
"available since OpenSSH 7.3"
|
"available since OpenSSH 7.3"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -162,7 +186,8 @@
|
|||||||
"available since OpenSSH 7.3, Dropbear SSH 2016.73"
|
"available since OpenSSH 7.3, Dropbear SSH 2016.73"
|
||||||
],
|
],
|
||||||
"warn": [
|
"warn": [
|
||||||
"2048-bit modulus only provides 112-bits of symmetric strength"
|
"2048-bit modulus only provides 112-bits of symmetric strength",
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -176,7 +201,8 @@
|
|||||||
"available since OpenSSH 3.9, Dropbear SSH 0.53"
|
"available since OpenSSH 3.9, Dropbear SSH 0.53"
|
||||||
],
|
],
|
||||||
"warn": [
|
"warn": [
|
||||||
"2048-bit modulus only provides 112-bits of symmetric strength"
|
"2048-bit modulus only provides 112-bits of symmetric strength",
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -356,6 +382,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"warning": {
|
"warning": {
|
||||||
|
"chg": {
|
||||||
|
"kex": [
|
||||||
|
{
|
||||||
|
"name": "diffie-hellman-group-exchange-sha256",
|
||||||
|
"notes": "increase modulus size to 3072 bits or larger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"del": {
|
"del": {
|
||||||
"enc": [
|
"enc": [
|
||||||
{
|
{
|
||||||
@@ -364,9 +398,25 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"kex": [
|
"kex": [
|
||||||
|
{
|
||||||
|
"name": "curve25519-sha256",
|
||||||
|
"notes": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "curve25519-sha256@libssh.org",
|
||||||
|
"notes": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "diffie-hellman-group14-sha256",
|
"name": "diffie-hellman-group14-sha256",
|
||||||
"notes": ""
|
"notes": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "diffie-hellman-group16-sha512",
|
||||||
|
"notes": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "diffie-hellman-group18-sha512",
|
||||||
|
"notes": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"mac": [
|
"mac": [
|
||||||
|
|||||||
@@ -5,24 +5,34 @@
|
|||||||
[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, Dropbear SSH 2018.76[0m
|
[0;33m(kex) curve25519-sha256 -- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
[0;32m `- [info] default key exchange from OpenSSH 7.4 to 8.9[0m
|
`- [info] available since OpenSSH 7.4, Dropbear SSH 2018.76
|
||||||
[0;32m(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.4, Dropbear SSH 2013.62[0m
|
`- [info] default key exchange from OpenSSH 7.4 to 8.9
|
||||||
[0;32m `- [info] default key exchange from OpenSSH 6.5 to 7.3[0m
|
[0;33m(kex) curve25519-sha256@libssh.org -- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
|
`- [info] available since OpenSSH 6.4, Dropbear SSH 2013.62
|
||||||
|
`- [info] default key exchange from OpenSSH 6.5 to 7.3
|
||||||
[0;31m(kex) ecdh-sha2-nistp256 -- [fail] using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency[0m
|
[0;31m(kex) ecdh-sha2-nistp256 -- [fail] using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
||||||
[0;31m(kex) ecdh-sha2-nistp384 -- [fail] using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency[0m
|
[0;31m(kex) ecdh-sha2-nistp384 -- [fail] using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
||||||
[0;31m(kex) ecdh-sha2-nistp521 -- [fail] using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency[0m
|
[0;31m(kex) ecdh-sha2-nistp521 -- [fail] using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
||||||
[0;32m(kex) diffie-hellman-group-exchange-sha256 (4096-bit) -- [info] available since OpenSSH 4.4[0m
|
[0;33m(kex) diffie-hellman-group-exchange-sha256 (4096-bit) -- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
[0;32m `- [info] OpenSSH's GEX fallback mechanism was triggered during testing. Very old SSH clients will still be able to create connections using a 2048-bit modulus, though modern clients will use 4096. This can only be disabled by recompiling the code (see https://github.com/openssh/openssh-portable/blob/V_9_4/dh.c#L477).[0m
|
`- [info] available since OpenSSH 4.4
|
||||||
[0;32m(kex) diffie-hellman-group16-sha512 -- [info] available since OpenSSH 7.3, Dropbear SSH 2016.73[0m
|
`- [info] OpenSSH's GEX fallback mechanism was triggered during testing. Very old SSH clients will still be able to create connections using a 2048-bit modulus, though modern clients will use 4096. This can only be disabled by recompiling the code (see https://github.com/openssh/openssh-portable/blob/V_9_4/dh.c#L477).
|
||||||
[0;32m(kex) diffie-hellman-group18-sha512 -- [info] available since OpenSSH 7.3[0m
|
[0;33m(kex) diffie-hellman-group16-sha512 -- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
|
`- [info] available since OpenSSH 7.3, Dropbear SSH 2016.73
|
||||||
|
[0;33m(kex) diffie-hellman-group18-sha512 -- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
|
`- [info] available since OpenSSH 7.3
|
||||||
[0;33m(kex) diffie-hellman-group14-sha256 -- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
[0;33m(kex) diffie-hellman-group14-sha256 -- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 7.3, Dropbear SSH 2016.73
|
`- [info] available since OpenSSH 7.3, Dropbear SSH 2016.73
|
||||||
[0;31m(kex) diffie-hellman-group14-sha1 -- [fail] using broken SHA-1 hash algorithm[0m
|
[0;31m(kex) diffie-hellman-group14-sha1 -- [fail] using broken SHA-1 hash algorithm[0m
|
||||||
[0;33m `- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
[0;33m `- [warn] 2048-bit modulus only provides 112-bits of symmetric strength[0m
|
||||||
|
[0;33m `- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
`- [info] available since OpenSSH 3.9, Dropbear SSH 0.53
|
`- [info] available since OpenSSH 3.9, Dropbear SSH 0.53
|
||||||
|
|
||||||
[0;36m# host-key algorithms[0m
|
[0;36m# host-key algorithms[0m
|
||||||
@@ -72,8 +82,13 @@
|
|||||||
[0;31m(rec) -hmac-sha1-etm@openssh.com -- mac algorithm to remove [0m
|
[0;31m(rec) -hmac-sha1-etm@openssh.com -- mac 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;33m(rec) !diffie-hellman-group-exchange-sha256 -- kex algorithm to change (increase modulus size to 3072 bits or larger) [0m
|
||||||
[0;33m(rec) -chacha20-poly1305@openssh.com -- enc algorithm to remove [0m
|
[0;33m(rec) -chacha20-poly1305@openssh.com -- enc algorithm to remove [0m
|
||||||
|
[0;33m(rec) -curve25519-sha256 -- kex algorithm to remove [0m
|
||||||
|
[0;33m(rec) -curve25519-sha256@libssh.org -- kex algorithm to remove [0m
|
||||||
[0;33m(rec) -diffie-hellman-group14-sha256 -- kex algorithm to remove [0m
|
[0;33m(rec) -diffie-hellman-group14-sha256 -- kex algorithm to remove [0m
|
||||||
|
[0;33m(rec) -diffie-hellman-group16-sha512 -- kex algorithm to remove [0m
|
||||||
|
[0;33m(rec) -diffie-hellman-group18-sha512 -- kex algorithm to remove [0m
|
||||||
[0;33m(rec) -hmac-sha2-256 -- mac algorithm to remove [0m
|
[0;33m(rec) -hmac-sha2-256 -- mac algorithm to remove [0m
|
||||||
[0;33m(rec) -hmac-sha2-512 -- mac algorithm to remove [0m
|
[0;33m(rec) -hmac-sha2-512 -- mac algorithm to remove [0m
|
||||||
[0;33m(rec) -umac-128@openssh.com -- mac algorithm to remove [0m
|
[0;33m(rec) -umac-128@openssh.com -- mac algorithm to remove [0m
|
||||||
|
|||||||
@@ -84,6 +84,9 @@
|
|||||||
"info": [
|
"info": [
|
||||||
"default key exchange from OpenSSH 7.4 to 8.9",
|
"default key exchange from OpenSSH 7.4 to 8.9",
|
||||||
"available since OpenSSH 7.4, Dropbear SSH 2018.76"
|
"available since OpenSSH 7.4, Dropbear SSH 2018.76"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -93,6 +96,9 @@
|
|||||||
"info": [
|
"info": [
|
||||||
"default key exchange from OpenSSH 6.5 to 7.3",
|
"default key exchange from OpenSSH 6.5 to 7.3",
|
||||||
"available since OpenSSH 6.4, Dropbear SSH 2013.62"
|
"available since OpenSSH 6.4, Dropbear SSH 2013.62"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -103,6 +109,9 @@
|
|||||||
"info": [
|
"info": [
|
||||||
"OpenSSH's GEX fallback mechanism was triggered during testing. Very old SSH clients will still be able to create connections using a 2048-bit modulus, though modern clients will use 4096. This can only be disabled by recompiling the code (see https://github.com/openssh/openssh-portable/blob/V_9_4/dh.c#L477).",
|
"OpenSSH's GEX fallback mechanism was triggered during testing. Very old SSH clients will still be able to create connections using a 2048-bit modulus, though modern clients will use 4096. This can only be disabled by recompiling the code (see https://github.com/openssh/openssh-portable/blob/V_9_4/dh.c#L477).",
|
||||||
"available since OpenSSH 4.4"
|
"available since OpenSSH 4.4"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -146,16 +155,6 @@
|
|||||||
"recommendations": {
|
"recommendations": {
|
||||||
"informational": {
|
"informational": {
|
||||||
"add": {
|
"add": {
|
||||||
"kex": [
|
|
||||||
{
|
|
||||||
"name": "diffie-hellman-group16-sha512",
|
|
||||||
"notes": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "diffie-hellman-group18-sha512",
|
|
||||||
"notes": ""
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"key": [
|
"key": [
|
||||||
{
|
{
|
||||||
"name": "rsa-sha2-256",
|
"name": "rsa-sha2-256",
|
||||||
@@ -169,12 +168,30 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"warning": {
|
"warning": {
|
||||||
|
"chg": {
|
||||||
|
"kex": [
|
||||||
|
{
|
||||||
|
"name": "diffie-hellman-group-exchange-sha256",
|
||||||
|
"notes": "increase modulus size to 3072 bits or larger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"del": {
|
"del": {
|
||||||
"enc": [
|
"enc": [
|
||||||
{
|
{
|
||||||
"name": "chacha20-poly1305@openssh.com",
|
"name": "chacha20-poly1305@openssh.com",
|
||||||
"notes": ""
|
"notes": ""
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"kex": [
|
||||||
|
{
|
||||||
|
"name": "curve25519-sha256",
|
||||||
|
"notes": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "curve25519-sha256@libssh.org",
|
||||||
|
"notes": ""
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,15 @@
|
|||||||
[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, Dropbear SSH 2018.76[0m
|
[0;33m(kex) curve25519-sha256 -- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
[0;32m `- [info] default key exchange from OpenSSH 7.4 to 8.9[0m
|
`- [info] available since OpenSSH 7.4, Dropbear SSH 2018.76
|
||||||
[0;32m(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.4, Dropbear SSH 2013.62[0m
|
`- [info] default key exchange from OpenSSH 7.4 to 8.9
|
||||||
[0;32m `- [info] default key exchange from OpenSSH 6.5 to 7.3[0m
|
[0;33m(kex) curve25519-sha256@libssh.org -- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
[0;32m(kex) diffie-hellman-group-exchange-sha256 (4096-bit) -- [info] available since OpenSSH 4.4[0m
|
`- [info] available since OpenSSH 6.4, Dropbear SSH 2013.62
|
||||||
[0;32m `- [info] OpenSSH's GEX fallback mechanism was triggered during testing. Very old SSH clients will still be able to create connections using a 2048-bit modulus, though modern clients will use 4096. This can only be disabled by recompiling the code (see https://github.com/openssh/openssh-portable/blob/V_9_4/dh.c#L477).[0m
|
`- [info] default key exchange from OpenSSH 6.5 to 7.3
|
||||||
|
[0;33m(kex) diffie-hellman-group-exchange-sha256 (4096-bit) -- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
|
`- [info] available since OpenSSH 4.4
|
||||||
|
`- [info] OpenSSH's GEX fallback mechanism was triggered during testing. Very old SSH clients will still be able to create connections using a 2048-bit modulus, though modern clients will use 4096. This can only be disabled by recompiling the code (see https://github.com/openssh/openssh-portable/blob/V_9_4/dh.c#L477).
|
||||||
|
|
||||||
[0;36m# host-key algorithms[0m
|
[0;36m# host-key algorithms[0m
|
||||||
[0;32m(key) ssh-ed25519 -- [info] available since OpenSSH 6.5, Dropbear SSH 2020.79[0m
|
[0;32m(key) ssh-ed25519 -- [info] available since OpenSSH 6.5, Dropbear SSH 2020.79[0m
|
||||||
@@ -34,11 +37,12 @@
|
|||||||
[0;32m(fin) ssh-ed25519: SHA256:UrnXIVH+7dlw8UqYocl48yUEcKrthGDQG2CPCgp7MxU[0m
|
[0;32m(fin) ssh-ed25519: SHA256:UrnXIVH+7dlw8UqYocl48yUEcKrthGDQG2CPCgp7MxU[0m
|
||||||
|
|
||||||
[0;36m# algorithm recommendations (for OpenSSH 8.0)[0m
|
[0;36m# algorithm recommendations (for OpenSSH 8.0)[0m
|
||||||
[0;32m(rec) +diffie-hellman-group16-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;33m(rec) !diffie-hellman-group-exchange-sha256 -- kex algorithm to change (increase modulus size to 3072 bits or larger) [0m
|
||||||
[0;33m(rec) -chacha20-poly1305@openssh.com -- enc algorithm to remove [0m
|
[0;33m(rec) -chacha20-poly1305@openssh.com -- enc algorithm to remove [0m
|
||||||
|
[0;33m(rec) -curve25519-sha256 -- kex algorithm to remove [0m
|
||||||
|
[0;33m(rec) -curve25519-sha256@libssh.org -- kex algorithm to remove [0m
|
||||||
|
|
||||||
[0;36m# additional info[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
|
[0;33m(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>[0m
|
||||||
|
|||||||
@@ -43,6 +43,9 @@
|
|||||||
"info": [
|
"info": [
|
||||||
"default key exchange from OpenSSH 7.4 to 8.9",
|
"default key exchange from OpenSSH 7.4 to 8.9",
|
||||||
"available since OpenSSH 7.4, Dropbear SSH 2018.76"
|
"available since OpenSSH 7.4, Dropbear SSH 2018.76"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -52,6 +55,9 @@
|
|||||||
"info": [
|
"info": [
|
||||||
"default key exchange from OpenSSH 6.5 to 7.3",
|
"default key exchange from OpenSSH 6.5 to 7.3",
|
||||||
"available since OpenSSH 6.4, Dropbear SSH 2013.62"
|
"available since OpenSSH 6.4, Dropbear SSH 2013.62"
|
||||||
|
],
|
||||||
|
"warn": [
|
||||||
|
"does not provide protection against post-quantum attacks"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,10 +4,12 @@
|
|||||||
[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, Dropbear SSH 2018.76[0m
|
[0;33m(kex) curve25519-sha256 -- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
[0;32m `- [info] default key exchange from OpenSSH 7.4 to 8.9[0m
|
`- [info] available since OpenSSH 7.4, Dropbear SSH 2018.76
|
||||||
[0;32m(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.4, Dropbear SSH 2013.62[0m
|
`- [info] default key exchange from OpenSSH 7.4 to 8.9
|
||||||
[0;32m `- [info] default key exchange from OpenSSH 6.5 to 7.3[0m
|
[0;33m(kex) curve25519-sha256@libssh.org -- [warn] does not provide protection against post-quantum attacks[0m
|
||||||
|
`- [info] available since OpenSSH 6.4, Dropbear SSH 2013.62
|
||||||
|
`- [info] default key exchange from OpenSSH 6.5 to 7.3
|
||||||
[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
|
||||||
`- [info] the sntrup4591761 algorithm was withdrawn, as it may not provide strong post-quantum security
|
`- [info] the sntrup4591761 algorithm was withdrawn, as it may not provide strong post-quantum security
|
||||||
|
|||||||
+3
-14
@@ -8,7 +8,6 @@ class TestAuditConf:
|
|||||||
def init(self, ssh_audit):
|
def init(self, ssh_audit):
|
||||||
self.AuditConf = ssh_audit.AuditConf
|
self.AuditConf = ssh_audit.AuditConf
|
||||||
self.OutputBuffer = ssh_audit.OutputBuffer()
|
self.OutputBuffer = ssh_audit.OutputBuffer()
|
||||||
self.usage = ssh_audit.usage
|
|
||||||
self.process_commandline = process_commandline
|
self.process_commandline = process_commandline
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -16,8 +15,6 @@ class TestAuditConf:
|
|||||||
options = {
|
options = {
|
||||||
'host': '',
|
'host': '',
|
||||||
'port': 22,
|
'port': 22,
|
||||||
'ssh1': True,
|
|
||||||
'ssh2': True,
|
|
||||||
'batch': False,
|
'batch': False,
|
||||||
'colors': True,
|
'colors': True,
|
||||||
'verbose': False,
|
'verbose': False,
|
||||||
@@ -29,8 +26,6 @@ class TestAuditConf:
|
|||||||
options[k] = v
|
options[k] = v
|
||||||
assert conf.host == options['host']
|
assert conf.host == options['host']
|
||||||
assert conf.port == options['port']
|
assert conf.port == options['port']
|
||||||
assert conf.ssh1 is options['ssh1']
|
|
||||||
assert conf.ssh2 is options['ssh2']
|
|
||||||
assert conf.batch is options['batch']
|
assert conf.batch is options['batch']
|
||||||
assert conf.colors is options['colors']
|
assert conf.colors is options['colors']
|
||||||
assert conf.verbose is options['verbose']
|
assert conf.verbose is options['verbose']
|
||||||
@@ -44,7 +39,7 @@ class TestAuditConf:
|
|||||||
|
|
||||||
def test_audit_conf_booleans(self):
|
def test_audit_conf_booleans(self):
|
||||||
conf = self.AuditConf()
|
conf = self.AuditConf()
|
||||||
for p in ['ssh1', 'ssh2', 'batch', 'colors', 'verbose']:
|
for p in ['batch', 'colors', 'verbose']:
|
||||||
for v in [True, 1]:
|
for v in [True, 1]:
|
||||||
setattr(conf, p, v)
|
setattr(conf, p, v)
|
||||||
assert getattr(conf, p) is True
|
assert getattr(conf, p) is True
|
||||||
@@ -107,7 +102,7 @@ class TestAuditConf:
|
|||||||
|
|
||||||
def test_audit_conf_process_commandline(self):
|
def test_audit_conf_process_commandline(self):
|
||||||
# pylint: disable=too-many-statements
|
# pylint: disable=too-many-statements
|
||||||
c = lambda x: self.process_commandline(self.OutputBuffer, x.split(), self.usage) # noqa
|
c = lambda x: self.process_commandline(self.OutputBuffer, x.split()) # noqa
|
||||||
with pytest.raises(SystemExit):
|
with pytest.raises(SystemExit):
|
||||||
conf = c('')
|
conf = c('')
|
||||||
with pytest.raises(SystemExit):
|
with pytest.raises(SystemExit):
|
||||||
@@ -148,12 +143,6 @@ class TestAuditConf:
|
|||||||
conf = c('localhost:99999')
|
conf = c('localhost:99999')
|
||||||
with pytest.raises(SystemExit):
|
with pytest.raises(SystemExit):
|
||||||
conf = c('-p 99999 localhost')
|
conf = c('-p 99999 localhost')
|
||||||
conf = c('-1 localhost')
|
|
||||||
self._test_conf(conf, host='localhost', ssh1=True, ssh2=False)
|
|
||||||
conf = c('-2 localhost')
|
|
||||||
self._test_conf(conf, host='localhost', ssh1=False, ssh2=True)
|
|
||||||
conf = c('-12 localhost')
|
|
||||||
self._test_conf(conf, host='localhost', ssh1=True, ssh2=True)
|
|
||||||
conf = c('-4 localhost')
|
conf = c('-4 localhost')
|
||||||
self._test_conf(conf, host='localhost', ipv4=True, ipv6=False, ipvo=(4,))
|
self._test_conf(conf, host='localhost', ipv4=True, ipv6=False, ipvo=(4,))
|
||||||
conf = c('-6 localhost')
|
conf = c('-6 localhost')
|
||||||
@@ -163,7 +152,7 @@ class TestAuditConf:
|
|||||||
conf = c('-64 localhost')
|
conf = c('-64 localhost')
|
||||||
self._test_conf(conf, host='localhost', ipv4=True, ipv6=True, ipvo=(6, 4))
|
self._test_conf(conf, host='localhost', ipv4=True, ipv6=True, ipvo=(6, 4))
|
||||||
conf = c('-b localhost')
|
conf = c('-b localhost')
|
||||||
self._test_conf(conf, host='localhost', batch=True, verbose=True)
|
self._test_conf(conf, host='localhost', batch=True)
|
||||||
conf = c('-n localhost')
|
conf = c('-n localhost')
|
||||||
self._test_conf(conf, host='localhost', colors=False)
|
self._test_conf(conf, host='localhost', colors=False)
|
||||||
conf = c('-v localhost')
|
conf = c('-v localhost')
|
||||||
|
|||||||
@@ -166,7 +166,6 @@ class TestErrors:
|
|||||||
vsocket.rdata.append(b'SSH-1.3-ssh-audit-test\r\n')
|
vsocket.rdata.append(b'SSH-1.3-ssh-audit-test\r\n')
|
||||||
vsocket.rdata.append(b'Protocol major versions differ.\n')
|
vsocket.rdata.append(b'Protocol major versions differ.\n')
|
||||||
conf = self._conf()
|
conf = self._conf()
|
||||||
conf.ssh1, conf.ssh2 = True, False
|
|
||||||
lines = self._audit(output_spy, conf)
|
lines = self._audit(output_spy, conf)
|
||||||
assert len(lines) == 4
|
assert len(lines) == 4
|
||||||
assert 'error reading packet' in lines[-1]
|
assert 'error reading packet' in lines[-1]
|
||||||
|
|||||||
@@ -1,174 +0,0 @@
|
|||||||
import struct
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from ssh_audit.auditconf import AuditConf
|
|
||||||
from ssh_audit.fingerprint import Fingerprint
|
|
||||||
from ssh_audit.outputbuffer import OutputBuffer
|
|
||||||
from ssh_audit.protocol import Protocol
|
|
||||||
from ssh_audit.readbuf import ReadBuf
|
|
||||||
from ssh_audit.ssh1 import SSH1
|
|
||||||
from ssh_audit.ssh1_publickeymessage import SSH1_PublicKeyMessage
|
|
||||||
from ssh_audit.ssh_audit import audit
|
|
||||||
from ssh_audit.writebuf import WriteBuf
|
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=line-too-long,attribute-defined-outside-init
|
|
||||||
class TestSSH1:
|
|
||||||
@pytest.fixture(autouse=True)
|
|
||||||
def init(self, ssh_audit):
|
|
||||||
self.OutputBuffer = OutputBuffer
|
|
||||||
self.protocol = Protocol
|
|
||||||
self.ssh1 = SSH1
|
|
||||||
self.PublicKeyMessage = SSH1_PublicKeyMessage
|
|
||||||
self.rbuf = ReadBuf
|
|
||||||
self.wbuf = WriteBuf
|
|
||||||
self.audit = audit
|
|
||||||
self.AuditConf = AuditConf
|
|
||||||
self.fingerprint = Fingerprint
|
|
||||||
|
|
||||||
def _conf(self):
|
|
||||||
conf = self.AuditConf('localhost', 22)
|
|
||||||
conf.colors = False
|
|
||||||
conf.batch = True
|
|
||||||
conf.verbose = True
|
|
||||||
conf.ssh1 = True
|
|
||||||
conf.ssh2 = False
|
|
||||||
conf.skip_rate_test = True
|
|
||||||
return conf
|
|
||||||
|
|
||||||
def _create_ssh1_packet(self, payload, valid_crc=True):
|
|
||||||
padding = -(len(payload) + 4) % 8
|
|
||||||
plen = len(payload) + 4
|
|
||||||
pad_bytes = b'\x00' * padding
|
|
||||||
cksum = self.ssh1.crc32(pad_bytes + payload) if valid_crc else 0
|
|
||||||
data = struct.pack('>I', plen) + pad_bytes + payload + struct.pack('>I', cksum)
|
|
||||||
return data
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _server_key(cls):
|
|
||||||
return (1024, 0x10001, 0xee6552da432e0ac2c422df1a51287507748bfe3b5e3e4fa989a8f49fdc163a17754939ef18ef8a667ea3b71036a151fcd7f5e01ceef1e4439864baf3ac569047582c69d6c128212e0980dcb3168f00d371004039983f6033cd785b8b8f85096c7d9405cbfdc664e27c966356a6b4eb6ee20ad43414b50de18b22829c1880b551)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _host_key(cls):
|
|
||||||
return (2048, 0x10001, 0xdfa20cd2a530ccc8c870aa60d9feb3b35deeab81c3215a96557abbd683d21f4600f38e475d87100da9a4404220eeb3bb5584e5a2b5b48ffda58530ea19104a32577d7459d91e76aa711b241050f4cc6d5327ccce254f371acad3be56d46eb5919b73f20dbdb1177b700f00891c5bf4ed128bb90ed541b778288285bcfa28432ab5cbcb8321b6e24760e998e0daa519f093a631e44276d7dd252ce0c08c75e2ab28a7349ead779f97d0f20a6d413bf3623cd216dc35375f6366690bcc41e3b2d5465840ec7ee0dc7e3f1c101d674a0c7dbccbc3942788b111396add2f8153b46a0e4b50d66e57ee92958f1c860dd97cc0e40e32febff915343ed53573142bdf4b)
|
|
||||||
|
|
||||||
def _pkm_payload(self):
|
|
||||||
w = self.wbuf()
|
|
||||||
w.write(b'\x88\x99\xaa\xbb\xcc\xdd\xee\xff')
|
|
||||||
b, e, m = self._server_key()
|
|
||||||
w.write_int(b).write_mpint1(e).write_mpint1(m)
|
|
||||||
b, e, m = self._host_key()
|
|
||||||
w.write_int(b).write_mpint1(e).write_mpint1(m)
|
|
||||||
w.write_int(2)
|
|
||||||
w.write_int(72)
|
|
||||||
w.write_int(36)
|
|
||||||
return w.write_flush()
|
|
||||||
|
|
||||||
def test_crc32(self):
|
|
||||||
assert self.ssh1.crc32(b'') == 0x00
|
|
||||||
assert self.ssh1.crc32(b'The quick brown fox jumps over the lazy dog') == 0xb9c60808
|
|
||||||
|
|
||||||
def test_fingerprint(self):
|
|
||||||
# pylint: disable=protected-access
|
|
||||||
b, e, m = self._host_key()
|
|
||||||
fpd = self.wbuf._create_mpint(m, False)
|
|
||||||
fpd += self.wbuf._create_mpint(e, False)
|
|
||||||
fp = self.fingerprint(fpd)
|
|
||||||
assert b == 2048
|
|
||||||
assert fp.md5 == 'MD5:9d:26:f8:39:fc:20:9d:9b:ca:cc:4a:0f:e1:93:f5:96'
|
|
||||||
assert fp.sha256 == 'SHA256:vZdx3mhzbvVJmn08t/ruv8WDhJ9jfKYsCTuSzot+QIs'
|
|
||||||
|
|
||||||
def _assert_pkm_keys(self, pkm, skey, hkey):
|
|
||||||
b, e, m = skey
|
|
||||||
assert pkm.server_key_bits == b
|
|
||||||
assert pkm.server_key_public_exponent == e
|
|
||||||
assert pkm.server_key_public_modulus == m
|
|
||||||
b, e, m = hkey
|
|
||||||
assert pkm.host_key_bits == b
|
|
||||||
assert pkm.host_key_public_exponent == e
|
|
||||||
assert pkm.host_key_public_modulus == m
|
|
||||||
|
|
||||||
def _assert_pkm_fields(self, pkm, skey, hkey):
|
|
||||||
assert pkm is not None
|
|
||||||
assert pkm.cookie == b'\x88\x99\xaa\xbb\xcc\xdd\xee\xff'
|
|
||||||
self._assert_pkm_keys(pkm, skey, hkey)
|
|
||||||
assert pkm.protocol_flags == 2
|
|
||||||
assert pkm.supported_ciphers_mask == 72
|
|
||||||
assert pkm.supported_ciphers == ['3des', 'blowfish']
|
|
||||||
assert pkm.supported_authentications_mask == 36
|
|
||||||
assert pkm.supported_authentications == ['rsa', 'tis']
|
|
||||||
fp = self.fingerprint(pkm.host_key_fingerprint_data)
|
|
||||||
assert fp.md5 == 'MD5:9d:26:f8:39:fc:20:9d:9b:ca:cc:4a:0f:e1:93:f5:96'
|
|
||||||
assert fp.sha256 == 'SHA256:vZdx3mhzbvVJmn08t/ruv8WDhJ9jfKYsCTuSzot+QIs'
|
|
||||||
|
|
||||||
def test_pkm_init(self):
|
|
||||||
cookie = b'\x88\x99\xaa\xbb\xcc\xdd\xee\xff'
|
|
||||||
pflags, cmask, amask = 2, 72, 36
|
|
||||||
skey, hkey = self._server_key(), self._host_key()
|
|
||||||
pkm = self.PublicKeyMessage(cookie, skey, hkey, pflags, cmask, amask)
|
|
||||||
self._assert_pkm_fields(pkm, skey, hkey)
|
|
||||||
for skey2 in ([], [0], [0, 1], [0, 1, 2, 3]):
|
|
||||||
with pytest.raises(ValueError):
|
|
||||||
pkm = self.PublicKeyMessage(cookie, skey2, hkey, pflags, cmask, amask)
|
|
||||||
for hkey2 in ([], [0], [0, 1], [0, 1, 2, 3]):
|
|
||||||
with pytest.raises(ValueError):
|
|
||||||
print(hkey2)
|
|
||||||
pkm = self.PublicKeyMessage(cookie, skey, hkey2, pflags, cmask, amask)
|
|
||||||
|
|
||||||
def test_pkm_read(self):
|
|
||||||
pkm = self.PublicKeyMessage.parse(self._pkm_payload())
|
|
||||||
self._assert_pkm_fields(pkm, self._server_key(), self._host_key())
|
|
||||||
|
|
||||||
def test_pkm_payload(self):
|
|
||||||
cookie = b'\x88\x99\xaa\xbb\xcc\xdd\xee\xff'
|
|
||||||
skey, hkey = self._server_key(), self._host_key()
|
|
||||||
pflags, cmask, amask = 2, 72, 36
|
|
||||||
pkm1 = self.PublicKeyMessage(cookie, skey, hkey, pflags, cmask, amask)
|
|
||||||
pkm2 = self.PublicKeyMessage.parse(self._pkm_payload())
|
|
||||||
assert pkm1.payload == pkm2.payload
|
|
||||||
|
|
||||||
def test_ssh1_server_simple(self, output_spy, virtual_socket):
|
|
||||||
vsocket = virtual_socket
|
|
||||||
w = self.wbuf()
|
|
||||||
w.write_byte(self.protocol.SMSG_PUBLIC_KEY)
|
|
||||||
w.write(self._pkm_payload())
|
|
||||||
vsocket.rdata.append(b'SSH-1.5-OpenSSH_7.2 ssh-audit-test\r\n')
|
|
||||||
vsocket.rdata.append(self._create_ssh1_packet(w.write_flush()))
|
|
||||||
output_spy.begin()
|
|
||||||
out = self.OutputBuffer()
|
|
||||||
self.audit(out, self._conf())
|
|
||||||
out.write()
|
|
||||||
lines = output_spy.flush()
|
|
||||||
assert len(lines) == 13
|
|
||||||
|
|
||||||
def test_ssh1_server_invalid_first_packet(self, output_spy, virtual_socket):
|
|
||||||
vsocket = virtual_socket
|
|
||||||
w = self.wbuf()
|
|
||||||
w.write_byte(self.protocol.SMSG_PUBLIC_KEY + 1)
|
|
||||||
w.write(self._pkm_payload())
|
|
||||||
vsocket.rdata.append(b'SSH-1.5-OpenSSH_7.2 ssh-audit-test\r\n')
|
|
||||||
vsocket.rdata.append(self._create_ssh1_packet(w.write_flush()))
|
|
||||||
output_spy.begin()
|
|
||||||
out = self.OutputBuffer()
|
|
||||||
ret = self.audit(out, self._conf())
|
|
||||||
out.write()
|
|
||||||
assert ret != 0
|
|
||||||
lines = output_spy.flush()
|
|
||||||
assert len(lines) == 6
|
|
||||||
assert 'unknown message' in lines[-1]
|
|
||||||
|
|
||||||
def test_ssh1_server_invalid_checksum(self, output_spy, virtual_socket):
|
|
||||||
vsocket = virtual_socket
|
|
||||||
w = self.wbuf()
|
|
||||||
w.write_byte(self.protocol.SMSG_PUBLIC_KEY + 1)
|
|
||||||
w.write(self._pkm_payload())
|
|
||||||
vsocket.rdata.append(b'SSH-1.5-OpenSSH_7.2 ssh-audit-test\r\n')
|
|
||||||
vsocket.rdata.append(self._create_ssh1_packet(w.write_flush(), False))
|
|
||||||
output_spy.begin()
|
|
||||||
out = self.OutputBuffer()
|
|
||||||
with pytest.raises(SystemExit):
|
|
||||||
self.audit(out, self._conf())
|
|
||||||
out.write()
|
|
||||||
lines = output_spy.flush()
|
|
||||||
assert len(lines) == 3
|
|
||||||
assert ('checksum' in lines[0]) or ('checksum' in lines[1]) or ('checksum' in lines[2])
|
|
||||||
+1
-1
@@ -165,7 +165,7 @@ class TestSSH2:
|
|||||||
self.audit(out, self._conf())
|
self.audit(out, self._conf())
|
||||||
out.write()
|
out.write()
|
||||||
lines = output_spy.flush()
|
lines = output_spy.flush()
|
||||||
assert len(lines) == 78
|
assert len(lines) == 74
|
||||||
|
|
||||||
def test_ssh2_server_invalid_first_packet(self, output_spy, virtual_socket):
|
def test_ssh2_server_invalid_first_packet(self, output_spy, virtual_socket):
|
||||||
vsocket = virtual_socket
|
vsocket = virtual_socket
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ class Test_SSH2_KexDB:
|
|||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def init(self):
|
def init(self):
|
||||||
self.db = SSH2_KexDB.get_db()
|
self.db = SSH2_KexDB.get_db()
|
||||||
|
self.pq_warning = SSH2_KexDB.WARN_NOT_PQ_SAFE
|
||||||
|
|
||||||
def test_ssh2_kexdb(self):
|
def test_ssh2_kexdb(self):
|
||||||
'''Ensures that the SSH2_KexDB.ALGORITHMS dictionary is in the right format.'''
|
'''Ensures that the SSH2_KexDB.ALGORITHMS dictionary is in the right format.'''
|
||||||
@@ -33,3 +34,40 @@ class Test_SSH2_KexDB:
|
|||||||
# The first entry denotes the versions when this algorithm was added to OpenSSH, Dropbear, and/or libssh, followed by when it was deprecated, and finally when it was removed. Hence it must have between 0 and 3 entries.
|
# The first entry denotes the versions when this algorithm was added to OpenSSH, Dropbear, and/or libssh, followed by when it was deprecated, and finally when it was removed. Hence it must have between 0 and 3 entries.
|
||||||
added_entry = alg_data[0]
|
added_entry = alg_data[0]
|
||||||
assert 0 <= len(added_entry) <= 3
|
assert 0 <= len(added_entry) <= 3
|
||||||
|
|
||||||
|
|
||||||
|
def test_kex_pq_unsafe(self):
|
||||||
|
'''Ensures that all key exchange algorithms are marked as post-quantum unsafe, unless they appear in a whitelist.'''
|
||||||
|
|
||||||
|
# These algorithms include protections against quantum attacks.
|
||||||
|
kex_pq_safe = [
|
||||||
|
"ecdh-nistp256-kyber-512r3-sha256-d00@openquantumsafe.org",
|
||||||
|
"ecdh-nistp384-kyber-768r3-sha384-d00@openquantumsafe.org",
|
||||||
|
"ecdh-nistp521-kyber-1024r3-sha512-d00@openquantumsafe.org",
|
||||||
|
"ext-info-c",
|
||||||
|
"ext-info-s",
|
||||||
|
"kex-strict-c-v00@openssh.com",
|
||||||
|
"kex-strict-s-v00@openssh.com",
|
||||||
|
"mlkem768x25519-sha256",
|
||||||
|
"sntrup4591761x25519-sha512@tinyssh.org",
|
||||||
|
"sntrup761x25519-sha512@openssh.com",
|
||||||
|
"sntrup761x25519-sha512",
|
||||||
|
"x25519-kyber-512r3-sha256-d00@amazon.com",
|
||||||
|
"x25519-kyber512-sha512@aws.amazon.com",
|
||||||
|
"mlkem768nistp256-sha256", # PQ safe, but has a conventional back-door.
|
||||||
|
"mlkem1024nistp384-sha384" # PQ safe, but has a conventional back-door.
|
||||||
|
]
|
||||||
|
|
||||||
|
failures = []
|
||||||
|
for kex_name in self.db['kex']:
|
||||||
|
|
||||||
|
# Skip key exchanges that are PQ safe.
|
||||||
|
if kex_name in kex_pq_safe:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Ensure all other kex exchanges have the proper PQ unsafe flag set in their warnings list.
|
||||||
|
alg_data = self.db['kex'][kex_name]
|
||||||
|
if len(alg_data) < 3 or self.pq_warning not in alg_data[2]:
|
||||||
|
failures.append(kex_name)
|
||||||
|
|
||||||
|
assert failures == []
|
||||||
|
|||||||
Reference in New Issue
Block a user