mirror of
https://github.com/jtesta/ssh-audit.git
synced 2025-07-06 05:57:50 -05:00
Compare commits
60 Commits
Author | SHA1 | Date | |
---|---|---|---|
68cf05d0ff | |||
2d9ddabcad | |||
986f83653d | |||
3c459f1428 | |||
46b89fff2e | |||
81718d1948 | |||
8124c8e443 | |||
b9f569fdf8 | |||
9126ae7d9c | |||
d2f1a295a1 | |||
8190fe59d0 | |||
d7f8bf3e6d | |||
3d403b1d70 | |||
9fae870260 | |||
20873db596 | |||
3c31934ac7 | |||
5bd925ffc6 | |||
7b3402b207 | |||
b2f46eb71a | |||
ab41ca1023 | |||
b70fb0bc4c | |||
db5104ecb8 | |||
15078aaea9 | |||
f0874af4cd | |||
064b55e0c2 | |||
a4f508374a | |||
6f39407a8c | |||
cb0f6b63d7 | |||
3313046714 | |||
8ee0deade1 | |||
699739d42a | |||
a958fd1fec | |||
c33f419224 | |||
6ee4899b4f | |||
20fbb706b0 | |||
73b669b49d | |||
f326d58068 | |||
b72f6a420f | |||
fe65b5df8a | |||
44393c56b3 | |||
164356e776 | |||
c8e075ad13 | |||
eebeac99a0 | |||
dd91c2a41a | |||
bef8c6c0f7 | |||
75dbc03a77 | |||
c9412cbb88 | |||
a0f99942a2 | |||
c259a83782 | |||
8e972c5e94 | |||
46eb970376 | |||
965bcb6b18 | |||
ba8e8a7e68 | |||
bad2c9cd8e | |||
69e1e121fd | |||
848052df68 | |||
d62e4cd80c | |||
2809ff464a | |||
02ab487232 | |||
d62acd688e |
4
.github/workflows/tox.yaml
vendored
4
.github/workflows/tox.yaml
vendored
@ -7,7 +7,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.7, 3.8, 3.9, "3.10", 3.11]
|
||||
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@ -18,7 +18,7 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -U codecov coveralls flake8 mypy pylint pytest tox vulture
|
||||
pip install -U codecov coveralls flake8 mypy pylint pytest tox
|
||||
- name: Run Tox
|
||||
run: |
|
||||
tox
|
||||
|
21
Dockerfile
21
Dockerfile
@ -1,16 +1,19 @@
|
||||
FROM python:3-slim
|
||||
# syntax=docker/dockerfile:latest
|
||||
FROM scratch AS files
|
||||
|
||||
WORKDIR /
|
||||
# Copy ssh-audit code to temporary container
|
||||
COPY ssh-audit.py /
|
||||
COPY src/ /
|
||||
|
||||
FROM python:3-alpine AS runtime
|
||||
|
||||
# Update the image to remediate any vulnerabilities.
|
||||
RUN apt clean && apt update && apt -y dist-upgrade && apt clean
|
||||
RUN apk upgrade -U --no-cache -a -l && \
|
||||
# Remove suid & sgid bits from all files.
|
||||
find / -xdev -perm /6000 -exec chmod ug-s {} \; 2> /dev/null || true
|
||||
|
||||
# Remove suid & sgid bits from all files.
|
||||
RUN find / -xdev -perm /6000 -exec chmod ug-s {} \; 2> /dev/null || true
|
||||
|
||||
# Copy the ssh-audit code.
|
||||
COPY ssh-audit.py .
|
||||
COPY src/ .
|
||||
# Copy the ssh-audit code from files container.
|
||||
COPY --from=files / /
|
||||
|
||||
# Allow listening on 2222/tcp for client auditing.
|
||||
EXPOSE 2222
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (C) 2017-2023 Joe Testa (jtesta@positronsecurity.com)
|
||||
Copyright (C) 2017-2024 Joe Testa (jtesta@positronsecurity.com)
|
||||
Copyright (C) 2017 Andris Raugulis (moo@arthepsy.eu)
|
||||
|
||||
|
||||
|
@ -4,17 +4,22 @@ ifeq ($(VERSION),)
|
||||
endif
|
||||
|
||||
all:
|
||||
./add_builtin_man_page.sh
|
||||
docker buildx create --name multiarch --use || exit 0
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64,linux/arm/v7 \
|
||||
--tag positronsecurity/ssh-audit:${VERSION} \
|
||||
--tag positronsecurity/ssh-audit:latest \
|
||||
.
|
||||
|
||||
local-build:
|
||||
docker build -t positronsecurity/ssh-audit:${VERSION} .
|
||||
docker buildx build \
|
||||
--tag positronsecurity/ssh-audit:${VERSION} \
|
||||
--tag positronsecurity/ssh-audit:latest \
|
||||
--load \
|
||||
--builder=multiarch \
|
||||
.
|
||||
|
||||
upload:
|
||||
docker login
|
||||
docker login -u positronsecurity
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64,linux/arm/v7 \
|
||||
--tag positronsecurity/ssh-audit:${VERSION} \
|
||||
|
@ -1,4 +1,5 @@
|
||||
all:
|
||||
./add_builtin_man_page.sh
|
||||
rm -rf /tmp/pypi_upload
|
||||
virtualenv -p /usr/bin/python3 /tmp/pypi_upload/
|
||||
cp -R src /tmp/pypi_upload/
|
||||
|
17
PACKAGING.md
17
PACKAGING.md
@ -2,7 +2,7 @@
|
||||
|
||||
An executable can only be made on a Windows host because the PyInstaller tool (https://www.pyinstaller.org/) does not support cross-compilation.
|
||||
|
||||
1.) Install Python v3.11.x from https://www.python.org/. To make life easier, check the option to add Python to the PATH environment variable.
|
||||
1.) Install Python v3.x from https://www.python.org/. To make life easier, check the option to add Python to the PATH environment variable.
|
||||
|
||||
2.) Install Cygwin (https://www.cygwin.com/).
|
||||
|
||||
@ -15,10 +15,10 @@ An executable can only be made on a Windows host because the PyInstaller tool (h
|
||||
|
||||
# PyPI
|
||||
|
||||
To create package and upload to test server:
|
||||
To create package and upload to test server (hint: use username '\_\_token\_\_' and API token for test.pypi.org):
|
||||
|
||||
```
|
||||
$ sudo apt install python3-virtualenv python3.8-venv
|
||||
$ sudo apt install python3-virtualenv python3.10-venv
|
||||
$ make -f Makefile.pypi
|
||||
$ make -f Makefile.pypi uploadtest
|
||||
```
|
||||
@ -31,7 +31,7 @@ To download from test server and verify:
|
||||
$ pip3 install --index-url https://test.pypi.org/simple ssh-audit
|
||||
```
|
||||
|
||||
To upload to production server (hint: use username '\_\_token\_\_' and API token):
|
||||
To upload to production server (hint: use username '\_\_token\_\_' and API token for production pypi.org):
|
||||
|
||||
```
|
||||
$ make -f Makefile.pypi uploadprod
|
||||
@ -61,19 +61,22 @@ Upload the snap with:
|
||||
$ snapcraft export-login ~/snap_creds.txt
|
||||
$ export SNAPCRAFT_STORE_CREDENTIALS=$(cat ~/snap_creds.txt)
|
||||
$ snapcraft upload --release=beta ssh-audit_*.snap
|
||||
$ snapcraft upload --release=stable ssh-audit_*.snap
|
||||
$ snapcraft status ssh-audit # Note the revision number of the beta channel.
|
||||
$ snapcraft release ssh-audit X stable # Fill in with the revision number.
|
||||
```
|
||||
|
||||
|
||||
# Docker
|
||||
|
||||
Build image with:
|
||||
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:
|
||||
|
||||
```
|
||||
$ make -f Makefile.docker
|
||||
```
|
||||
|
||||
Then upload it to Dockerhub with:
|
||||
Create a multi-architecture build and upload it to Dockerhub with (hint: use the API token as the password):
|
||||
|
||||
```
|
||||
$ make -f Makefile.docker upload
|
||||
|
70
README.md
70
README.md
@ -32,7 +32,7 @@
|
||||
- historical information from OpenSSH, Dropbear SSH and libssh;
|
||||
- policy scans to ensure adherence to a hardened/standard configuration;
|
||||
- runs on Linux and Windows;
|
||||
- supports Python 3.7 - 3.11;
|
||||
- supports Python 3.8 - 3.12;
|
||||
- no dependencies
|
||||
|
||||
## Usage
|
||||
@ -48,16 +48,33 @@ usage: ssh-audit.py [options] <host>
|
||||
-c, --client-audit starts a server on port 2222 to audit client
|
||||
software config (use -p to change port;
|
||||
use -t to change timeout)
|
||||
--conn-rate-test=N[:max_rate] 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.
|
||||
-d, --debug Enable debug output.
|
||||
--dheat=N[:kex[:e_len]] 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).
|
||||
-g, --gex-test=<x[,y,...]> dh gex modulus size test
|
||||
<min1:pref1:max1[,min2:pref2:max2,...]>
|
||||
<x-y[:step]>
|
||||
-j, --json JSON output (use -jj to enable indents)
|
||||
-l, --level=<level> minimum output level (info|warn|fail)
|
||||
-L, --list-policies list all the official, built-in policies
|
||||
-L, --list-policies list all the official, built-in policies. Use with -v
|
||||
to view policy change logs.
|
||||
--lookup=<alg1,alg2,...> looks up an algorithm(s) without
|
||||
connecting to a server
|
||||
-m, --manual print the man page (Windows only)
|
||||
-m, --manual print the man page (Docker, PyPI, Snap, and Windows
|
||||
builds only)
|
||||
-M, --make-policy=<policy.txt> creates a policy based on the target server
|
||||
(i.e.: the target server has the ideal
|
||||
configuration that other servers should
|
||||
@ -66,6 +83,9 @@ usage: ssh-audit.py [options] <host>
|
||||
-p, --port=<port> port to connect
|
||||
-P, --policy=<"policy name" | policy.txt> run a policy test using the
|
||||
specified policy
|
||||
--skip-rate-test skip the connection rate test during standard audits
|
||||
(used to safely infer whether the DHEat attack
|
||||
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
|
||||
@ -130,6 +150,21 @@ To create a policy based on a target server (which can be manually edited):
|
||||
ssh-audit -M new_policy.txt targetserver
|
||||
```
|
||||
|
||||
To run the DHEat CPU exhaustion DoS attack ([CVE-2002-20001](https://nvd.nist.gov/vuln/detail/CVE-2002-20001)) against a target using 10 concurrent sockets:
|
||||
```
|
||||
ssh-audit --dheat=10 targetserver
|
||||
```
|
||||
|
||||
To run the DHEat attack using the `diffie-hellman-group-exchange-sha256` key exchange algorithm:
|
||||
```
|
||||
ssh-audit --dheat=10:diffie-hellman-group-exchange-sha256 targetserver
|
||||
```
|
||||
|
||||
To run the DHEat attack using the `diffie-hellman-group-exchange-sha256` key exchange algorithm along with very small but non-standard packet lengths (this may result in the same CPU exhaustion, but with many less bytes per second being sent):
|
||||
```
|
||||
ssh-audit --dheat=10:diffie-hellman-group-exchange-sha256:4 targetserver
|
||||
```
|
||||
|
||||
## Screenshots
|
||||
|
||||
### Server Standard Audit Example
|
||||
@ -151,7 +186,7 @@ Below is a screen shot of the client-auditing output when an unhardened OpenSSH
|
||||
Guides to harden server & client configuration can be found here: [https://www.ssh-audit.com/hardening_guides.html](https://www.ssh-audit.com/hardening_guides.html)
|
||||
|
||||
## Pre-Built Packages
|
||||
Pre-built packages are available for Windows (see the releases page), PyPI, Snap, and Docker:
|
||||
Pre-built packages are available for Windows (see the [Releases](https://github.com/jtesta/ssh-audit/releases) page), PyPI, Snap, and Docker:
|
||||
|
||||
To install from PyPI:
|
||||
```
|
||||
@ -178,6 +213,33 @@ For convenience, a web front-end on top of the command-line tool is available at
|
||||
|
||||
## ChangeLog
|
||||
|
||||
### v3.2.0 (2024-04-22)
|
||||
- Added implementation of the DHEat denial-of-service attack (see `--dheat` option; [CVE-2002-20001](https://nvd.nist.gov/vuln/detail/CVE-2002-20001)).
|
||||
- Expanded filter of CBC ciphers to flag for the Terrapin vulnerability. It now includes more rarely found ciphers.
|
||||
- Fixed parsing of `ecdsa-sha2-nistp*` CA signatures on host keys. Additionally, they are now flagged as potentially back-doored, just as standard host keys are.
|
||||
- Gracefully handle rare exceptions (i.e.: crashes) while performing GEX tests.
|
||||
- The built-in man page (`-m`, `--manual`) is now available on Docker, PyPI, and Snap builds, in addition to the Windows build.
|
||||
- Snap builds are now architecture-independent.
|
||||
- Changed Docker base image from `python:3-slim` to `python:3-alpine`, resulting in a 59% reduction in image size; credit [Daniel Thamdrup](https://github.com/dallemon).
|
||||
- Added built-in policies for Amazon Linux 2023, Debian 12, OpenSSH 9.7, and Rocky Linux 9.
|
||||
- Built-in policies now include a change log (use `-L -v` to view them).
|
||||
- Custom policies now support the `allow_algorithm_subset_and_reordering` directive to allow targets to pass with a subset and/or re-ordered list of host keys, kex, ciphers, and MACs. This allows for the creation of a baseline policy where targets can optionally implement stricter controls; partial credit [yannik1015](https://github.com/yannik1015).
|
||||
- Custom policies now support the `allow_larger_keys` directive to allow targets to pass with larger host keys, CA keys, and Diffie-Hellman keys. This allows for the creation of a baseline policy where targets can optionally implement stricter controls; partial credit [Damian Szuberski](https://github.com/szubersk).
|
||||
- Color output is disabled if the `NO_COLOR` environment variable is set (see https://no-color.org/).
|
||||
- Added 1 new key exchange algorithm: `gss-nistp384-sha384-*`.
|
||||
- Added 1 new cipher: `aes128-ocb@libassh.org`.
|
||||
|
||||
### v3.1.0 (2023-12-20)
|
||||
- Added test for the Terrapin message prefix truncation vulnerability ([CVE-2023-48795](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-48795)).
|
||||
- Dropped support for Python 3.7 (EOL was reached in June 2023).
|
||||
- Added Python 3.12 support.
|
||||
- In server policies, reduced expected DH modulus sizes from 4096 to 3072 to match the [online hardening guides](https://ssh-audit.com/hardening_guides.html) (note that 3072-bit moduli provide the equivalent of 128-bit symmetric security).
|
||||
- In Ubuntu 22.04 client policy, moved host key types `sk-ssh-ed25519@openssh.com` and `ssh-ed25519` to the end of all certificate types.
|
||||
- Updated Ubuntu Server & Client policies for 20.04 and 22.04 to account for key exchange list changes due to Terrapin vulnerability patches.
|
||||
- Re-organized option host key types for OpenSSH 9.2 server policy to correspond with updated Debian 12 hardening guide.
|
||||
- Added built-in policies for OpenSSH 9.5 and 9.6.
|
||||
- Added an `additional_notes` field to the JSON output.
|
||||
|
||||
### v3.0.0 (2023-09-07)
|
||||
- Results from concurrent scans against multiple hosts are no longer improperly combined; bug discovered by [Adam Russell](https://github.com/thecliguy).
|
||||
- Hostname resolution failure no longer causes scans against multiple hosts to terminate unexpectedly; credit [Dani Cuesta](https://github.com/daniel-cues).
|
||||
|
@ -3,7 +3,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (C) 2021 Joe Testa (jtesta@positronsecurity.com)
|
||||
# Copyright (C) 2021-2024 Joe Testa (jtesta@positronsecurity.com)
|
||||
# Copyright (C) 2021 Adam Russell (<adam[at]thecliguy[dot]co[dot]uk>)
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@ -26,22 +26,21 @@
|
||||
#
|
||||
|
||||
################################################################################
|
||||
# update_windows_man_page.sh
|
||||
# add_builtin_man_page.sh
|
||||
#
|
||||
# PURPOSE
|
||||
# Since Windows lacks a manual reader it's necessary to provide an alternative
|
||||
# means of reading the man page.
|
||||
# Since some platforms lack a manual reader it's necessary to provide an
|
||||
# alternative means of reading the man page.
|
||||
#
|
||||
# This script should be run as part of the ssh-audit packaging process for
|
||||
# Windows. It populates the 'WINDOWS_MAN_PAGE' variable in 'globals.py' with
|
||||
# the contents of the man page. Windows users can then print the content of
|
||||
# 'WINDOWS_MAN_PAGE' by invoking ssh-audit with the manual parameters
|
||||
# (--manual / -m).
|
||||
# Docker, PyPI, Snap, and Windows. It populates the 'BUILTIN_MAN_PAGE'
|
||||
# variable in 'globals.py' with the contents of the man page. Users can then
|
||||
# see the man page with "ssh-audit [--manual|-m]".
|
||||
#
|
||||
# Cygwin is required.
|
||||
# Linux or Cygwin is required to run this script.
|
||||
#
|
||||
# USAGE
|
||||
# update_windows_man_page.sh [-m <path-to-man-page>] [-g <path-to-globals.py>]
|
||||
# add_builtin_man_page.sh [-m <path-to-man-page>] [-g <path-to-globals.py>]
|
||||
#
|
||||
################################################################################
|
||||
|
||||
@ -102,7 +101,7 @@ command -v sed >/dev/null 2>&1 || { echo >&2 "sed not found."; exit 1; }
|
||||
git checkout "${GLOBALS_PY}" > /dev/null 2>&1
|
||||
|
||||
# Remove the Windows man page placeholder from 'globals.py'.
|
||||
sed -i '/^WINDOWS_MAN_PAGE/d' "${GLOBALS_PY}"
|
||||
sed -i '/^BUILTIN_MAN_PAGE/d' "${GLOBALS_PY}"
|
||||
|
||||
echo "Processing man page at ${MAN_PAGE} and placing output into ${GLOBALS_PY}..."
|
||||
|
||||
@ -116,7 +115,7 @@ echo "Processing man page at ${MAN_PAGE} and placing output into ${GLOBALS_PY}..
|
||||
# escape sequence. Not required under Cygwin because man outputs ANSI escape
|
||||
# codes automatically.
|
||||
|
||||
echo WINDOWS_MAN_PAGE = '"""' >> "${GLOBALS_PY}"
|
||||
echo BUILTIN_MAN_PAGE = '"""' >> "${GLOBALS_PY}"
|
||||
|
||||
if [[ "${PLATFORM}" == CYGWIN* ]]; then
|
||||
MANWIDTH=80 MAN_KEEP_FORMATTING=1 man "${MAN_PAGE}" | sed $'s/\u2010/-/g' >> "${GLOBALS_PY}"
|
@ -3,7 +3,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (C) 2021 Joe Testa (jtesta@positronsecurity.com)
|
||||
# Copyright (C) 2021-2024 Joe Testa (jtesta@positronsecurity.com)
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
@ -44,6 +44,9 @@ rm -rf parts/ prime/ snap/ stage/ build/ dist/ src/*.egg-info/ ssh-audit*.snap
|
||||
git checkout snapcraft.yaml 2> /dev/null
|
||||
git checkout src/ssh_audit/globals.py 2> /dev/null
|
||||
|
||||
# Add the built-in manual page.
|
||||
./add_builtin_man_page.sh
|
||||
|
||||
# Get the version from the globals.py file.
|
||||
version=$(grep VERSION src/ssh_audit/globals.py | awk 'BEGIN {FS="="} ; {print $2}' | tr -d '[:space:]')
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (C) 2021 Joe Testa (jtesta@positronsecurity.com)
|
||||
# Copyright (C) 2021-2024 Joe Testa (jtesta@positronsecurity.com)
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
@ -77,7 +77,7 @@ fi
|
||||
git checkout src/ssh_audit/globals.py 2> /dev/null
|
||||
|
||||
# Update the man page.
|
||||
./update_windows_man_page.sh
|
||||
./add_builtin_man_page.sh
|
||||
retval=$?
|
||||
if [[ ${retval} != 0 ]]; then
|
||||
echo "Failed to run ./update_windows_man_page.sh"
|
||||
|
@ -456,15 +456,15 @@ run_test() {
|
||||
test_name="TinySSH ${version} ${test_number}"
|
||||
fi
|
||||
|
||||
cid=$(docker run -d -p 2222:22 "${IMAGE_NAME}:${IMAGE_VERSION}" ${server_exec})
|
||||
#echo "Running: docker run --rm -d -p 2222:22 $IMAGE_NAME:$IMAGE_VERSION ${server_exec}"
|
||||
cid=$(docker run --rm -d -p 2222:22 "${IMAGE_NAME}:${IMAGE_VERSION}" ${server_exec})
|
||||
retval=$?
|
||||
#echo "Running: docker run -d -p 2222:22 $IMAGE_NAME:$IMAGE_VERSION ${server_exec}"
|
||||
if [[ ${retval} != 0 ]]; then
|
||||
echo -e "${REDB}Failed to run docker image! (exit code: ${retval})${CLR}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
./ssh-audit.py localhost:2222 > "$test_result_stdout"
|
||||
./ssh-audit.py --skip-rate-test localhost:2222 > "$test_result_stdout"
|
||||
actual_retval=$?
|
||||
if [[ $actual_retval != "$expected_retval" ]]; then
|
||||
echo -e "${REDB}Unexpected return value. Expected: ${expected_retval}; Actual: ${actual_retval}${CLR}"
|
||||
@ -478,7 +478,7 @@ run_test() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
./ssh-audit.py -jj localhost:2222 > "$test_result_json"
|
||||
./ssh-audit.py --skip-rate-test -jj localhost:2222 > "$test_result_json"
|
||||
actual_retval=$?
|
||||
if [[ $actual_retval != "$expected_retval" ]]; then
|
||||
echo -e "${REDB}Unexpected return value. Expected: ${expected_retval}; Actual: ${actual_retval}${CLR}"
|
||||
@ -603,8 +603,8 @@ run_policy_test() {
|
||||
expected_exit_code=$6
|
||||
|
||||
|
||||
#echo "Running: docker run -d -p 2222:22 $IMAGE_NAME:$IMAGE_VERSION ${server_exec}"
|
||||
cid=$(docker run -d -p 2222:22 "${IMAGE_NAME}:${IMAGE_VERSION}" ${server_exec})
|
||||
#echo "Running: docker run --rm -d -p 2222:22 $IMAGE_NAME:$IMAGE_VERSION ${server_exec}"
|
||||
cid=$(docker run --rm -d -p 2222:22 "${IMAGE_NAME}:${IMAGE_VERSION}" ${server_exec})
|
||||
retval=$?
|
||||
if [[ ${retval} != 0 ]]; then
|
||||
echo -e "${REDB}Failed to run docker image! (exit code: ${retval})${CLR}"
|
||||
@ -746,7 +746,7 @@ run_openssh_test "5.6p1" "test5" "${PROGRAM_RETVAL_FAILURE}"
|
||||
echo
|
||||
run_openssh_test "8.0p1" "test1" "${PROGRAM_RETVAL_FAILURE}"
|
||||
run_openssh_test "8.0p1" "test2" "${PROGRAM_RETVAL_FAILURE}"
|
||||
run_openssh_test "8.0p1" "test3" "${PROGRAM_RETVAL_GOOD}"
|
||||
run_openssh_test "8.0p1" "test3" "${PROGRAM_RETVAL_WARNING}"
|
||||
echo
|
||||
run_dropbear_test "2019.78" "test1" "-r /etc/dropbear/dropbear_rsa_host_key_1024 -r /etc/dropbear/dropbear_dss_host_key -r /etc/dropbear/dropbear_ecdsa_host_key" 3
|
||||
echo
|
||||
@ -784,11 +784,20 @@ run_custom_policy_test "config2" "test13" "${PROGRAM_RETVAL_GOOD}"
|
||||
# Failing test with DH modulus test.
|
||||
run_custom_policy_test "config2" "test14" "${PROGRAM_RETVAL_FAILURE}"
|
||||
|
||||
# Passing test with algorithm subset matching.
|
||||
run_custom_policy_test "config2" "test15" "${PROGRAM_RETVAL_GOOD}"
|
||||
|
||||
# Failing test with algorithm subset matching.
|
||||
run_custom_policy_test "config2" "test16" "${PROGRAM_RETVAL_FAILURE}"
|
||||
|
||||
# Passing test with larger key matching.
|
||||
run_custom_policy_test "config2" "test17" "${PROGRAM_RETVAL_GOOD}"
|
||||
|
||||
# Failing test for built-in OpenSSH 8.0p1 server policy (RSA host key size is 3072 instead of 4096).
|
||||
run_builtin_policy_test "Hardened OpenSSH Server v8.0 (version 3)" "8.0p1" "test1" "-o HostKeyAlgorithms=rsa-sha2-512,rsa-sha2-256,ssh-ed25519 -o KexAlgorithms=curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256 -o Ciphers=chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr -o MACs=hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-128-etm@openssh.com" "${PROGRAM_RETVAL_FAILURE}"
|
||||
run_builtin_policy_test "Hardened OpenSSH Server v8.0 (version 4)" "8.0p1" "test1" "-o HostKeyAlgorithms=rsa-sha2-512,rsa-sha2-256,ssh-ed25519 -o KexAlgorithms=curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256 -o Ciphers=chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr -o MACs=hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-128-etm@openssh.com" "${PROGRAM_RETVAL_FAILURE}"
|
||||
|
||||
# Failing test for built-in OpenSSH 8.0p1 server policy (MACs not hardened).
|
||||
run_builtin_policy_test "Hardened OpenSSH Server v8.0 (version 3)" "8.0p1" "test2" "-o HostKeyAlgorithms=rsa-sha2-512,rsa-sha2-256,ssh-ed25519 -o KexAlgorithms=curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256 -o Ciphers=chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr" "${PROGRAM_RETVAL_FAILURE}"
|
||||
run_builtin_policy_test "Hardened OpenSSH Server v8.0 (version 4)" "8.0p1" "test2" "-o HostKeyAlgorithms=rsa-sha2-512,rsa-sha2-256,ssh-ed25519 -o KexAlgorithms=curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256 -o Ciphers=chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr" "${PROGRAM_RETVAL_FAILURE}"
|
||||
|
||||
|
||||
if [[ $num_failures == 0 ]]; then
|
||||
|
@ -19,11 +19,11 @@ classifiers =
|
||||
License :: OSI Approved :: MIT License
|
||||
Operating System :: OS Independent
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.7
|
||||
Programming Language :: Python :: 3.8
|
||||
Programming Language :: Python :: 3.9
|
||||
Programming Language :: Python :: 3.10
|
||||
Programming Language :: Python :: 3.11
|
||||
Programming Language :: Python :: 3.12
|
||||
Programming Language :: Python :: Implementation :: CPython
|
||||
Programming Language :: Python :: Implementation :: PyPy
|
||||
Topic :: Security
|
||||
@ -33,7 +33,7 @@ classifiers =
|
||||
packages = find:
|
||||
package_dir =
|
||||
= src
|
||||
python_requires = >=3.7,<4
|
||||
python_requires = >=3.8,<4
|
||||
|
||||
[options.packages.find]
|
||||
where = src
|
||||
|
@ -8,6 +8,9 @@ description: |
|
||||
base: core22
|
||||
grade: stable
|
||||
confinement: strict
|
||||
architectures:
|
||||
- build-on: [amd64]
|
||||
build-for: [all]
|
||||
|
||||
apps:
|
||||
ssh-audit:
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (C) 2017-2021 Joe Testa (jtesta@positronsecurity.com)
|
||||
Copyright (C) 2017-2024 Joe Testa (jtesta@positronsecurity.com)
|
||||
Copyright (C) 2017 Andris Raugulis (moo@arthepsy.eu)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@ -60,10 +60,20 @@ class AuditConf:
|
||||
self.manual = False
|
||||
self.debug = False
|
||||
self.gex_test = ''
|
||||
self.dheat: Optional[str] = None
|
||||
self.dheat_concurrent_connections: int = 0
|
||||
self.dheat_e_length: int = 0
|
||||
self.dheat_target_alg: str = ""
|
||||
self.skip_rate_test = False
|
||||
self.conn_rate_test: str = "1:1"
|
||||
self.conn_rate_test_enabled = False
|
||||
self.conn_rate_test_threads = 0
|
||||
self.conn_rate_test_target_rate = 0
|
||||
|
||||
|
||||
def __setattr__(self, name: str, value: Union[str, int, float, bool, Sequence[int]]) -> None:
|
||||
valid = False
|
||||
if name in ['batch', 'client_audit', 'colors', 'json', 'json_print_indent', 'list_policies', 'manual', 'make_policy', 'ssh1', 'ssh2', 'timeout_set', 'verbose', 'debug']:
|
||||
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']:
|
||||
valid, value = True, bool(value)
|
||||
elif name in ['ipv4', 'ipv6']:
|
||||
valid, value = True, bool(value)
|
||||
@ -94,6 +104,89 @@ class AuditConf:
|
||||
if num_threads < 1:
|
||||
raise ValueError('invalid number of threads: {}'.format(value))
|
||||
value = num_threads
|
||||
elif name == "dheat":
|
||||
# Valid values:
|
||||
# * None
|
||||
# * "10" (concurrent-connections)
|
||||
# * "10:diffie-hellman-group18-sha512" (concurrent-connections:target-alg)
|
||||
# * "10:diffie-hellman-group18-sha512:100" (concurrent-connections:target-alg:e-length)
|
||||
valid = True
|
||||
if value is not None:
|
||||
|
||||
def _parse_concurrent_connections(s: str) -> int:
|
||||
if Utils.parse_int(s) < 1:
|
||||
raise ValueError("number of concurrent connections must be 1 or greater: {}".format(s))
|
||||
return int(s)
|
||||
|
||||
def _parse_e_length(s: str) -> int:
|
||||
s_int = Utils.parse_int(s)
|
||||
if s_int < 2:
|
||||
raise ValueError("length of e must not be less than 2: {}".format(s))
|
||||
return s_int
|
||||
|
||||
def _parse_target_alg(s: str) -> str:
|
||||
if len(s) == 0:
|
||||
raise ValueError("target algorithm must not be the empty string.")
|
||||
return s
|
||||
|
||||
value = str(value)
|
||||
fields = value.split(':')
|
||||
|
||||
self.dheat_concurrent_connections = _parse_concurrent_connections(fields[0])
|
||||
|
||||
# Parse the target algorithm if present.
|
||||
if len(fields) >= 2:
|
||||
self.dheat_target_alg = _parse_target_alg(fields[1])
|
||||
|
||||
# Parse the length of e, if present.
|
||||
if len(fields) == 3:
|
||||
self.dheat_e_length = _parse_e_length(fields[2])
|
||||
|
||||
if len(fields) > 3:
|
||||
raise ValueError("only three fields are expected instead of {}: {}".format(len(fields), value))
|
||||
|
||||
elif name in ["dheat_concurrent_connections", "dheat_e_length"]:
|
||||
valid = True
|
||||
if not isinstance(value, int):
|
||||
valid = False
|
||||
|
||||
elif name == "dheat_target_alg":
|
||||
valid = True
|
||||
if not isinstance(value, str):
|
||||
valid = False
|
||||
|
||||
elif name == "conn_rate_test":
|
||||
# Valid values:
|
||||
# * "4" (run rate test with 4 threads)
|
||||
# * "4:100" (run rate test with 4 threads, targeting 100 connections/second)
|
||||
|
||||
error_msg = "valid format for {:s} is \"N\" or \"N:N\", where N is an integer.".format(name)
|
||||
self.conn_rate_test_enabled = True
|
||||
fields = str(value).split(":")
|
||||
|
||||
if len(fields) > 2 or len(fields) == 0:
|
||||
raise ValueError(error_msg)
|
||||
else:
|
||||
self.conn_rate_test_threads = int(fields[0])
|
||||
if self.conn_rate_test_threads < 1:
|
||||
raise ValueError("number of threads must be 1 or greater.")
|
||||
|
||||
self.conn_rate_test_target_rate = 0
|
||||
if len(fields) == 2:
|
||||
self.conn_rate_test_target_rate = int(fields[1])
|
||||
if self.conn_rate_test_target_rate < 1:
|
||||
raise ValueError("rate target must be 1 or greater.")
|
||||
|
||||
elif name == "conn_rate_test_enabled":
|
||||
valid = True
|
||||
if not isinstance(value, bool):
|
||||
valid = False
|
||||
|
||||
elif name in ["conn_rate_test_threads", "conn_rate_test_target_rate"]:
|
||||
valid = True
|
||||
if not isinstance(value, int):
|
||||
valid = False
|
||||
|
||||
|
||||
if valid:
|
||||
object.__setattr__(self, name, value)
|
||||
|
124
src/ssh_audit/builtin_policies.py
Normal file
124
src/ssh_audit/builtin_policies.py
Normal file
@ -0,0 +1,124 @@
|
||||
"""
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (C) 2020-2024 Joe Testa (jtesta@positronsecurity.com)
|
||||
|
||||
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.
|
||||
"""
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
# Each field maps directly to a private member variable of the Policy class.
|
||||
BUILTIN_POLICIES: Dict[str, Dict[str, Union[Optional[str], Optional[List[str]], bool, Dict[str, Any]]]] = {
|
||||
|
||||
|
||||
# Amazon Linux 2023
|
||||
'Hardened Amazon Linux 2023 (version 1)': {'version': '1', 'changelog': 'Initial version', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256', 'kex-strict-s-v00@openssh.com'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
|
||||
# Debian Server 12
|
||||
'Hardened Debian 12 (version 1)': {'version': '1', 'changelog': 'Initial version', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256', 'kex-strict-s-v00@openssh.com'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
|
||||
# Rocky Linux 9
|
||||
'Hardened Rocky Linux 9 (version 1)': {'version': '1', 'changelog': 'Initial version', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256', 'kex-strict-s-v00@openssh.com'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
|
||||
# Ubuntu Server policies
|
||||
|
||||
'Hardened Ubuntu Server 16.04 LTS (version 4)': {'version': '4', 'changelog': 'No change log available.', 'banner': None, 'compressions': None, 'host_keys': ['ssh-ed25519'], 'optional_host_keys': ['ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256@libssh.org', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
'Hardened Ubuntu Server 18.04 LTS (version 4)': {'version': '4', 'changelog': 'No change log available.', 'banner': None, 'compressions': None, 'host_keys': ['ssh-ed25519'], 'optional_host_keys': ['ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
'Hardened Ubuntu Server 20.04 LTS (version 5)': {'version': '5', 'changelog': 'Added kex-strict-s-v00@openssh.com to kex list.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256', 'kex-strict-s-v00@openssh.com'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
'Hardened Ubuntu Server 22.04 LTS (version 5)': {'version': '5', 'changelog': 'Added kex-strict-s-v00@openssh.com to kex list.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256', 'kex-strict-s-v00@openssh.com'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
|
||||
# Generic OpenSSH Server policies
|
||||
|
||||
'Hardened OpenSSH Server v7.7 (version 4)': {'version': '4', 'changelog': 'No change log available.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v7.8 (version 4)': {'version': '4', 'changelog': 'No change log available.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v7.9 (version 4)': {'version': '4', 'changelog': 'No change log available.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v8.0 (version 4)': {'version': '4', 'changelog': 'No change log available.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v8.1 (version 4)': {'version': '4', 'changelog': 'No change log available.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v8.2 (version 4)': {'version': '4', 'changelog': 'No change log available.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v8.3 (version 4)': {'version': '4', 'changelog': 'No change log available.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v8.4 (version 4)': {'version': '4', 'changelog': 'No change log available.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v8.5 (version 4)': {'version': '4', 'changelog': 'No change log available.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v8.6 (version 4)': {'version': '4', 'changelog': 'No change log available.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v8.7 (version 4)': {'version': '4', 'changelog': 'No change log available.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v8.8 (version 3)': {'version': '3', 'changelog': 'No change log available.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v8.9 (version 3)': {'version': '3', 'changelog': 'No change log available.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v9.0 (version 3)': {'version': '3', 'changelog': 'No change log available.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v9.1 (version 3)': {'version': '3', 'changelog': 'No change log available.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v9.2 (version 3)': {'version': '3', 'changelog': 'No change log available.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519-cert-v01@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'sk-ssh-ed25519@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v9.3 (version 3)': {'version': '3', 'changelog': 'No change log available.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v9.4 (version 2)': {'version': '2', 'changelog': 'No change log available.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v9.5 (version 1)': {'version': '1', 'changelog': 'Initial version.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v9.6 (version 1)': {'version': '1', 'changelog': 'Initial version.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256', 'ext-info-s', 'kex-strict-s-v00@openssh.com'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v9.7 (version 1)': {'version': '1', 'changelog': 'Initial version.', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256', 'ext-info-s', 'kex-strict-s-v00@openssh.com'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 3072}, 'server_policy': True},
|
||||
|
||||
|
||||
# Amazon Linux Policies
|
||||
|
||||
'Hardened Amazon Linux Client 2023 (version 1)': {'version': '1', 'changelog': 'Initial version.', 'banner': None, 'compressions': None, 'host_keys': ['sk-ssh-ed25519-cert-v01@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'sk-ssh-ed25519@openssh.com', 'ssh-ed25519', 'rsa-sha2-512', 'rsa-sha2-256'], 'optional_host_keys': None, 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256', 'ext-info-c', 'kex-strict-c-v00@openssh.com'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': None, 'dh_modulus_sizes': None, 'server_policy': False},
|
||||
|
||||
|
||||
# Debian Client Policies
|
||||
|
||||
'Hardened Debian Client 12 (version 1)': {'version': '1', 'changelog': 'Initial version.', 'banner': None, 'compressions': None, 'host_keys': ['sk-ssh-ed25519-cert-v01@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'sk-ssh-ed25519@openssh.com', 'ssh-ed25519', 'rsa-sha2-512', 'rsa-sha2-256'], 'optional_host_keys': None, 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256', 'ext-info-c', 'kex-strict-c-v00@openssh.com'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': None, 'dh_modulus_sizes': None, 'server_policy': False},
|
||||
|
||||
|
||||
# Rocky Linux Policies
|
||||
|
||||
'Hardened Rocky Linux Client 9 (version 1)': {'version': '1', 'changelog': 'Initial version.', 'banner': None, 'compressions': None, 'host_keys': ['sk-ssh-ed25519-cert-v01@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'sk-ssh-ed25519@openssh.com', 'ssh-ed25519', 'rsa-sha2-512', 'rsa-sha2-256'], 'optional_host_keys': None, 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256', 'ext-info-c', 'kex-strict-c-v00@openssh.com'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': None, 'dh_modulus_sizes': None, 'server_policy': False},
|
||||
|
||||
|
||||
# Ubuntu Client policies
|
||||
|
||||
'Hardened Ubuntu Client 16.04 LTS (version 2)': {'version': '2', 'changelog': 'No change log available.', 'banner': None, 'compressions': None, 'host_keys': ['ssh-ed25519', 'ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256', 'rsa-sha2-512'], 'optional_host_keys': None, 'kex': ['curve25519-sha256@libssh.org', 'diffie-hellman-group-exchange-sha256', 'ext-info-c'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': None, 'dh_modulus_sizes': None, 'server_policy': False},
|
||||
|
||||
'Hardened Ubuntu Client 18.04 LTS (version 2)': {'version': '2', 'changelog': 'No change log available.', 'banner': None, 'compressions': None, 'host_keys': ['ssh-ed25519', 'ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256', 'rsa-sha2-512'], 'optional_host_keys': None, 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256', 'ext-info-c'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': None, 'dh_modulus_sizes': None, 'server_policy': False},
|
||||
|
||||
'Hardened Ubuntu Client 20.04 LTS (version 3)': {'version': '3', 'changelog': 'Added kex-strict-c-v00@openssh.com to kex list.', 'banner': None, 'compressions': None, 'host_keys': ['ssh-ed25519', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512', 'rsa-sha2-512-cert-v01@openssh.com'], 'optional_host_keys': None, 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256', 'ext-info-c', 'kex-strict-c-v00@openssh.com'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': None, 'dh_modulus_sizes': None, 'server_policy': False},
|
||||
|
||||
'Hardened Ubuntu Client 22.04 LTS (version 4)': {'version': '4', 'changelog': 'Added kex-strict-c-v00@openssh.com to kex list.', 'banner': None, 'compressions': None, 'host_keys': ['sk-ssh-ed25519-cert-v01@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'sk-ssh-ed25519@openssh.com', 'ssh-ed25519', 'rsa-sha2-512', 'rsa-sha2-256'], 'optional_host_keys': None, 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256', 'ext-info-c', 'kex-strict-c-v00@openssh.com'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': None, 'dh_modulus_sizes': None, 'server_policy': False},
|
||||
|
||||
|
||||
}
|
1064
src/ssh_audit/dheat.py
Normal file
1064
src/ssh_audit/dheat.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -21,6 +21,7 @@
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
"""
|
||||
import struct
|
||||
import traceback
|
||||
|
||||
# pylint: disable=unused-import
|
||||
@ -65,7 +66,7 @@ class GEXTest:
|
||||
# Parse the server's KEX.
|
||||
_, payload = s.read_packet(2)
|
||||
SSH2_Kex.parse(out, payload)
|
||||
except KexDHException:
|
||||
except (KexDHException, struct.error):
|
||||
out.v("Failed to parse server's kex. Stack trace:\n%s" % str(traceback.format_exc()), write_now=True)
|
||||
return False
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (C) 2017-2023 Joe Testa (jtesta@positronsecurity.com)
|
||||
Copyright (C) 2017-2024 Joe Testa (jtesta@positronsecurity.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@ -22,7 +22,7 @@
|
||||
THE SOFTWARE.
|
||||
"""
|
||||
# The version to display.
|
||||
VERSION = 'v3.0.0'
|
||||
VERSION = 'v3.2.0'
|
||||
|
||||
# SSH software to impersonate
|
||||
SSH_HEADER = 'SSH-{0}-OpenSSH_8.2'
|
||||
@ -30,8 +30,8 @@ SSH_HEADER = 'SSH-{0}-OpenSSH_8.2'
|
||||
# The URL to the Github issues tracker.
|
||||
GITHUB_ISSUES_URL = 'https://github.com/jtesta/ssh-audit/issues'
|
||||
|
||||
# The man page. Only filled in on Windows systems.
|
||||
WINDOWS_MAN_PAGE = ''
|
||||
# The man page. Only filled in on Docker, PyPI, Snap, and Windows builds.
|
||||
BUILTIN_MAN_PAGE = ''
|
||||
|
||||
# True when installed from a Snap package, otherwise False.
|
||||
SNAP_PACKAGE = False
|
||||
|
@ -180,7 +180,7 @@ class HostKeyTest:
|
||||
hostkey_min_good = 256
|
||||
hostkey_min_warn = 224
|
||||
hostkey_warn_str = HostKeyTest.SMALL_ECC_MODULUS_WARNING
|
||||
if ca_key_type.startswith('ssh-ed25519') or host_key_type.startswith('ecdsa-sha2-nistp'):
|
||||
if ca_key_type.startswith('ssh-ed25519') or ca_key_type.startswith('ecdsa-sha2-nistp'):
|
||||
cakey_min_good = 256
|
||||
cakey_min_warn = 224
|
||||
cakey_warn_str = HostKeyTest.SMALL_ECC_MODULUS_WARNING
|
||||
@ -209,6 +209,10 @@ class HostKeyTest:
|
||||
elif (0 < ca_modulus_size < cakey_min_good) and (cakey_warn_str not in key_warn_comments):
|
||||
key_warn_comments.append(cakey_warn_str)
|
||||
|
||||
# If the CA key type uses ECDSA with a NIST P-curve, fail it for possibly being back-doored.
|
||||
if ca_key_type.startswith('ecdsa-sha2-nistp'):
|
||||
key_fail_comments.append('CA key uses elliptic curves that are suspected as being backdoored by the U.S. National Security Agency')
|
||||
|
||||
# If this host key type is in the RSA family, then mark them all as parsed (since results in one are valid for them all).
|
||||
if host_key_type in HostKeyTest.RSA_FAMILY:
|
||||
for rsa_type in HostKeyTest.RSA_FAMILY:
|
||||
|
@ -80,7 +80,7 @@ class KexDH: # pragma: nocover
|
||||
# contains the host key, among other things. Function returns the host
|
||||
# key blob (from which the fingerprint can be calculated).
|
||||
def recv_reply(self, s: 'SSH_Socket', parse_host_key_size: bool = True) -> Optional[bytes]:
|
||||
# Reset the CA info, in case it was set from a prior invokation.
|
||||
# Reset the CA info, in case it was set from a prior invocation.
|
||||
self.__hostkey_type = ''
|
||||
self.__hostkey_e = 0 # pylint: disable=unused-private-member
|
||||
self.__hostkey_n = 0 # pylint: disable=unused-private-member
|
||||
@ -100,7 +100,7 @@ class KexDH: # pragma: nocover
|
||||
# A connection error occurred. We can't parse anything, so just
|
||||
# return. The host key modulus (and perhaps certificate modulus)
|
||||
# will remain at length 0.
|
||||
self.out.d("KexDH.recv_reply(): received packge_type == -1.")
|
||||
self.out.d("KexDH.recv_reply(): received package_type == -1.")
|
||||
return None
|
||||
|
||||
# Get the host key blob, F, and signature.
|
||||
@ -212,6 +212,15 @@ class KexDH: # pragma: nocover
|
||||
# CA's modulus. Bingo.
|
||||
ca_key_n, ca_key_n_len, ptr = KexDH.__get_bytes(ca_key, ptr) # pylint: disable=unused-variable
|
||||
|
||||
if ca_key_type.startswith("ecdsa-sha2-nistp") and ca_key_n_len > 0:
|
||||
self.out.d("Found ecdsa-sha2-nistp* CA key type.")
|
||||
|
||||
# 0x04 signifies that this is an uncompressed public key (meaning that full X and Y values are provided in ca_key_n.
|
||||
if ca_key_n[0] == 4:
|
||||
ca_key_n_len = ca_key_n_len - 1 # Subtract the 0x04 byte.
|
||||
ca_key_n_len = int(ca_key_n_len / 2) # Divide by 2 since the modulus is the size of either the X or Y value.
|
||||
|
||||
|
||||
else:
|
||||
self.out.d("Certificate type %u found; this is not usually valid in the context of a host key! Skipping it..." % cert_type)
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (C) 2020-2023 Joe Testa (jtesta@positronsecurity.com)
|
||||
Copyright (C) 2020-2024 Joe Testa (jtesta@positronsecurity.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@ -31,6 +31,7 @@ from datetime import date
|
||||
|
||||
from ssh_audit import exitcodes
|
||||
from ssh_audit.banner import Banner
|
||||
from ssh_audit.builtin_policies import BUILTIN_POLICIES
|
||||
from ssh_audit.globals import SNAP_PACKAGE, SNAP_PERMISSIONS_ERROR
|
||||
from ssh_audit.ssh2_kex import SSH2_Kex
|
||||
|
||||
@ -38,71 +39,6 @@ from ssh_audit.ssh2_kex import SSH2_Kex
|
||||
# Validates policy files and performs policy testing
|
||||
class Policy:
|
||||
|
||||
# Each field maps directly to a private member variable of the Policy class.
|
||||
BUILTIN_POLICIES: Dict[str, Dict[str, Union[Optional[str], Optional[List[str]], bool, Dict[str, Any]]]] = {
|
||||
|
||||
# Ubuntu Server policies
|
||||
|
||||
'Hardened Ubuntu Server 16.04 LTS (version 3)': {'version': '3', 'banner': None, 'compressions': None, 'host_keys': ['ssh-ed25519'], 'optional_host_keys': ['ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256@libssh.org', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 4096}, 'server_policy': True},
|
||||
|
||||
'Hardened Ubuntu Server 18.04 LTS (version 3)': {'version': '3', 'banner': None, 'compressions': None, 'host_keys': ['ssh-ed25519'], 'optional_host_keys': ['ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 4096}, 'server_policy': True},
|
||||
|
||||
'Hardened Ubuntu Server 20.04 LTS (version 3)': {'version': '3', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 4096}, 'server_policy': True},
|
||||
|
||||
'Hardened Ubuntu Server 22.04 LTS (version 3)': {'version': '3', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 4096}, 'server_policy': True},
|
||||
|
||||
|
||||
# Generic OpenSSH Server policies
|
||||
|
||||
'Hardened OpenSSH Server v7.7 (version 3)': {'version': '3', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 4096}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v7.8 (version 3)': {'version': '3', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 4096}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v7.9 (version 3)': {'version': '3', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 4096}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v8.0 (version 3)': {'version': '3', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 4096}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v8.1 (version 3)': {'version': '3', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 4096}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v8.2 (version 3)': {'version': '3', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 4096}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v8.3 (version 3)': {'version': '3', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 4096}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v8.4 (version 3)': {'version': '3', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 4096}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v8.5 (version 3)': {'version': '3', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 4096}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v8.6 (version 3)': {'version': '3', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 4096}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v8.7 (version 3)': {'version': '3', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 4096}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v8.8 (version 2)': {'version': '2', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 4096}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v8.9 (version 2)': {'version': '2', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 4096}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v9.0 (version 2)': {'version': '2', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 4096}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v9.1 (version 2)': {'version': '2', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 4096}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v9.2 (version 2)': {'version': '2', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 4096}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v9.3 (version 2)': {'version': '2', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 4096}, 'server_policy': True},
|
||||
|
||||
'Hardened OpenSSH Server v9.4 (version 1)': {'version': '1', 'banner': None, 'compressions': None, 'host_keys': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-ed25519'], 'optional_host_keys': ['sk-ssh-ed25519@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512-cert-v01@openssh.com'], 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': {"rsa-sha2-256": {"hostkey_size": 4096}, "rsa-sha2-256-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "rsa-sha2-512": {"hostkey_size": 4096}, "rsa-sha2-512-cert-v01@openssh.com": {"ca_key_size": 4096, "ca_key_type": "ssh-rsa", "hostkey_size": 4096}, "sk-ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}, "sk-ssh-ed25519@openssh.com": {"hostkey_size": 256}, "ssh-ed25519": {"hostkey_size": 256}, "ssh-ed25519-cert-v01@openssh.com": {"ca_key_size": 256, "ca_key_type": "ssh-ed25519", "hostkey_size": 256}}, 'dh_modulus_sizes': {'diffie-hellman-group-exchange-sha256': 4096}, 'server_policy': True},
|
||||
|
||||
|
||||
# Ubuntu Client policies
|
||||
|
||||
'Hardened Ubuntu Client 16.04 LTS (version 2)': {'version': '2', 'banner': None, 'compressions': None, 'host_keys': ['ssh-ed25519', 'ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256', 'rsa-sha2-512'], 'optional_host_keys': None, 'kex': ['curve25519-sha256@libssh.org', 'diffie-hellman-group-exchange-sha256', 'ext-info-c'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': None, 'dh_modulus_sizes': None, 'server_policy': False},
|
||||
|
||||
'Hardened Ubuntu Client 18.04 LTS (version 2)': {'version': '2', 'banner': None, 'compressions': None, 'host_keys': ['ssh-ed25519', 'ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256', 'rsa-sha2-512'], 'optional_host_keys': None, 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256', 'ext-info-c'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': None, 'dh_modulus_sizes': None, 'server_policy': False},
|
||||
|
||||
'Hardened Ubuntu Client 20.04 LTS (version 2)': {'version': '2', 'banner': None, 'compressions': None, 'host_keys': ['ssh-ed25519', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519@openssh.com', 'sk-ssh-ed25519-cert-v01@openssh.com', 'rsa-sha2-256', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512', 'rsa-sha2-512-cert-v01@openssh.com'], 'optional_host_keys': None, 'kex': ['curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256', 'ext-info-c'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': None, 'dh_modulus_sizes': None, 'server_policy': False},
|
||||
|
||||
'Hardened Ubuntu Client 22.04 LTS (version 2)': {'version': '2', 'banner': None, 'compressions': None, 'host_keys': ['sk-ssh-ed25519-cert-v01@openssh.com', 'ssh-ed25519-cert-v01@openssh.com', 'sk-ssh-ed25519@openssh.com', 'ssh-ed25519', 'rsa-sha2-512-cert-v01@openssh.com', 'rsa-sha2-256-cert-v01@openssh.com', 'rsa-sha2-512', 'rsa-sha2-256'], 'optional_host_keys': None, 'kex': ['sntrup761x25519-sha512@openssh.com', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group-exchange-sha256', 'ext-info-c'], 'ciphers': ['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr'], 'macs': ['hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-128-etm@openssh.com'], 'hostkey_sizes': None, 'dh_modulus_sizes': None, 'server_policy': False},
|
||||
|
||||
}
|
||||
|
||||
WARNING_DEPRECATED_DIRECTIVES = "\nWARNING: this policy is using deprecated features. Future versions of ssh-audit may remove support for them. Re-generating the policy file is perhaps the most straight-forward way of resolving this issue. Manually converting the 'hostkey_size_*', 'cakey_size_*', and 'dh_modulus_size_*' directives into the new format is another option.\n"
|
||||
|
||||
def __init__(self, policy_file: Optional[str] = None, policy_data: Optional[str] = None, manual_load: bool = False, json_output: bool = False) -> None:
|
||||
@ -118,6 +54,9 @@ class Policy:
|
||||
self._hostkey_sizes: Optional[Dict[str, Dict[str, Union[int, str, bytes]]]] = None
|
||||
self._dh_modulus_sizes: Optional[Dict[str, int]] = None
|
||||
self._server_policy = True
|
||||
self._allow_algorithm_subset_and_reordering = False
|
||||
self._allow_larger_keys = False
|
||||
self._errors: List[Any] = []
|
||||
|
||||
self._name_and_version: str = ''
|
||||
|
||||
@ -176,7 +115,7 @@ class Policy:
|
||||
key = key.strip()
|
||||
val = val.strip()
|
||||
|
||||
if key not in ['name', 'version', 'banner', 'compressions', 'host keys', 'optional host keys', 'key exchanges', 'ciphers', 'macs', 'client policy', 'host_key_sizes', 'dh_modulus_sizes'] and not key.startswith('hostkey_size_') and not key.startswith('cakey_size_') and not key.startswith('dh_modulus_size_'):
|
||||
if key not in ['name', 'version', 'banner', 'compressions', 'host keys', 'optional host keys', 'key exchanges', 'ciphers', 'macs', 'client policy', 'host_key_sizes', 'dh_modulus_sizes', 'allow_algorithm_subset_and_reordering', 'allow_larger_keys'] and not key.startswith('hostkey_size_') and not key.startswith('cakey_size_') and not key.startswith('dh_modulus_size_'):
|
||||
raise ValueError("invalid field found in policy: %s" % line)
|
||||
|
||||
if key in ['name', 'banner']:
|
||||
@ -269,7 +208,10 @@ class Policy:
|
||||
|
||||
elif key.startswith('client policy') and val.lower() == 'true':
|
||||
self._server_policy = False
|
||||
|
||||
elif key == 'allow_algorithm_subset_and_reordering' and val.lower() == 'true':
|
||||
self._allow_algorithm_subset_and_reordering = True
|
||||
elif key == 'allow_larger_keys' and val.lower() == 'true':
|
||||
self._allow_larger_keys = True
|
||||
|
||||
if self._name is None:
|
||||
raise ValueError('The policy does not have a name field.')
|
||||
@ -279,13 +221,12 @@ class Policy:
|
||||
self._name_and_version = "%s (version %s)" % (self._name, self._version)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _append_error(errors: List[Any], mismatched_field: str, expected_required: Optional[List[str]], expected_optional: Optional[List[str]], actual: List[str]) -> None:
|
||||
def _append_error(self, mismatched_field: str, expected_required: Optional[List[str]], expected_optional: Optional[List[str]], actual: List[str]) -> None:
|
||||
if expected_required is None:
|
||||
expected_required = ['']
|
||||
if expected_optional is None:
|
||||
expected_optional = ['']
|
||||
errors.append({'mismatched_field': mismatched_field, 'expected_required': expected_required, 'expected_optional': expected_optional, 'actual': actual})
|
||||
self._errors.append({'mismatched_field': mismatched_field, 'expected_required': expected_required, 'expected_optional': expected_optional, 'actual': actual})
|
||||
|
||||
|
||||
def _normalize_hostkey_sizes(self) -> None:
|
||||
@ -358,6 +299,12 @@ name = "Custom Policy (based on %s on %s)"
|
||||
# The version of this policy (displayed in the output during scans). Not parsed, and may be any value, including strings.
|
||||
version = 1
|
||||
|
||||
# When false, host keys, kex, ciphers, and MAC lists must match exactly. When true, the target host may support a subset of the specified algorithms and/or algorithms may appear in a different order; this feature is useful for specifying a baseline and allowing some hosts the option to implement stricter controls.
|
||||
allow_algorithm_subset_and_reordering = false
|
||||
|
||||
# When false, host keys, CA keys, and Diffie-Hellman key sizes must exactly match what's specified in this policy. When true, target systems are allowed to have larger keys; this feature is useful for specifying a baseline and allowing some hosts the option to implement stricter controls.
|
||||
allow_larger_keys = false
|
||||
|
||||
# The banner that must match exactly. Commented out to ignore banners, since minor variability in the banner is sometimes normal.
|
||||
# banner = "%s"
|
||||
|
||||
@ -387,41 +334,53 @@ macs = %s
|
||||
'''Evaluates a server configuration against this policy. Returns a tuple of a boolean (True if server adheres to policy) and an array of strings that holds error messages.'''
|
||||
|
||||
ret = True
|
||||
errors: List[Any] = []
|
||||
|
||||
banner_str = str(banner)
|
||||
if (self._banner is not None) and (banner_str != self._banner):
|
||||
ret = False
|
||||
self._append_error(errors, 'Banner', [self._banner], None, [banner_str])
|
||||
self._append_error('Banner', [self._banner], None, [banner_str])
|
||||
|
||||
# All subsequent tests require a valid kex, so end here if we don't have one.
|
||||
if kex is None:
|
||||
return ret, errors, self._get_error_str(errors)
|
||||
error_list, error_str = self._get_errors()
|
||||
return ret, error_list, error_str
|
||||
|
||||
if (self._compressions is not None) and (kex.server.compression != self._compressions):
|
||||
ret = False
|
||||
self._append_error(errors, 'Compression', self._compressions, None, kex.server.compression)
|
||||
self._append_error('Compression', self._compressions, None, kex.server.compression)
|
||||
|
||||
# If a list of optional host keys was given in the policy, remove any of its entries from the list retrieved from the server. This allows us to do an exact comparison with the expected list below.
|
||||
pruned_host_keys = kex.key_algorithms
|
||||
if self._optional_host_keys is not None:
|
||||
pruned_host_keys = [x for x in kex.key_algorithms if x not in self._optional_host_keys]
|
||||
|
||||
if (self._host_keys is not None) and (pruned_host_keys != self._host_keys):
|
||||
ret = False
|
||||
self._append_error(errors, 'Host keys', self._host_keys, self._optional_host_keys, kex.key_algorithms)
|
||||
# Check host keys.
|
||||
if self._host_keys is not None:
|
||||
# If the policy allows subsets and re-ordered algorithms...
|
||||
if self._allow_algorithm_subset_and_reordering:
|
||||
for hostkey_t in kex.key_algorithms:
|
||||
if hostkey_t not in self._host_keys:
|
||||
ret = False
|
||||
self._append_error('Host keys', self._host_keys, self._optional_host_keys, kex.key_algorithms)
|
||||
break
|
||||
# The policy requires exact matching of algorithms.
|
||||
elif pruned_host_keys != self._host_keys:
|
||||
ret = False
|
||||
self._append_error('Host keys', self._host_keys, self._optional_host_keys, kex.key_algorithms)
|
||||
|
||||
# Check host key sizes.
|
||||
if self._hostkey_sizes is not None:
|
||||
hostkey_types = list(self._hostkey_sizes.keys())
|
||||
hostkey_types.sort() # Sorted to make testing output repeatable.
|
||||
for hostkey_type in hostkey_types:
|
||||
expected_hostkey_size = self._hostkey_sizes[hostkey_type]['hostkey_size']
|
||||
expected_hostkey_size = cast(int, self._hostkey_sizes[hostkey_type]['hostkey_size'])
|
||||
server_host_keys = kex.host_keys()
|
||||
if hostkey_type in server_host_keys:
|
||||
actual_hostkey_size = server_host_keys[hostkey_type]['hostkey_size']
|
||||
if actual_hostkey_size != expected_hostkey_size:
|
||||
actual_hostkey_size = cast(int, server_host_keys[hostkey_type]['hostkey_size'])
|
||||
if (self._allow_larger_keys and actual_hostkey_size < expected_hostkey_size) or \
|
||||
(not self._allow_larger_keys and actual_hostkey_size != expected_hostkey_size):
|
||||
ret = False
|
||||
self._append_error(errors, 'Host key (%s) sizes' % hostkey_type, [str(expected_hostkey_size)], None, [str(actual_hostkey_size)])
|
||||
self._append_error('Host key (%s) sizes' % hostkey_type, [str(expected_hostkey_size)], None, [str(actual_hostkey_size)])
|
||||
|
||||
# If we have expected CA signatures set, check them against what the server returned.
|
||||
if self._hostkey_sizes is not None and len(cast(str, self._hostkey_sizes[hostkey_type]['ca_key_type'])) > 0 and cast(int, self._hostkey_sizes[hostkey_type]['ca_key_size']) > 0:
|
||||
@ -433,23 +392,60 @@ macs = %s
|
||||
# Ensure that the CA signature type is what's expected (i.e.: the server doesn't have an RSA sig when we're expecting an ED25519 sig).
|
||||
if actual_ca_key_type != expected_ca_key_type:
|
||||
ret = False
|
||||
self._append_error(errors, 'CA signature type', [expected_ca_key_type], None, [actual_ca_key_type])
|
||||
self._append_error('CA signature type', [expected_ca_key_type], None, [actual_ca_key_type])
|
||||
# Ensure that the actual and expected signature sizes match.
|
||||
elif actual_ca_key_size != expected_ca_key_size:
|
||||
elif (self._allow_larger_keys and actual_ca_key_size < expected_ca_key_size) or \
|
||||
(not self._allow_larger_keys and actual_ca_key_size != expected_ca_key_size):
|
||||
ret = False
|
||||
self._append_error(errors, 'CA signature size (%s)' % actual_ca_key_type, [str(expected_ca_key_size)], None, [str(actual_ca_key_size)])
|
||||
self._append_error('CA signature size (%s)' % actual_ca_key_type, [str(expected_ca_key_size)], None, [str(actual_ca_key_size)])
|
||||
|
||||
if kex.kex_algorithms != self._kex:
|
||||
ret = False
|
||||
self._append_error(errors, 'Key exchanges', self._kex, None, kex.kex_algorithms)
|
||||
# Check key exchanges.
|
||||
if self._kex is not None:
|
||||
# If the policy allows subsets and re-ordered algorithms...
|
||||
if self._allow_algorithm_subset_and_reordering:
|
||||
for kex_t in kex.kex_algorithms:
|
||||
if kex_t not in self._kex:
|
||||
ret = False
|
||||
self._append_error('Key exchanges', self._kex, None, kex.kex_algorithms)
|
||||
break
|
||||
# If kex-strict-?-v00@openssh.com is in the policy (i.e. the Terrapin vulnerability countermeasure), then it must appear in the server's list, regardless of the "allow_algorithm_subset_and_reordering" flag.
|
||||
if ('kex-strict-s-v00@openssh.com' in self._kex and 'kex-strict-s-v00@openssh.com' not in kex.kex_algorithms) or \
|
||||
('kex-strict-c-v00@openssh.com' in self._kex and 'kex-strict-c-v00@openssh.com' not in kex.kex_algorithms):
|
||||
ret = False
|
||||
self._append_error('Key exchanges', self._kex, None, kex.kex_algorithms)
|
||||
|
||||
if (self._ciphers is not None) and (kex.server.encryption != self._ciphers):
|
||||
ret = False
|
||||
self._append_error(errors, 'Ciphers', self._ciphers, None, kex.server.encryption)
|
||||
# The policy requires exact matching of algorithms.
|
||||
elif kex.kex_algorithms != self._kex:
|
||||
ret = False
|
||||
self._append_error('Key exchanges', self._kex, None, kex.kex_algorithms)
|
||||
|
||||
if (self._macs is not None) and (kex.server.mac != self._macs):
|
||||
ret = False
|
||||
self._append_error(errors, 'MACs', self._macs, None, kex.server.mac)
|
||||
# Checking Ciphers
|
||||
if self._ciphers is not None:
|
||||
# If the policy allows subsets and re-ordered algorithms...
|
||||
if self._allow_algorithm_subset_and_reordering:
|
||||
for cipher_t in kex.server.encryption:
|
||||
if cipher_t not in self._ciphers:
|
||||
ret = False
|
||||
self._append_error('Ciphers', self._ciphers, None, kex.server.encryption)
|
||||
break
|
||||
# The policy requires exact matching of algorithms.
|
||||
elif kex.server.encryption != self._ciphers:
|
||||
ret = False
|
||||
self._append_error('Ciphers', self._ciphers, None, kex.server.encryption)
|
||||
|
||||
# Checking MACs
|
||||
if self._macs is not None:
|
||||
# If the policy allows subsets and re-ordered algorithms...
|
||||
if self._allow_algorithm_subset_and_reordering:
|
||||
for mac_t in kex.server.mac:
|
||||
if mac_t not in self._macs:
|
||||
ret = False
|
||||
self._append_error('MACs', self._macs, None, kex.server.mac)
|
||||
break
|
||||
# The policy requires exact matching of algorithms.
|
||||
elif kex.server.mac != self._macs:
|
||||
ret = False
|
||||
self._append_error('MACs', self._macs, None, kex.server.mac)
|
||||
|
||||
if self._dh_modulus_sizes is not None:
|
||||
dh_modulus_types = list(self._dh_modulus_sizes.keys())
|
||||
@ -458,26 +454,30 @@ macs = %s
|
||||
expected_dh_modulus_size = self._dh_modulus_sizes[dh_modulus_type]
|
||||
if dh_modulus_type in kex.dh_modulus_sizes():
|
||||
actual_dh_modulus_size = kex.dh_modulus_sizes()[dh_modulus_type]
|
||||
if expected_dh_modulus_size != actual_dh_modulus_size:
|
||||
if (self._allow_larger_keys and actual_dh_modulus_size < expected_dh_modulus_size) or \
|
||||
(not self._allow_larger_keys and actual_dh_modulus_size != expected_dh_modulus_size):
|
||||
ret = False
|
||||
self._append_error(errors, 'Group exchange (%s) modulus sizes' % dh_modulus_type, [str(expected_dh_modulus_size)], None, [str(actual_dh_modulus_size)])
|
||||
self._append_error('Group exchange (%s) modulus sizes' % dh_modulus_type, [str(expected_dh_modulus_size)], None, [str(actual_dh_modulus_size)])
|
||||
|
||||
return ret, errors, self._get_error_str(errors)
|
||||
error_list, error_str = self._get_errors()
|
||||
return ret, error_list, error_str
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _get_error_str(errors: List[Any]) -> str:
|
||||
'''Transforms an error struct to a flat string of error messages.'''
|
||||
def _get_errors(self) -> Tuple[List[Any], str]:
|
||||
'''Returns the list of errors, along with the string representation of those errors.'''
|
||||
|
||||
subset_and_reordering_semicolon = "; subset and/or reordering allowed" if self._allow_algorithm_subset_and_reordering else "; exact match"
|
||||
subset_and_reordering_parens = " (subset and/or reordering allowed)" if self._allow_algorithm_subset_and_reordering else ""
|
||||
error_list = []
|
||||
spacer = ''
|
||||
for e in errors:
|
||||
for e in self._errors:
|
||||
e_str = " * %s did not match.\n" % e['mismatched_field']
|
||||
|
||||
if ('expected_optional' in e) and (e['expected_optional'] != ['']):
|
||||
e_str += " - Expected (required): %s\n - Expected (optional): %s\n" % (Policy._normalize_error_field(e['expected_required']), Policy._normalize_error_field(e['expected_optional']))
|
||||
e_str += " - Expected (required%s): %s\n - Expected (optional): %s\n" % (subset_and_reordering_semicolon, Policy._normalize_error_field(e['expected_required']), Policy._normalize_error_field(e['expected_optional']))
|
||||
spacer = ' '
|
||||
else:
|
||||
e_str += " - Expected: %s\n" % Policy._normalize_error_field(e['expected_required'])
|
||||
e_str += " - Expected%s: %s\n" % (subset_and_reordering_parens, Policy._normalize_error_field(e['expected_required']))
|
||||
spacer = ' '
|
||||
e_str += " - Actual:%s%s\n" % (spacer, Policy._normalize_error_field(e['actual']))
|
||||
error_list.append(e_str)
|
||||
@ -488,7 +488,7 @@ macs = %s
|
||||
if len(error_list) > 0:
|
||||
error_str = "\n".join(error_list)
|
||||
|
||||
return error_str
|
||||
return self._errors, error_str
|
||||
|
||||
|
||||
def get_name_and_version(self) -> str:
|
||||
@ -502,28 +502,34 @@ macs = %s
|
||||
|
||||
|
||||
@staticmethod
|
||||
def list_builtin_policies() -> Tuple[List[str], List[str]]:
|
||||
def list_builtin_policies(verbose: bool) -> Tuple[List[str], List[str]]:
|
||||
'''Returns two lists: a list of names of built-in server policies, and a list of names of built-in client policies, respectively.'''
|
||||
server_policy_names = []
|
||||
client_policy_names = []
|
||||
server_policy_descriptions = []
|
||||
client_policy_descriptions = []
|
||||
|
||||
for policy_name, policy in Policy.BUILTIN_POLICIES.items():
|
||||
if policy['server_policy']:
|
||||
server_policy_names.append(policy_name)
|
||||
for policy_name, policy in BUILTIN_POLICIES.items():
|
||||
policy_description = ""
|
||||
if verbose:
|
||||
policy_description = "\"{:s}\": {:s}".format(policy_name, policy['changelog'])
|
||||
else:
|
||||
client_policy_names.append(policy_name)
|
||||
policy_description = "\"{:s}\"".format(policy_name)
|
||||
|
||||
server_policy_names.sort()
|
||||
client_policy_names.sort()
|
||||
return server_policy_names, client_policy_names
|
||||
if policy['server_policy']:
|
||||
server_policy_descriptions.append(policy_description)
|
||||
else:
|
||||
client_policy_descriptions.append(policy_description)
|
||||
|
||||
server_policy_descriptions.sort()
|
||||
client_policy_descriptions.sort()
|
||||
return server_policy_descriptions, client_policy_descriptions
|
||||
|
||||
|
||||
@staticmethod
|
||||
def load_builtin_policy(policy_name: str, json_output: bool = False) -> Optional['Policy']:
|
||||
'''Returns a Policy with the specified built-in policy name loaded, or None if no policy of that name exists.'''
|
||||
p = None
|
||||
if policy_name in Policy.BUILTIN_POLICIES:
|
||||
policy_struct = Policy.BUILTIN_POLICIES[policy_name]
|
||||
if policy_name in BUILTIN_POLICIES:
|
||||
policy_struct = BUILTIN_POLICIES[policy_name]
|
||||
p = Policy(manual_load=True, json_output=json_output)
|
||||
policy_name_without_version = policy_name[0:policy_name.rfind(' (')]
|
||||
p._name = policy_name_without_version # pylint: disable=protected-access
|
||||
@ -598,4 +604,4 @@ macs = %s
|
||||
if self._dh_modulus_sizes is not None:
|
||||
dh_modulus_sizes_str = str(self._dh_modulus_sizes)
|
||||
|
||||
return "Name: %s\nVersion: %s\nBanner: %s\nCompressions: %s\nHost Keys: %s\nOptional Host Keys: %s\nKey Exchanges: %s\nCiphers: %s\nMACs: %s\nHost Key Sizes: %s\nDH Modulus Sizes: %s\nServer Policy: %r" % (name, version, banner, compressions_str, host_keys_str, optional_host_keys_str, kex_str, ciphers_str, macs_str, hostkey_sizes_str, dh_modulus_sizes_str, self._server_policy)
|
||||
return "Name: %s\nVersion: %s\nAllow Algorithm Subset and/or Reordering: %r\nBanner: %s\nCompressions: %s\nHost Keys: %s\nOptional Host Keys: %s\nKey Exchanges: %s\nCiphers: %s\nMACs: %s\nHost Key Sizes: %s\nDH Modulus Sizes: %s\nServer Policy: %r" % (name, version, self._allow_algorithm_subset_and_reordering, banner, compressions_str, host_keys_str, optional_host_keys_str, kex_str, ciphers_str, macs_str, hostkey_sizes_str, dh_modulus_sizes_str, self._server_policy)
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (C) 2017-2023 Joe Testa (jtesta@positronsecurity.com)
|
||||
Copyright (C) 2017-2024 Joe Testa (jtesta@positronsecurity.com)
|
||||
Copyright (C) 2017 Andris Raugulis (moo@arthepsy.eu)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@ -62,7 +62,9 @@ class SSH2_KexDB: # pylint: disable=too-few-public-methods
|
||||
WARN_TAG_SIZE_96 = 'using small 96-bit tag size'
|
||||
|
||||
INFO_DEFAULT_OPENSSH_CIPHER = 'default cipher since OpenSSH 6.9'
|
||||
INFO_DEFAULT_OPENSSH_KEX = 'default key exchange since OpenSSH 6.4'
|
||||
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_90 = 'default key exchange since OpenSSH 9.0'
|
||||
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_OPENSSH70 = 'disabled in OpenSSH 7.0: https://www.openssh.com/txt/release-7.0'
|
||||
@ -71,6 +73,8 @@ class SSH2_KexDB: # pylint: disable=too-few-public-methods
|
||||
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_WITHDRAWN_PQ_ALG = 'the sntrup4591761 algorithm was withdrawn, as it may not provide strong post-quantum security'
|
||||
INFO_EXTENSION_NEGOTIATION = 'pseudo-algorithm that denotes the peer supports RFC8308 extensions'
|
||||
INFO_STRICT_KEX = 'pseudo-algorithm that denotes the peer supports a stricter key exchange method as a counter-measure to the Terrapin attack (CVE-2023-48795)'
|
||||
|
||||
# Maintains a dictionary per calling thread that yields its own copy of MASTER_DB. This prevents results from one thread polluting the results of another thread.
|
||||
DB_PER_THREAD: Dict[int, Dict[str, Dict[str, List[List[Optional[str]]]]]] = {}
|
||||
@ -79,8 +83,8 @@ class SSH2_KexDB: # pylint: disable=too-few-public-methods
|
||||
# Format: 'algorithm_name': [['version_first_appeared_in'], [reason_for_failure1, reason_for_failure2, ...], [warning1, warning2, ...], [info1, info2, ...]]
|
||||
'kex': {
|
||||
'Curve25519SHA256': [[]],
|
||||
'curve25519-sha256': [['7.4,d2018.76'], [], [], [INFO_DEFAULT_OPENSSH_KEX]],
|
||||
'curve25519-sha256@libssh.org': [['6.4,d2013.62,l10.6.0'], [], [], [INFO_DEFAULT_OPENSSH_KEX]],
|
||||
'curve25519-sha256': [['7.4,d2018.76'], [], [], [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]],
|
||||
'curve448-sha512': [[]],
|
||||
'curve448-sha512@libssh.org': [[]],
|
||||
'diffie-hellman-group14-sha1': [['3.9,d0.53,l10.6.0'], [FAIL_SHA1], [WARN_2048BIT_MODULUS]],
|
||||
@ -154,8 +158,10 @@ class SSH2_KexDB: # pylint: disable=too-few-public-methods
|
||||
'ecdh-sha2-wiRIU8TKjMZ418sMqlqtvQ==': [[], [FAIL_UNPROVEN]], # sect283k1
|
||||
'ecdh-sha2-zD/b3hu/71952ArpUG4OjQ==': [[], [FAIL_UNPROVEN, FAIL_SMALL_ECC_MODULUS]], # sect233k1
|
||||
'ecmqv-sha2': [[], [FAIL_UNPROVEN]],
|
||||
'ext-info-c': [[]], # Extension negotiation (RFC 8308)
|
||||
'ext-info-s': [[]], # Extension negotiation (RFC 8308)
|
||||
'ext-info-c': [[], [], [], [INFO_EXTENSION_NEGOTIATION]], # Extension negotiation (RFC 8308)
|
||||
'ext-info-s': [[], [], [], [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-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.
|
||||
'gss-13.3.132.0.10-sha256-*': [[], [FAIL_UNKNOWN]],
|
||||
@ -172,6 +178,7 @@ class SSH2_KexDB: # pylint: disable=too-few-public-methods
|
||||
'gss-group1-sha1-*': [[], [FAIL_1024BIT_MODULUS, FAIL_LOGJAM_ATTACK, FAIL_SHA1]],
|
||||
'gss-nistp256-sha256-*': [[], [FAIL_NSA_BACKDOORED_CURVE]],
|
||||
'gss-nistp384-sha256-*': [[], [FAIL_NSA_BACKDOORED_CURVE]],
|
||||
'gss-nistp384-sha384-*': [[], [FAIL_NSA_BACKDOORED_CURVE]],
|
||||
'gss-nistp521-sha512-*': [[], [FAIL_NSA_BACKDOORED_CURVE]],
|
||||
'kexAlgoCurve25519SHA256': [[]],
|
||||
'kexAlgoDH14SHA1': [[], [FAIL_SHA1], [WARN_2048BIT_MODULUS]],
|
||||
@ -186,7 +193,7 @@ class SSH2_KexDB: # pylint: disable=too-few-public-methods
|
||||
'rsa2048-sha256': [[], [], [WARN_2048BIT_MODULUS]],
|
||||
'sm2kep-sha2-nistp256': [[], [FAIL_NSA_BACKDOORED_CURVE, FAIL_UNTRUSTED]],
|
||||
'sntrup4591761x25519-sha512@tinyssh.org': [['8.0', '8.4'], [], [WARN_EXPERIMENTAL], [INFO_WITHDRAWN_PQ_ALG]],
|
||||
'sntrup761x25519-sha512@openssh.com': [['8.5'], [], []],
|
||||
'sntrup761x25519-sha512@openssh.com': [['8.5'], [], [], [INFO_DEFAULT_OPENSSH_KEX_90]],
|
||||
'x25519-kyber-512r3-sha256-d00@amazon.com': [[]],
|
||||
'x25519-kyber512-sha512@aws.amazon.com': [[]],
|
||||
},
|
||||
@ -217,7 +224,7 @@ class SSH2_KexDB: # pylint: disable=too-few-public-methods
|
||||
'null': [[], [FAIL_PLAINTEXT]],
|
||||
'pgp-sign-dss': [[], [FAIL_1024BIT_MODULUS]],
|
||||
'pgp-sign-rsa': [[], [FAIL_1024BIT_MODULUS]],
|
||||
'rsa-sha2-256': [['7.2']],
|
||||
'rsa-sha2-256': [['7.2,d2020.79']],
|
||||
'rsa-sha2-256-cert-v01@openssh.com': [['7.8']],
|
||||
'rsa-sha2-512': [['7.2']],
|
||||
'rsa-sha2-512-cert-v01@openssh.com': [['7.8']],
|
||||
@ -236,7 +243,7 @@ class SSH2_KexDB: # pylint: disable=too-few-public-methods
|
||||
'ssh-dss-sha256@ssh.com': [[], [FAIL_1024BIT_MODULUS]],
|
||||
'ssh-dss-sha384@ssh.com': [[], [FAIL_1024BIT_MODULUS]],
|
||||
'ssh-dss-sha512@ssh.com': [[], [FAIL_1024BIT_MODULUS]],
|
||||
'ssh-ed25519': [['6.5,l10.7.0']],
|
||||
'ssh-ed25519': [['6.5,d2020.79,l10.7.0']],
|
||||
'ssh-ed25519-cert-v01@openssh.com': [['6.5']],
|
||||
'ssh-ed448': [[]],
|
||||
'ssh-ed448-cert-v01@openssh.com': [[], [], [], [INFO_NEVER_IMPLEMENTED_IN_OPENSSH]],
|
||||
@ -290,6 +297,7 @@ class SSH2_KexDB: # pylint: disable=too-few-public-methods
|
||||
'aes128-ctr': [['3.7,d0.52,l10.4.1']],
|
||||
'aes128-gcm': [[]],
|
||||
'aes128-gcm@openssh.com': [['6.2']],
|
||||
'aes128-ocb@libassh.org': [[], [], [WARN_CIPHER_MODE]],
|
||||
'aes192-cbc': [['2.3.0,l10.2', '6.6', None], [], [WARN_CIPHER_MODE]],
|
||||
'aes192-ctr': [['3.7,l10.4.1']],
|
||||
'aes192-gcm@openssh.com': [[], [], [], [INFO_NEVER_IMPLEMENTED_IN_OPENSSH]],
|
||||
@ -329,7 +337,7 @@ class SSH2_KexDB: # pylint: disable=too-few-public-methods
|
||||
'cast128-ecb': [[], [FAIL_CAST], [WARN_CIPHER_MODE]],
|
||||
'cast128-ofb': [[], [FAIL_CAST], [WARN_CIPHER_MODE]],
|
||||
'chacha20-poly1305': [[], [], [], [INFO_DEFAULT_OPENSSH_CIPHER]],
|
||||
'chacha20-poly1305@openssh.com': [['6.5'], [], [], [INFO_DEFAULT_OPENSSH_CIPHER]],
|
||||
'chacha20-poly1305@openssh.com': [['6.5,d2020.79'], [], [], [INFO_DEFAULT_OPENSSH_CIPHER]],
|
||||
'crypticore128@ssh.com': [[], [FAIL_UNPROVEN]],
|
||||
'des-cbc': [[], [FAIL_DES], [WARN_CIPHER_MODE, WARN_BLOCK_SIZE]],
|
||||
'des-cfb': [[], [FAIL_DES], [WARN_CIPHER_MODE, WARN_BLOCK_SIZE]],
|
||||
|
@ -27,6 +27,7 @@ import concurrent.futures
|
||||
import copy
|
||||
import getopt
|
||||
import json
|
||||
import multiprocessing
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
@ -39,11 +40,12 @@ from typing import cast, Callable, Optional, Union, Any # noqa: F401
|
||||
from ssh_audit.globals import SNAP_PACKAGE
|
||||
from ssh_audit.globals import SNAP_PERMISSIONS_ERROR
|
||||
from ssh_audit.globals import VERSION
|
||||
from ssh_audit.globals import WINDOWS_MAN_PAGE
|
||||
from ssh_audit.globals import BUILTIN_MAN_PAGE
|
||||
from ssh_audit.algorithm import Algorithm
|
||||
from ssh_audit.algorithms import Algorithms
|
||||
from ssh_audit.auditconf import AuditConf
|
||||
from ssh_audit.banner import Banner
|
||||
from ssh_audit.dheat import DHEat
|
||||
from ssh_audit import exitcodes
|
||||
from ssh_audit.fingerprint import Fingerprint
|
||||
from ssh_audit.gextest import GEXTest
|
||||
@ -96,19 +98,37 @@ def usage(uout: OutputBuffer, err: Optional[str] = None) -> None:
|
||||
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')
|
||||
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')
|
||||
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 --threads\n to control concurrent scans.')
|
||||
uout.info(' --threads=<threads> number of threads to use when scanning multiple\n targets (-T/--targets) (default: 32)')
|
||||
@ -364,11 +384,8 @@ def output_recommendations(out: OutputBuffer, algs: Algorithms, algorithm_recomm
|
||||
for cve_list in VersionVulnerabilityDB.CVE['PuTTY']:
|
||||
vuln_version = float(cve_list[1])
|
||||
cvssv2_severity = cve_list[4]
|
||||
|
||||
if vuln_version > max_vuln_version:
|
||||
max_vuln_version = vuln_version
|
||||
if cvssv2_severity > max_cvssv2_severity:
|
||||
max_cvssv2_severity = cvssv2_severity
|
||||
max_vuln_version = max(vuln_version, max_vuln_version)
|
||||
max_cvssv2_severity = max(cvssv2_severity, max_cvssv2_severity)
|
||||
|
||||
fn = out.warn
|
||||
if max_cvssv2_severity > 8.0:
|
||||
@ -431,7 +448,7 @@ def output_recommendations(out: OutputBuffer, algs: Algorithms, algorithm_recomm
|
||||
|
||||
|
||||
# Output additional information & notes.
|
||||
def output_info(out: OutputBuffer, software: Optional['Software'], client_audit: bool, any_problems: bool, is_json_output: bool) -> None:
|
||||
def output_info(out: OutputBuffer, software: Optional['Software'], client_audit: bool, any_problems: bool, is_json_output: bool, additional_notes: List[str]) -> None:
|
||||
with out:
|
||||
# Tell user that PuTTY cannot be hardened at the protocol-level.
|
||||
if client_audit and (software is not None) and (software.product == Product.PuTTY):
|
||||
@ -441,17 +458,102 @@ def output_info(out: OutputBuffer, software: Optional['Software'], client_audit:
|
||||
if any_problems:
|
||||
out.warn('(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>')
|
||||
|
||||
# Add any additional notes.
|
||||
for additional_note in additional_notes:
|
||||
if len(additional_note) > 0:
|
||||
out.warn("(nfo) %s" % additional_note)
|
||||
|
||||
if not out.is_section_empty() and not is_json_output:
|
||||
out.head('# additional info')
|
||||
out.flush_section()
|
||||
out.sep()
|
||||
|
||||
|
||||
def post_process_findings(banner: Optional[Banner], algs: Algorithms) -> List[str]:
|
||||
'''Perform post-processing on scan results before reporting them to the user. Returns a list of algorithms that should not be recommended'''
|
||||
def post_process_findings(banner: Optional[Banner], algs: Algorithms, client_audit: bool, dh_rate_test_notes: str) -> Tuple[List[str], List[str]]:
|
||||
'''Perform post-processing on scan results before reporting them to the user. Returns a list of algorithms that should not be recommended and a list of notes.'''
|
||||
|
||||
def _add_terrapin_warning(db: Dict[str, Dict[str, List[List[Optional[str]]]]], category: str, algorithm_name: str) -> None:
|
||||
'''Adds a warning regarding the Terrapin vulnerability for the specified algorithm.'''
|
||||
# Ensure that a slot for warnings exists for this algorithm.
|
||||
while len(db[category][algorithm_name]) < 3:
|
||||
db[category][algorithm_name].append([])
|
||||
|
||||
db[category][algorithm_name][2].append("vulnerable to the Terrapin attack (CVE-2023-48795), allowing message prefix truncation")
|
||||
|
||||
def _get_chacha_ciphers_enabled(algs: Algorithms) -> List[str]:
|
||||
'''Returns a list of chacha20-poly1305 ciphers that the peer supports.'''
|
||||
ret = []
|
||||
|
||||
if algs.ssh2kex is not None:
|
||||
ciphers_supported = algs.ssh2kex.client.encryption if client_audit else algs.ssh2kex.server.encryption
|
||||
for cipher in ciphers_supported:
|
||||
if cipher.startswith("chacha20-poly1305"):
|
||||
ret.append(cipher)
|
||||
|
||||
return ret
|
||||
|
||||
def _get_chacha_ciphers_not_enabled(db: Dict[str, Dict[str, List[List[Optional[str]]]]], algs: Algorithms) -> List[str]:
|
||||
'''Returns a list of all chacha20-poly1305 in our algorithm database.'''
|
||||
ret = []
|
||||
|
||||
for cipher in db["enc"]:
|
||||
if cipher.startswith("chacha20-poly1305") and cipher not in _get_chacha_ciphers_enabled(algs):
|
||||
ret.append(cipher)
|
||||
|
||||
return ret
|
||||
|
||||
def _get_cbc_ciphers_enabled(algs: Algorithms) -> List[str]:
|
||||
'''Returns a list of CBC ciphers that the peer supports.'''
|
||||
ret = []
|
||||
|
||||
if algs.ssh2kex is not None:
|
||||
ciphers_supported = algs.ssh2kex.client.encryption if client_audit else algs.ssh2kex.server.encryption
|
||||
for cipher in ciphers_supported:
|
||||
if cipher.endswith("-cbc") or cipher.endswith("-cbc@openssh.org") or cipher.endswith("-cbc@ssh.com") or cipher == "rijndael-cbc@lysator.liu.se":
|
||||
ret.append(cipher)
|
||||
|
||||
return ret
|
||||
|
||||
def _get_cbc_ciphers_not_enabled(db: Dict[str, Dict[str, List[List[Optional[str]]]]], algs: Algorithms) -> List[str]:
|
||||
'''Returns a list of all CBC ciphers in our algorithm database.'''
|
||||
ret = []
|
||||
|
||||
for cipher in db["enc"]:
|
||||
if (cipher.endswith("-cbc") or cipher.endswith("-cbc@openssh.org") or cipher.endswith("-cbc@ssh.com") or cipher == "rijndael-cbc@lysator.liu.se") and cipher not in _get_cbc_ciphers_enabled(algs):
|
||||
ret.append(cipher)
|
||||
|
||||
return ret
|
||||
|
||||
def _get_etm_macs_enabled(algs: Algorithms) -> List[str]:
|
||||
'''Returns a list of ETM MACs that the peer supports.'''
|
||||
ret = []
|
||||
|
||||
if algs.ssh2kex is not None:
|
||||
macs_supported = algs.ssh2kex.client.mac if client_audit else algs.ssh2kex.server.mac
|
||||
for mac in macs_supported:
|
||||
if mac.endswith("-etm@openssh.com"):
|
||||
ret.append(mac)
|
||||
|
||||
return ret
|
||||
|
||||
def _get_etm_macs_not_enabled(db: Dict[str, Dict[str, List[List[Optional[str]]]]], algs: Algorithms) -> List[str]:
|
||||
'''Returns a list of ETM MACs in our algorithm database.'''
|
||||
ret = []
|
||||
|
||||
for mac in db["mac"]:
|
||||
if mac.endswith("-etm@openssh.com") and mac not in _get_etm_macs_enabled(algs):
|
||||
ret.append(mac)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
algorithm_recommendation_suppress_list = []
|
||||
algs_to_note = []
|
||||
|
||||
|
||||
#
|
||||
# Post-processing of the OpenSSH diffie-hellman-group-exchange-sha256 fallback mechanism bug/feature.
|
||||
#
|
||||
|
||||
# If the server is OpenSSH, and the diffie-hellman-group-exchange-sha256 key exchange was found with modulus size 2048, add a note regarding the bug that causes the server to support 2048-bit moduli no matter the configuration.
|
||||
if (algs.ssh2kex is not None and 'diffie-hellman-group-exchange-sha256' in algs.ssh2kex.kex_algorithms and 'diffie-hellman-group-exchange-sha256' in algs.ssh2kex.dh_modulus_sizes() and algs.ssh2kex.dh_modulus_sizes()['diffie-hellman-group-exchange-sha256'] == 2048) and (banner is not None and banner.software is not None and banner.software.find('OpenSSH') != -1):
|
||||
@ -466,11 +568,65 @@ def post_process_findings(banner: Optional[Banner], algs: Algorithms) -> List[st
|
||||
# Ensure that this algorithm doesn't appear in the recommendations section since the user cannot control this OpenSSH bug.
|
||||
algorithm_recommendation_suppress_list.append('diffie-hellman-group-exchange-sha256')
|
||||
|
||||
return algorithm_recommendation_suppress_list
|
||||
# Check for the Terrapin vulnerability (CVE-2023-48795), and mark the vulnerable algorithms.
|
||||
kex_strict_marker = False
|
||||
if algs.ssh2kex is not None and \
|
||||
((client_audit and 'kex-strict-c-v00@openssh.com' in algs.ssh2kex.kex_algorithms) or (not client_audit and 'kex-strict-s-v00@openssh.com' in algs.ssh2kex.kex_algorithms)): # Strict KEX marker is present.
|
||||
kex_strict_marker = True
|
||||
|
||||
db = SSH2_KexDB.get_db()
|
||||
|
||||
|
||||
#
|
||||
# Post-processing of algorithms related to the Terrapin vulnerability (CVE-2023-48795).
|
||||
#
|
||||
|
||||
# Without the strict KEX marker, the chacha20-poly1305 ciphers are always vulnerable.
|
||||
for chacha_cipher in _get_chacha_ciphers_enabled(algs):
|
||||
if kex_strict_marker:
|
||||
# Inform the user that the target is correctly configured, but another peer may still choose this algorithm without using strict KEX negotiation, which would still result in vulnerability.
|
||||
algs_to_note.append(chacha_cipher)
|
||||
else:
|
||||
_add_terrapin_warning(db, "enc", chacha_cipher)
|
||||
|
||||
cbc_ciphers_enabled = _get_cbc_ciphers_enabled(algs)
|
||||
etm_macs_enabled = _get_etm_macs_enabled(algs)
|
||||
|
||||
# Without the strict KEX marker, if at least one CBC cipher and at least one ETM MAC is supported, mark them all as vulnerable.
|
||||
if len(cbc_ciphers_enabled) > 0 and len(etm_macs_enabled) > 0:
|
||||
for cipher in cbc_ciphers_enabled:
|
||||
if kex_strict_marker:
|
||||
# Inform the user that the target is correctly configured, but another peer may still choose this algorithm without using strict KEX negotiation, which would still result in vulnerability.
|
||||
algs_to_note.append(cipher)
|
||||
else:
|
||||
_add_terrapin_warning(db, "enc", cipher)
|
||||
|
||||
for mac in etm_macs_enabled:
|
||||
if kex_strict_marker:
|
||||
# Inform the user that the target is correctly configured, but another peer may still choose this algorithm without using strict KEX negotiation, which would still result in vulnerability.
|
||||
algs_to_note.append(mac)
|
||||
else:
|
||||
_add_terrapin_warning(db, "mac", mac)
|
||||
|
||||
# Return a note telling the user that, while this target is properly configured, if connected to a vulnerable peer, then a vulnerable connection is still possible.
|
||||
additional_notes = []
|
||||
if len(algs_to_note) > 0:
|
||||
additional_notes.append("Be aware that, while this target properly supports the strict key exchange method (via the kex-strict-?-v00@openssh.com marker) needed to protect against the Terrapin vulnerability (CVE-2023-48795), all peers must also support this feature as well, otherwise the vulnerability will still be present. The following algorithms would allow an unpatched peer to create vulnerable SSH channels with this target: %s. If any CBC ciphers are in this list, you may remove them while leaving the *-etm@openssh.com MACs in place; these MACs are fine while paired with non-CBC cipher types." % ", ".join(algs_to_note))
|
||||
|
||||
# Add the chacha ciphers, CBC ciphers, and ETM MACs to the recommendation suppression list if they are not enabled on the server. That way they are not recommended to the user to enable if they were explicitly disabled to handle the Terrapin vulnerability. However, they can still be recommended for disabling.
|
||||
algorithm_recommendation_suppress_list += _get_chacha_ciphers_not_enabled(db, algs)
|
||||
algorithm_recommendation_suppress_list += _get_cbc_ciphers_not_enabled(db, algs)
|
||||
algorithm_recommendation_suppress_list += _get_etm_macs_not_enabled(db, algs)
|
||||
|
||||
# Append any notes related to the DH rate test.
|
||||
if len(dh_rate_test_notes) > 0:
|
||||
additional_notes.append(dh_rate_test_notes)
|
||||
|
||||
return algorithm_recommendation_suppress_list, additional_notes
|
||||
|
||||
|
||||
# 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) -> int:
|
||||
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:
|
||||
|
||||
program_retval = exitcodes.GOOD
|
||||
client_audit = client_host is not None # If set, this is a client audit.
|
||||
@ -478,7 +634,7 @@ def output(out: OutputBuffer, aconf: AuditConf, banner: Optional[Banner], header
|
||||
algs = Algorithms(pkm, kex)
|
||||
|
||||
# Perform post-processing on the findings to make final adjustments before outputting the results.
|
||||
algorithm_recommendation_suppress_list = post_process_findings(banner, algs)
|
||||
algorithm_recommendation_suppress_list, additional_notes = post_process_findings(banner, algs, client_audit, dh_rate_test_notes)
|
||||
|
||||
with out:
|
||||
if print_target:
|
||||
@ -558,12 +714,12 @@ def output(out: OutputBuffer, aconf: AuditConf, banner: Optional[Banner], header
|
||||
|
||||
output_fingerprints(out, algs, aconf.json)
|
||||
perfect_config = output_recommendations(out, algs, algorithm_recommendation_suppress_list, software, aconf.json, maxlen)
|
||||
output_info(out, software, client_audit, not perfect_config, aconf.json)
|
||||
output_info(out, software, client_audit, not perfect_config, aconf.json, additional_notes)
|
||||
|
||||
if aconf.json:
|
||||
out.reset()
|
||||
# Build & write the JSON struct.
|
||||
out.info(json.dumps(build_struct(aconf.host + ":" + str(aconf.port), banner, cves, kex=kex, client_host=client_host, software=software, algorithms=algs, algorithm_recommendation_suppress_list=algorithm_recommendation_suppress_list), indent=4 if aconf.json_print_indent else None, sort_keys=True))
|
||||
out.info(json.dumps(build_struct(aconf.host + ":" + str(aconf.port), banner, cves, kex=kex, client_host=client_host, software=software, algorithms=algs, algorithm_recommendation_suppress_list=algorithm_recommendation_suppress_list, additional_notes=additional_notes), indent=4 if aconf.json_print_indent else None, sort_keys=True))
|
||||
elif len(unknown_algorithms) > 0: # If we encountered any unknown algorithms, ask the user to report them.
|
||||
out.warn("\n\n!!! WARNING: unknown algorithm(s) found!: %s. Please email the full output above to the maintainer (jtesta@positronsecurity.com), or create a Github issue at <https://github.com/jtesta/ssh-audit/issues>.\n" % ','.join(unknown_algorithms))
|
||||
|
||||
@ -662,24 +818,26 @@ def get_algorithm_recommendations(algs: Optional[Algorithms], algorithm_recommen
|
||||
return ret
|
||||
|
||||
|
||||
def list_policies(out: OutputBuffer) -> None:
|
||||
def list_policies(out: OutputBuffer, verbose: bool) -> None:
|
||||
'''Prints a list of server & client policies.'''
|
||||
|
||||
server_policy_names, client_policy_names = Policy.list_builtin_policies()
|
||||
server_policy_names, client_policy_names = Policy.list_builtin_policies(verbose)
|
||||
|
||||
if len(server_policy_names) > 0:
|
||||
out.head('\nServer policies:\n')
|
||||
out.info(" * \"%s\"" % "\"\n * \"".join(server_policy_names))
|
||||
out.info(" * %s" % "\n * ".join(server_policy_names))
|
||||
|
||||
if len(client_policy_names) > 0:
|
||||
out.head('\nClient policies:\n')
|
||||
out.info(" * \"%s\"" % "\"\n * \"".join(client_policy_names))
|
||||
out.info(" * %s" % "\n * ".join(client_policy_names))
|
||||
|
||||
out.sep()
|
||||
if len(server_policy_names) == 0 and len(client_policy_names) == 0:
|
||||
out.fail("Error: no built-in policies found!")
|
||||
else:
|
||||
out.info("\nHint: Use -P and provide the full name of a policy to run a policy scan with.\n")
|
||||
out.info("Hint: Use -L -v to also see the change log for each policy.\n")
|
||||
out.info("Note: the general OpenSSH policies apply to the official releases only. OS distributions may back-port changes that cause failures (for example, Debian 11 back-ported the strict KEX mode into their package of OpenSSH v8.4, whereas it was only officially added to OpenSSH v9.6 and later). In these cases, consider creating a custom policy (-M option).\n")
|
||||
out.write()
|
||||
|
||||
|
||||
@ -723,12 +881,17 @@ def process_commandline(out: OutputBuffer, args: List[str], usage_cb: Callable[.
|
||||
aconf = AuditConf()
|
||||
|
||||
enable_colors = not any(i in args for i in ['--no-colors', '-n'])
|
||||
|
||||
# Disable colors if the NO_COLOR environment variable is set.
|
||||
if "NO_COLOR" in os.environ:
|
||||
enable_colors = False
|
||||
|
||||
aconf.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=']
|
||||
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))
|
||||
@ -816,6 +979,12 @@ def process_commandline(out: OutputBuffer, args: List[str], usage_cb: Callable[.
|
||||
usage_cb(out, '{} {} {} is not valid'.format(o, bits_left_bound, bits_right_bound))
|
||||
|
||||
aconf.gex_test = a
|
||||
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 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:
|
||||
@ -828,7 +997,7 @@ def process_commandline(out: OutputBuffer, args: List[str], usage_cb: Callable[.
|
||||
return aconf
|
||||
|
||||
if aconf.list_policies:
|
||||
list_policies(out)
|
||||
list_policies(out, aconf.verbose)
|
||||
sys.exit(exitcodes.GOOD)
|
||||
|
||||
if aconf.client_audit is False and aconf.target_file is None:
|
||||
@ -899,7 +1068,7 @@ def process_commandline(out: OutputBuffer, args: List[str], usage_cb: Callable[.
|
||||
return aconf
|
||||
|
||||
|
||||
def build_struct(target_host: str, banner: Optional['Banner'], cves: List[Dict[str, Union[str, float]]], 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) -> Any: # pylint: disable=too-many-arguments
|
||||
def build_struct(target_host: str, banner: Optional['Banner'], cves: List[Dict[str, Union[str, float]]], 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 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.'''
|
||||
@ -1067,6 +1236,9 @@ def build_struct(target_host: str, banner: Optional['Banner'], cves: List[Dict[s
|
||||
# Add in the recommendations.
|
||||
res['recommendations'] = get_algorithm_recommendations(algorithms, algorithm_recommendation_suppress_list, software, for_server=True)
|
||||
|
||||
# Add in the additional notes.
|
||||
res['additional_notes'] = additional_notes
|
||||
|
||||
return res
|
||||
|
||||
|
||||
@ -1147,6 +1319,14 @@ def audit(out: OutputBuffer, aconf: AuditConf, sshv: Optional[int] = None, print
|
||||
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:
|
||||
DHEat(out, aconf, banner, kex).run()
|
||||
return exitcodes.GOOD
|
||||
elif aconf.conn_rate_test_enabled:
|
||||
DHEat.dh_rate_test(out, aconf, kex, 0, 0, 0)
|
||||
return exitcodes.GOOD
|
||||
|
||||
dh_rate_test_notes = ""
|
||||
if aconf.client_audit is False:
|
||||
HostKeyTest.run(out, s, kex)
|
||||
if aconf.gex_test != '':
|
||||
@ -1154,9 +1334,16 @@ def audit(out: OutputBuffer, aconf: AuditConf, sshv: Optional[int] = None, print
|
||||
else:
|
||||
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)
|
||||
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):
|
||||
@ -1272,23 +1459,21 @@ def target_worker_thread(host: str, port: int, shared_aconf: AuditConf) -> Tuple
|
||||
return ret, string_output
|
||||
|
||||
|
||||
def windows_manual(out: OutputBuffer) -> int:
|
||||
'''Prints the man page on Windows. Returns an exitcodes.* flag.'''
|
||||
def builtin_manual(out: OutputBuffer) -> int:
|
||||
'''Prints the man page (Docker, PyPI, Snap, and Windows builds only). Returns an exitcodes.* flag.'''
|
||||
|
||||
retval = exitcodes.GOOD
|
||||
|
||||
if sys.platform != 'win32':
|
||||
out.fail("The '-m' and '--manual' parameters are reserved for use on Windows only.\nUsers of other operating systems should read the man page.")
|
||||
retval = exitcodes.FAILURE
|
||||
return retval
|
||||
builtin_man_page = BUILTIN_MAN_PAGE
|
||||
if builtin_man_page == "":
|
||||
out.fail("The '-m' and '--manual' parameters are reserved for use in Docker, PyPI, Snap,\nand Windows builds only. Users of other platforms should read the system man\npage.")
|
||||
return exitcodes.FAILURE
|
||||
|
||||
# If colors are disabled, strip the ANSI color codes from the man page.
|
||||
windows_man_page = WINDOWS_MAN_PAGE
|
||||
if not out.use_colors:
|
||||
windows_man_page = re.sub(r'\x1b\[\d+?m', '', windows_man_page)
|
||||
builtin_man_page = re.sub(r'\x1b\[\d+?m', '', builtin_man_page)
|
||||
|
||||
out.info(windows_man_page)
|
||||
return retval
|
||||
out.info(builtin_man_page)
|
||||
return exitcodes.GOOD
|
||||
|
||||
|
||||
def get_permitted_syntax_for_gex_test() -> Dict[str, str]:
|
||||
@ -1382,7 +1567,7 @@ def main() -> int:
|
||||
# to output a plain text version of the man page.
|
||||
if (sys.platform == 'win32') and ('colorama' not in sys.modules):
|
||||
out.use_colors = False
|
||||
retval = windows_manual(out)
|
||||
retval = builtin_manual(out)
|
||||
out.write()
|
||||
sys.exit(retval)
|
||||
|
||||
@ -1447,8 +1632,9 @@ def main() -> int:
|
||||
|
||||
|
||||
if __name__ == '__main__': # pragma: nocover
|
||||
exit_code = exitcodes.GOOD
|
||||
multiprocessing.freeze_support() # Needed for PyInstaller (Windows) builds.
|
||||
|
||||
exit_code = exitcodes.GOOD
|
||||
try:
|
||||
exit_code = main()
|
||||
except Exception:
|
||||
|
@ -246,8 +246,7 @@ class SSH_Socket(ReadBuf, WriteBuf):
|
||||
|
||||
def send_banner(self, banner: str) -> None:
|
||||
self.send(banner.encode() + b'\r\n')
|
||||
if self.__state < self.SM_BANNER_SENT:
|
||||
self.__state = self.SM_BANNER_SENT
|
||||
self.__state = max(self.__state, self.SM_BANNER_SENT)
|
||||
|
||||
def ensure_read(self, size: int) -> None:
|
||||
while self.unread_len < size:
|
||||
|
@ -122,7 +122,7 @@ class VersionVulnerabilityDB: # pylint: disable=too-few-public-methods
|
||||
['2.1', '4.1p1', 1, 'CVE-2005-2798', 5.0, 'leak data about authentication credentials'],
|
||||
['3.5', '3.5p1', 1, 'CVE-2004-2760', 6.8, 'leak data through different connection states'],
|
||||
['2.3', '3.7.1p2', 1, 'CVE-2004-2069', 5.0, 'cause DoS via large number of connections (slot exhaustion)'],
|
||||
['3.0', '3.4p1', 1, 'CVE-2004-0175', 4.3, 'leak data through directoy traversal'],
|
||||
['3.0', '3.4p1', 1, 'CVE-2004-0175', 4.3, 'leak data through directory traversal'],
|
||||
['1.2', '3.9p1', 1, 'CVE-2003-1562', 7.6, 'leak data about authentication credentials'],
|
||||
['3.1p1', '3.7.1p1', 1, 'CVE-2003-0787', 7.5, 'privilege escalation via modifying stack'],
|
||||
['3.1p1', '3.7.1p1', 1, 'CVE-2003-0786', 10.0, 'privilege escalation via bypassing authentication'],
|
||||
|
63
ssh-audit.1
63
ssh-audit.1
@ -1,4 +1,4 @@
|
||||
.TH SSH-AUDIT 1 "March 13, 2022"
|
||||
.TH SSH-AUDIT 1 "April 18, 2024"
|
||||
.SH NAME
|
||||
\fBssh-audit\fP \- SSH server & client configuration auditor
|
||||
.SH SYNOPSIS
|
||||
@ -46,11 +46,21 @@ Enables grepable output.
|
||||
.br
|
||||
Starts a server on port 2222 to audit client software configuration. Use -p/--port=<port> to change port and -t/--timeout=<secs> to change listen timeout.
|
||||
|
||||
.TP
|
||||
.B \-\-conn\-rate\-test=N[:max_rate]
|
||||
.br
|
||||
Performs a connection rate test (useful for collecting metrics related to susceptibility of the DHEat vulnerability [CVE-2002-20001]). A successful connection is counted when the server returns a valid SSH banner. Testing is conducted with N concurrent sockets with an optional maximum rate of connections per second.
|
||||
|
||||
.TP
|
||||
.B -d, \-\-debug
|
||||
.br
|
||||
Enable debug output.
|
||||
|
||||
.TP
|
||||
.B \-\-dheat=N[:kex[:e_len]]
|
||||
.br
|
||||
Run the DHEat DoS attack (CVE-2002-20001) against the target server (which will consume all available CPU resources). The number of concurrent sockets, N, needed to achieve this effect will be highly dependent on the CPU resources available on the target, as well as the latency between the source and target machines. The key exchange is automatically chosen based on which would cause maximum effect, unless explicitly chosen in the second field. Lastly, an (experimental) option allows the length in bytes of the fake e value sent to the server to be specified in the third field. Normally, the length of e is roughly the length of the modulus of the Diffie-Hellman exchange (hence, an 8192-bit / 1024-byte value of e is sent in each connection when targeting the diffie-hellman-group18-sha512 algorithm). Instead, it was observed that many SSH implementations accept small values, such as 4 bytes; this results in a much more network-efficient attack.
|
||||
|
||||
.TP
|
||||
.B -g, \-\-gex-test=<x[,y,...] | min1:pref1:max1[,min2:pref2:max2,...] | x-y[:step]>
|
||||
.br
|
||||
@ -94,7 +104,7 @@ Specify the minimum output level. Default is info.
|
||||
.TP
|
||||
.B -L, \-\-list-policies
|
||||
.br
|
||||
List all official, built-in policies for common systems. Their full names can then be passed to -P/--policy.
|
||||
List all official, built-in policies for common systems. Their full names can then be passed to -P/--policy. Add \-v to \-L to view policy change logs.
|
||||
|
||||
.TP
|
||||
.B \-\-lookup=<alg1,alg2,...>
|
||||
@ -104,7 +114,7 @@ Look up the security information of an algorithm(s) in the internal database. D
|
||||
.TP
|
||||
.B -m, \-\-manual
|
||||
.br
|
||||
Print the man page (Windows only).
|
||||
Print the man page (Docker, PyPI, Snap, and Windows builds only).
|
||||
|
||||
.TP
|
||||
.B -M, \-\-make-policy=<custom_policy.txt>
|
||||
@ -114,7 +124,7 @@ Creates a policy based on the target server. Useful when other servers should b
|
||||
.TP
|
||||
.B -n, \-\-no-colors
|
||||
.br
|
||||
Disable color output.
|
||||
Disable color output. Automatically set when the NO_COLOR environment variable is set.
|
||||
|
||||
.TP
|
||||
.B -p, \-\-port=<port>
|
||||
@ -126,6 +136,11 @@ The TCP port to connect to when auditing a server, or the port to listen on when
|
||||
.br
|
||||
Runs a policy audit against a target using the specified policy (see \fBPOLICY AUDIT\fP section for detailed description of this mode of operation). Combine with -c/--client-audit to audit a client configuration instead of a server. Use -L/--list-policies to list all official, built-in policies for common systems.
|
||||
|
||||
.TP
|
||||
.B \-\-skip\-rate\-test
|
||||
.br
|
||||
Skips the connection rate test during standard audits. By default, a few dozen TCP connections are created with the target host to see if connection throttling is implemented (this can safely infer whether the target is vulnerable to the DHEat attack; see CVE-2002-20001).
|
||||
|
||||
.TP
|
||||
.B -t, \-\-timeout=<secs>
|
||||
.br
|
||||
@ -273,6 +288,46 @@ ssh-audit targetserver --gex-test=0-5120:1024
|
||||
.fi
|
||||
.RE
|
||||
|
||||
.LP
|
||||
To run the DHEat DoS attack (monitor the target server's CPU usage to determine the optimal number of concurrent sockets):
|
||||
.RS
|
||||
.nf
|
||||
ssh-audit targetserver --dheat=10
|
||||
.fi
|
||||
.RE
|
||||
|
||||
.LP
|
||||
To run the DHEat attack and manually target the diffie-hellman-group-exchange-sha256 algorithm:
|
||||
.RS
|
||||
.nf
|
||||
ssh-audit targetserver --dheat=10:diffie-hellman-group-exchange-sha256
|
||||
.fi
|
||||
.RE
|
||||
|
||||
.LP
|
||||
To run the DHEat attack and manually target the diffie-hellman-group-exchange-sha256 algorithm with a very small length of e (resulting in the same effect but without having to send large packets):
|
||||
.RS
|
||||
.nf
|
||||
ssh-audit targetserver --dheat=10:diffie-hellman-group-exchange-sha256:4
|
||||
.fi
|
||||
.RE
|
||||
|
||||
.LP
|
||||
To test the number of successful connections per second that can be created with the target using 8 parallel threads (useful for detecting whether connection throttling is implemented by the target):
|
||||
.RS
|
||||
.nf
|
||||
ssh-audit targetserver --conn-rate-test=8
|
||||
.fi
|
||||
.RE
|
||||
|
||||
.LP
|
||||
To use 8 parallel threads to create up to 100 connections per second with the target (useful for understanding how much CPU load is caused on the target simply from handling new connections vs excess modular exponentiation when performing the DHEat attack):
|
||||
.RS
|
||||
.nf
|
||||
ssh-audit targetserver --conn-rate-test=8:100
|
||||
.fi
|
||||
.RE
|
||||
|
||||
.SH RETURN VALUES
|
||||
When a successful connection is made and all algorithms are rated as "good", \fBssh-audit\fP returns 0. Other possible return values are:
|
||||
|
||||
|
17
ssh-audit.py
17
ssh-audit.py
@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
"""src/ssh_audit/ssh_audit.py wrapper for backwards compatibility"""
|
||||
|
||||
import multiprocessing
|
||||
import sys
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
@ -10,12 +11,14 @@ sys.path.insert(0, str(Path(__file__).resolve().parent / "src"))
|
||||
from ssh_audit.ssh_audit import main # noqa: E402
|
||||
from ssh_audit import exitcodes # noqa: E402
|
||||
|
||||
exit_code = exitcodes.GOOD
|
||||
if __name__ == "__main__":
|
||||
multiprocessing.freeze_support() # Needed for PyInstaller (Windows) builds.
|
||||
|
||||
try:
|
||||
exit_code = main()
|
||||
except Exception:
|
||||
exit_code = exitcodes.UNKNOWN_ERROR
|
||||
print(traceback.format_exc())
|
||||
exit_code = exitcodes.GOOD
|
||||
try:
|
||||
exit_code = main()
|
||||
except Exception:
|
||||
exit_code = exitcodes.UNKNOWN_ERROR
|
||||
print(traceback.format_exc())
|
||||
|
||||
sys.exit(exit_code)
|
||||
sys.exit(exit_code)
|
||||
|
@ -73,6 +73,7 @@ class _VirtualSocket:
|
||||
self.rdata = []
|
||||
self.sdata = []
|
||||
self.errors = {}
|
||||
self.blocking = False
|
||||
self.gsock = _VirtualGlobalSocket(self)
|
||||
|
||||
def _check_err(self, method):
|
||||
@ -83,12 +84,18 @@ class _VirtualSocket:
|
||||
def connect(self, address):
|
||||
return self._connect(address, False)
|
||||
|
||||
def connect_ex(self, address):
|
||||
return self.connect(address)
|
||||
|
||||
def _connect(self, address, ret=True):
|
||||
self.peer_address = address
|
||||
self._connected = True
|
||||
self._check_err('connect')
|
||||
return self if ret else None
|
||||
|
||||
def setblocking(self, r: bool):
|
||||
self.blocking = r
|
||||
|
||||
def settimeout(self, timeout):
|
||||
self.timeout = timeout
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"additional_notes": [],
|
||||
"banner": {
|
||||
"comments": null,
|
||||
"protocol": "2.0",
|
||||
@ -93,7 +94,7 @@
|
||||
"algorithm": "curve25519-sha256",
|
||||
"notes": {
|
||||
"info": [
|
||||
"default key exchange since OpenSSH 6.4",
|
||||
"default key exchange from OpenSSH 7.4 to 8.9",
|
||||
"available since OpenSSH 7.4, Dropbear SSH 2018.76"
|
||||
]
|
||||
}
|
||||
@ -102,7 +103,7 @@
|
||||
"algorithm": "curve25519-sha256@libssh.org",
|
||||
"notes": {
|
||||
"info": [
|
||||
"default key exchange since OpenSSH 6.4",
|
||||
"default key exchange from OpenSSH 6.5 to 7.3",
|
||||
"available since OpenSSH 6.4, Dropbear SSH 2013.62"
|
||||
]
|
||||
}
|
||||
|
@ -6,9 +6,9 @@
|
||||
|
||||
[0;36m# key exchange algorithms[0m
|
||||
[0;32m(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4, Dropbear SSH 2018.76[0m
|
||||
[0;32m `- [info] default key exchange since OpenSSH 6.4[0m
|
||||
[0;32m `- [info] default key exchange from OpenSSH 7.4 to 8.9[0m
|
||||
[0;32m(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.4, Dropbear SSH 2013.62[0m
|
||||
[0;32m `- [info] default key exchange since OpenSSH 6.4[0m
|
||||
[0;32m `- [info] default key exchange from OpenSSH 6.5 to 7.3[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
|
||||
`- [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
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"additional_notes": [],
|
||||
"banner": {
|
||||
"comments": null,
|
||||
"protocol": "1.99",
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"additional_notes": [],
|
||||
"banner": {
|
||||
"comments": null,
|
||||
"protocol": "2.0",
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"additional_notes": [],
|
||||
"banner": {
|
||||
"comments": null,
|
||||
"protocol": "2.0",
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"additional_notes": [],
|
||||
"banner": {
|
||||
"comments": null,
|
||||
"protocol": "2.0",
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"additional_notes": [],
|
||||
"banner": {
|
||||
"comments": null,
|
||||
"protocol": "2.0",
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"additional_notes": [],
|
||||
"banner": {
|
||||
"comments": null,
|
||||
"protocol": "2.0",
|
||||
|
@ -23,9 +23,21 @@
|
||||
"4096"
|
||||
],
|
||||
"mismatched_field": "Host key (rsa-sha2-512) sizes"
|
||||
},
|
||||
{
|
||||
"actual": [
|
||||
"4096"
|
||||
],
|
||||
"expected_optional": [
|
||||
""
|
||||
],
|
||||
"expected_required": [
|
||||
"3072"
|
||||
],
|
||||
"mismatched_field": "Group exchange (diffie-hellman-group-exchange-sha256) modulus sizes"
|
||||
}
|
||||
],
|
||||
"host": "localhost",
|
||||
"passed": false,
|
||||
"policy": "Hardened OpenSSH Server v8.0 (version 3)"
|
||||
"policy": "Hardened OpenSSH Server v8.0 (version 4)"
|
||||
}
|
||||
|
@ -1,8 +1,12 @@
|
||||
Host: localhost:2222
|
||||
Policy: Hardened OpenSSH Server v8.0 (version 3)
|
||||
Policy: Hardened OpenSSH Server v8.0 (version 4)
|
||||
Result: [0;31m❌ Failed![0m
|
||||
[0;33m
|
||||
Errors:
|
||||
* Group exchange (diffie-hellman-group-exchange-sha256) modulus sizes did not match.
|
||||
- Expected: 3072
|
||||
- Actual: 4096
|
||||
|
||||
* Host key (rsa-sha2-256) sizes did not match.
|
||||
- Expected: 4096
|
||||
- Actual: 3072
|
||||
|
@ -46,9 +46,21 @@
|
||||
"umac-128-etm@openssh.com"
|
||||
],
|
||||
"mismatched_field": "MACs"
|
||||
},
|
||||
{
|
||||
"actual": [
|
||||
"4096"
|
||||
],
|
||||
"expected_optional": [
|
||||
""
|
||||
],
|
||||
"expected_required": [
|
||||
"3072"
|
||||
],
|
||||
"mismatched_field": "Group exchange (diffie-hellman-group-exchange-sha256) modulus sizes"
|
||||
}
|
||||
],
|
||||
"host": "localhost",
|
||||
"passed": false,
|
||||
"policy": "Hardened OpenSSH Server v8.0 (version 3)"
|
||||
"policy": "Hardened OpenSSH Server v8.0 (version 4)"
|
||||
}
|
||||
|
@ -1,8 +1,12 @@
|
||||
Host: localhost:2222
|
||||
Policy: Hardened OpenSSH Server v8.0 (version 3)
|
||||
Policy: Hardened OpenSSH Server v8.0 (version 4)
|
||||
Result: [0;31m❌ Failed![0m
|
||||
[0;33m
|
||||
Errors:
|
||||
* Group exchange (diffie-hellman-group-exchange-sha256) modulus sizes did not match.
|
||||
- Expected: 3072
|
||||
- Actual: 4096
|
||||
|
||||
* Host key (rsa-sha2-256) sizes did not match.
|
||||
- Expected: 4096
|
||||
- Actual: 3072
|
||||
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"errors": [],
|
||||
"host": "localhost",
|
||||
"passed": true,
|
||||
"policy": "Docker policy: test15 (version 1)"
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
Host: localhost:2222
|
||||
Policy: Docker policy: test15 (version 1)
|
||||
Result: [0;32m✔ Passed[0m
|
@ -0,0 +1,86 @@
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"actual": [
|
||||
"rsa-sha2-512",
|
||||
"rsa-sha2-256",
|
||||
"ssh-rsa",
|
||||
"ecdsa-sha2-nistp256",
|
||||
"ssh-ed25519"
|
||||
],
|
||||
"expected_optional": [
|
||||
""
|
||||
],
|
||||
"expected_required": [
|
||||
"rsa-sha2-512",
|
||||
"extra_hostkey_alg"
|
||||
],
|
||||
"mismatched_field": "Host keys"
|
||||
},
|
||||
{
|
||||
"actual": [
|
||||
"curve25519-sha256",
|
||||
"curve25519-sha256@libssh.org",
|
||||
"ecdh-sha2-nistp256",
|
||||
"ecdh-sha2-nistp384",
|
||||
"ecdh-sha2-nistp521",
|
||||
"diffie-hellman-group-exchange-sha256",
|
||||
"diffie-hellman-group16-sha512",
|
||||
"diffie-hellman-group18-sha512",
|
||||
"diffie-hellman-group14-sha256",
|
||||
"diffie-hellman-group14-sha1"
|
||||
],
|
||||
"expected_optional": [
|
||||
""
|
||||
],
|
||||
"expected_required": [
|
||||
"curve25519-sha256",
|
||||
"extra_kex_alg"
|
||||
],
|
||||
"mismatched_field": "Key exchanges"
|
||||
},
|
||||
{
|
||||
"actual": [
|
||||
"chacha20-poly1305@openssh.com",
|
||||
"aes128-ctr",
|
||||
"aes192-ctr",
|
||||
"aes256-ctr",
|
||||
"aes128-gcm@openssh.com",
|
||||
"aes256-gcm@openssh.com"
|
||||
],
|
||||
"expected_optional": [
|
||||
""
|
||||
],
|
||||
"expected_required": [
|
||||
"chacha20-poly1305@openssh.com",
|
||||
"extra_cipher_alg"
|
||||
],
|
||||
"mismatched_field": "Ciphers"
|
||||
},
|
||||
{
|
||||
"actual": [
|
||||
"umac-64-etm@openssh.com",
|
||||
"umac-128-etm@openssh.com",
|
||||
"hmac-sha2-256-etm@openssh.com",
|
||||
"hmac-sha2-512-etm@openssh.com",
|
||||
"hmac-sha1-etm@openssh.com",
|
||||
"umac-64@openssh.com",
|
||||
"umac-128@openssh.com",
|
||||
"hmac-sha2-256",
|
||||
"hmac-sha2-512",
|
||||
"hmac-sha1"
|
||||
],
|
||||
"expected_optional": [
|
||||
""
|
||||
],
|
||||
"expected_required": [
|
||||
"umac-64-etm@openssh.com",
|
||||
"extra_mac_alg"
|
||||
],
|
||||
"mismatched_field": "MACs"
|
||||
}
|
||||
],
|
||||
"host": "localhost",
|
||||
"passed": false,
|
||||
"policy": "Docker policy: test16 (version 1)"
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
Host: localhost:2222
|
||||
Policy: Docker policy: test16 (version 1)
|
||||
Result: [0;31m❌ Failed![0m
|
||||
[0;33m
|
||||
Errors:
|
||||
* Ciphers did not match.
|
||||
- Expected (subset and/or reordering allowed): chacha20-poly1305@openssh.com, extra_cipher_alg
|
||||
- Actual: chacha20-poly1305@openssh.com, aes128-ctr, aes192-ctr, aes256-ctr, aes128-gcm@openssh.com, aes256-gcm@openssh.com
|
||||
|
||||
* Host keys did not match.
|
||||
- Expected (subset and/or reordering allowed): rsa-sha2-512, extra_hostkey_alg
|
||||
- Actual: rsa-sha2-512, rsa-sha2-256, ssh-rsa, ecdsa-sha2-nistp256, ssh-ed25519
|
||||
|
||||
* Key exchanges did not match.
|
||||
- Expected (subset and/or reordering allowed): curve25519-sha256, extra_kex_alg
|
||||
- Actual: curve25519-sha256, curve25519-sha256@libssh.org, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group-exchange-sha256, diffie-hellman-group16-sha512, diffie-hellman-group18-sha512, diffie-hellman-group14-sha256, diffie-hellman-group14-sha1
|
||||
|
||||
* MACs did not match.
|
||||
- Expected (subset and/or reordering allowed): umac-64-etm@openssh.com, extra_mac_alg
|
||||
- Actual: umac-64-etm@openssh.com, umac-128-etm@openssh.com, hmac-sha2-256-etm@openssh.com, hmac-sha2-512-etm@openssh.com, hmac-sha1-etm@openssh.com, umac-64@openssh.com, umac-128@openssh.com, hmac-sha2-256, hmac-sha2-512, hmac-sha1
|
||||
[0m
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"errors": [],
|
||||
"host": "localhost",
|
||||
"passed": true,
|
||||
"policy": "Docker policy: test17 (version 1)"
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
Host: localhost:2222
|
||||
Policy: Docker policy: test17 (version 1)
|
||||
Result: [0;32m✔ Passed[0m
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"additional_notes": [],
|
||||
"banner": {
|
||||
"comments": null,
|
||||
"protocol": "2.0",
|
||||
@ -37,7 +38,10 @@
|
||||
"notes": {
|
||||
"info": [
|
||||
"default cipher since OpenSSH 6.9",
|
||||
"available since OpenSSH 6.5"
|
||||
"available since OpenSSH 6.5, Dropbear SSH 2020.79"
|
||||
],
|
||||
"warn": [
|
||||
"vulnerable to the Terrapin attack (CVE-2023-48795), allowing message prefix truncation"
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -109,7 +113,7 @@
|
||||
"algorithm": "curve25519-sha256",
|
||||
"notes": {
|
||||
"info": [
|
||||
"default key exchange since OpenSSH 6.4",
|
||||
"default key exchange from OpenSSH 7.4 to 8.9",
|
||||
"available since OpenSSH 7.4, Dropbear SSH 2018.76"
|
||||
]
|
||||
}
|
||||
@ -118,7 +122,7 @@
|
||||
"algorithm": "curve25519-sha256@libssh.org",
|
||||
"notes": {
|
||||
"info": [
|
||||
"default key exchange since OpenSSH 6.4",
|
||||
"default key exchange from OpenSSH 6.5 to 7.3",
|
||||
"available since OpenSSH 6.4, Dropbear SSH 2013.62"
|
||||
]
|
||||
}
|
||||
@ -223,7 +227,7 @@
|
||||
"keysize": 3072,
|
||||
"notes": {
|
||||
"info": [
|
||||
"available since OpenSSH 7.2"
|
||||
"available since OpenSSH 7.2, Dropbear SSH 2020.79"
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -258,7 +262,7 @@
|
||||
"algorithm": "ssh-ed25519",
|
||||
"notes": {
|
||||
"info": [
|
||||
"available since OpenSSH 6.5"
|
||||
"available since OpenSSH 6.5, Dropbear SSH 2020.79"
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -415,6 +419,12 @@
|
||||
},
|
||||
"warning": {
|
||||
"del": {
|
||||
"enc": [
|
||||
{
|
||||
"name": "chacha20-poly1305@openssh.com",
|
||||
"notes": ""
|
||||
}
|
||||
],
|
||||
"kex": [
|
||||
{
|
||||
"name": "diffie-hellman-group14-sha256",
|
||||
|
@ -1,7 +1,7 @@
|
||||
[0;36m# general[0m
|
||||
[0;32m(gen) banner: SSH-2.0-OpenSSH_8.0[0m
|
||||
[0;32m(gen) software: OpenSSH 8.0[0m
|
||||
[0;32m(gen) compatibility: OpenSSH 7.4+, Dropbear SSH 2018.76+[0m
|
||||
[0;32m(gen) compatibility: OpenSSH 7.4+, Dropbear SSH 2020.79+[0m
|
||||
[0;32m(gen) compression: enabled (zlib@openssh.com)[0m
|
||||
|
||||
[0;36m# security[0m
|
||||
@ -12,9 +12,9 @@
|
||||
|
||||
[0;36m# key exchange algorithms[0m
|
||||
[0;32m(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4, Dropbear SSH 2018.76[0m
|
||||
[0;32m `- [info] default key exchange since OpenSSH 6.4[0m
|
||||
[0;32m `- [info] default key exchange from OpenSSH 7.4 to 8.9[0m
|
||||
[0;32m(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.4, Dropbear SSH 2013.62[0m
|
||||
[0;32m `- [info] default key exchange since OpenSSH 6.4[0m
|
||||
[0;32m `- [info] default key exchange from OpenSSH 6.5 to 7.3[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
|
||||
`- [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
|
||||
@ -33,18 +33,19 @@
|
||||
|
||||
[0;36m# host-key algorithms[0m
|
||||
[0;32m(key) rsa-sha2-512 (3072-bit) -- [info] available since OpenSSH 7.2[0m
|
||||
[0;32m(key) rsa-sha2-256 (3072-bit) -- [info] available since OpenSSH 7.2[0m
|
||||
[0;32m(key) rsa-sha2-256 (3072-bit) -- [info] available since OpenSSH 7.2, Dropbear SSH 2020.79[0m
|
||||
[0;31m(key) ssh-rsa (3072-bit) -- [fail] using broken SHA-1 hash algorithm[0m
|
||||
`- [info] available since OpenSSH 2.5.0, Dropbear SSH 0.28
|
||||
`- [info] deprecated in OpenSSH 8.8: https://www.openssh.com/txt/release-8.8
|
||||
[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;33m `- [warn] using weak random number generator could reveal the key[0m
|
||||
`- [info] available since OpenSSH 5.7, Dropbear SSH 2013.62
|
||||
[0;32m(key) ssh-ed25519 -- [info] available since OpenSSH 6.5[0m
|
||||
[0;32m(key) ssh-ed25519 -- [info] available since OpenSSH 6.5, Dropbear SSH 2020.79[0m
|
||||
|
||||
[0;36m# encryption algorithms (ciphers)[0m
|
||||
[0;32m(enc) chacha20-poly1305@openssh.com -- [info] available since OpenSSH 6.5[0m
|
||||
[0;32m `- [info] default cipher since OpenSSH 6.9[0m
|
||||
[0;33m(enc) chacha20-poly1305@openssh.com -- [warn] vulnerable to the Terrapin attack (CVE-2023-48795), allowing message prefix truncation[0m
|
||||
`- [info] available since OpenSSH 6.5, Dropbear SSH 2020.79
|
||||
`- [info] default cipher since OpenSSH 6.9
|
||||
[0;32m(enc) aes128-ctr -- [info] available since OpenSSH 3.7, Dropbear SSH 0.52[0m
|
||||
[0;32m(enc) aes192-ctr -- [info] available since OpenSSH 3.7[0m
|
||||
[0;32m(enc) aes256-ctr -- [info] available since OpenSSH 3.7, Dropbear SSH 0.52[0m
|
||||
@ -85,6 +86,7 @@
|
||||
[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) -ssh-rsa -- key algorithm to remove [0m
|
||||
[0;33m(rec) -chacha20-poly1305@openssh.com -- enc 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-512 -- mac algorithm to remove [0m
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"additional_notes": [],
|
||||
"banner": {
|
||||
"comments": null,
|
||||
"protocol": "2.0",
|
||||
@ -37,7 +38,10 @@
|
||||
"notes": {
|
||||
"info": [
|
||||
"default cipher since OpenSSH 6.9",
|
||||
"available since OpenSSH 6.5"
|
||||
"available since OpenSSH 6.5, Dropbear SSH 2020.79"
|
||||
],
|
||||
"warn": [
|
||||
"vulnerable to the Terrapin attack (CVE-2023-48795), allowing message prefix truncation"
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -99,7 +103,7 @@
|
||||
"algorithm": "curve25519-sha256",
|
||||
"notes": {
|
||||
"info": [
|
||||
"default key exchange since OpenSSH 6.4",
|
||||
"default key exchange from OpenSSH 7.4 to 8.9",
|
||||
"available since OpenSSH 7.4, Dropbear SSH 2018.76"
|
||||
]
|
||||
}
|
||||
@ -108,7 +112,7 @@
|
||||
"algorithm": "curve25519-sha256@libssh.org",
|
||||
"notes": {
|
||||
"info": [
|
||||
"default key exchange since OpenSSH 6.4",
|
||||
"default key exchange from OpenSSH 6.5 to 7.3",
|
||||
"available since OpenSSH 6.4, Dropbear SSH 2013.62"
|
||||
]
|
||||
}
|
||||
@ -203,7 +207,7 @@
|
||||
"algorithm": "ssh-ed25519",
|
||||
"notes": {
|
||||
"info": [
|
||||
"available since OpenSSH 6.5"
|
||||
"available since OpenSSH 6.5, Dropbear SSH 2020.79"
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -374,6 +378,12 @@
|
||||
},
|
||||
"warning": {
|
||||
"del": {
|
||||
"enc": [
|
||||
{
|
||||
"name": "chacha20-poly1305@openssh.com",
|
||||
"notes": ""
|
||||
}
|
||||
],
|
||||
"kex": [
|
||||
{
|
||||
"name": "diffie-hellman-group14-sha256",
|
||||
|
@ -1,7 +1,7 @@
|
||||
[0;36m# general[0m
|
||||
[0;32m(gen) banner: SSH-2.0-OpenSSH_8.0[0m
|
||||
[0;32m(gen) software: OpenSSH 8.0[0m
|
||||
[0;32m(gen) compatibility: OpenSSH 7.4+, Dropbear SSH 2018.76+[0m
|
||||
[0;32m(gen) compatibility: OpenSSH 7.4+, Dropbear SSH 2020.79+[0m
|
||||
[0;32m(gen) compression: enabled (zlib@openssh.com)[0m
|
||||
|
||||
[0;36m# security[0m
|
||||
@ -12,9 +12,9 @@
|
||||
|
||||
[0;36m# key exchange algorithms[0m
|
||||
[0;32m(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4, Dropbear SSH 2018.76[0m
|
||||
[0;32m `- [info] default key exchange since OpenSSH 6.4[0m
|
||||
[0;32m `- [info] default key exchange from OpenSSH 7.4 to 8.9[0m
|
||||
[0;32m(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.4, Dropbear SSH 2013.62[0m
|
||||
[0;32m `- [info] default key exchange since OpenSSH 6.4[0m
|
||||
[0;32m `- [info] default key exchange from OpenSSH 6.5 to 7.3[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
|
||||
`- [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
|
||||
@ -32,12 +32,13 @@
|
||||
`- [info] available since OpenSSH 3.9, Dropbear SSH 0.53
|
||||
|
||||
[0;36m# host-key algorithms[0m
|
||||
[0;32m(key) ssh-ed25519 -- [info] available since OpenSSH 6.5[0m
|
||||
[0;32m(key) ssh-ed25519 -- [info] available since OpenSSH 6.5, Dropbear SSH 2020.79[0m
|
||||
[0;32m(key) ssh-ed25519-cert-v01@openssh.com (256-bit cert/256-bit ssh-ed25519 CA) -- [info] available since OpenSSH 6.5[0m
|
||||
|
||||
[0;36m# encryption algorithms (ciphers)[0m
|
||||
[0;32m(enc) chacha20-poly1305@openssh.com -- [info] available since OpenSSH 6.5[0m
|
||||
[0;32m `- [info] default cipher since OpenSSH 6.9[0m
|
||||
[0;33m(enc) chacha20-poly1305@openssh.com -- [warn] vulnerable to the Terrapin attack (CVE-2023-48795), allowing message prefix truncation[0m
|
||||
`- [info] available since OpenSSH 6.5, Dropbear SSH 2020.79
|
||||
`- [info] default cipher since OpenSSH 6.9
|
||||
[0;32m(enc) aes128-ctr -- [info] available since OpenSSH 3.7, Dropbear SSH 0.52[0m
|
||||
[0;32m(enc) aes192-ctr -- [info] available since OpenSSH 3.7[0m
|
||||
[0;32m(enc) aes256-ctr -- [info] available since OpenSSH 3.7, Dropbear SSH 0.52[0m
|
||||
@ -77,6 +78,7 @@
|
||||
[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-512 -- key algorithm to append [0m
|
||||
[0;33m(rec) -chacha20-poly1305@openssh.com -- enc 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-512 -- mac algorithm to remove [0m
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"additional_notes": [],
|
||||
"banner": {
|
||||
"comments": null,
|
||||
"protocol": "2.0",
|
||||
@ -37,7 +38,10 @@
|
||||
"notes": {
|
||||
"info": [
|
||||
"default cipher since OpenSSH 6.9",
|
||||
"available since OpenSSH 6.5"
|
||||
"available since OpenSSH 6.5, Dropbear SSH 2020.79"
|
||||
],
|
||||
"warn": [
|
||||
"vulnerable to the Terrapin attack (CVE-2023-48795), allowing message prefix truncation"
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -99,7 +103,7 @@
|
||||
"algorithm": "curve25519-sha256",
|
||||
"notes": {
|
||||
"info": [
|
||||
"default key exchange since OpenSSH 6.4",
|
||||
"default key exchange from OpenSSH 7.4 to 8.9",
|
||||
"available since OpenSSH 7.4, Dropbear SSH 2018.76"
|
||||
]
|
||||
}
|
||||
@ -108,7 +112,7 @@
|
||||
"algorithm": "curve25519-sha256@libssh.org",
|
||||
"notes": {
|
||||
"info": [
|
||||
"default key exchange since OpenSSH 6.4",
|
||||
"default key exchange from OpenSSH 6.5 to 7.3",
|
||||
"available since OpenSSH 6.4, Dropbear SSH 2013.62"
|
||||
]
|
||||
}
|
||||
@ -129,7 +133,7 @@
|
||||
"algorithm": "ssh-ed25519",
|
||||
"notes": {
|
||||
"info": [
|
||||
"available since OpenSSH 6.5"
|
||||
"available since OpenSSH 6.5, Dropbear SSH 2020.79"
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -184,6 +188,16 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"warning": {
|
||||
"del": {
|
||||
"enc": [
|
||||
{
|
||||
"name": "chacha20-poly1305@openssh.com",
|
||||
"notes": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"target": "localhost:2222"
|
||||
|
@ -1,7 +1,7 @@
|
||||
[0;36m# general[0m
|
||||
[0;32m(gen) banner: SSH-2.0-OpenSSH_8.0[0m
|
||||
[0;32m(gen) software: OpenSSH 8.0[0m
|
||||
[0;32m(gen) compatibility: OpenSSH 7.4+, Dropbear SSH 2018.76+[0m
|
||||
[0;32m(gen) compatibility: OpenSSH 7.4+, Dropbear SSH 2020.79+[0m
|
||||
[0;32m(gen) compression: enabled (zlib@openssh.com)[0m
|
||||
|
||||
[0;36m# security[0m
|
||||
@ -12,18 +12,19 @@
|
||||
|
||||
[0;36m# key exchange algorithms[0m
|
||||
[0;32m(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4, Dropbear SSH 2018.76[0m
|
||||
[0;32m `- [info] default key exchange since OpenSSH 6.4[0m
|
||||
[0;32m `- [info] default key exchange from OpenSSH 7.4 to 8.9[0m
|
||||
[0;32m(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.4, Dropbear SSH 2013.62[0m
|
||||
[0;32m `- [info] default key exchange since OpenSSH 6.4[0m
|
||||
[0;32m `- [info] default key exchange from OpenSSH 6.5 to 7.3[0m
|
||||
[0;32m(kex) diffie-hellman-group-exchange-sha256 (4096-bit) -- [info] available since OpenSSH 4.4[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
|
||||
|
||||
[0;36m# host-key algorithms[0m
|
||||
[0;32m(key) ssh-ed25519 -- [info] available since OpenSSH 6.5[0m
|
||||
[0;32m(key) ssh-ed25519 -- [info] available since OpenSSH 6.5, Dropbear SSH 2020.79[0m
|
||||
|
||||
[0;36m# encryption algorithms (ciphers)[0m
|
||||
[0;32m(enc) chacha20-poly1305@openssh.com -- [info] available since OpenSSH 6.5[0m
|
||||
[0;32m `- [info] default cipher since OpenSSH 6.9[0m
|
||||
[0;33m(enc) chacha20-poly1305@openssh.com -- [warn] vulnerable to the Terrapin attack (CVE-2023-48795), allowing message prefix truncation[0m
|
||||
`- [info] available since OpenSSH 6.5, Dropbear SSH 2020.79
|
||||
`- [info] default cipher since OpenSSH 6.9
|
||||
[0;32m(enc) aes256-gcm@openssh.com -- [info] available since OpenSSH 6.2[0m
|
||||
[0;32m(enc) aes128-gcm@openssh.com -- [info] available since OpenSSH 6.2[0m
|
||||
[0;32m(enc) aes256-ctr -- [info] available since OpenSSH 3.7, Dropbear SSH 0.52[0m
|
||||
@ -43,4 +44,8 @@
|
||||
[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-512 -- key algorithm to append [0m
|
||||
[0;33m(rec) -chacha20-poly1305@openssh.com -- enc algorithm to remove [0m
|
||||
|
||||
[0;36m# additional info[0m
|
||||
[0;33m(nfo) For hardening guides on common OSes, please see: <https://www.ssh-audit.com/hardening_guides.html>[0m
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"additional_notes": [],
|
||||
"banner": {
|
||||
"comments": "",
|
||||
"protocol": "2.0",
|
||||
@ -15,7 +16,10 @@
|
||||
"notes": {
|
||||
"info": [
|
||||
"default cipher since OpenSSH 6.9",
|
||||
"available since OpenSSH 6.5"
|
||||
"available since OpenSSH 6.5, Dropbear SSH 2020.79"
|
||||
],
|
||||
"warn": [
|
||||
"vulnerable to the Terrapin attack (CVE-2023-48795), allowing message prefix truncation"
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -37,7 +41,7 @@
|
||||
"algorithm": "curve25519-sha256",
|
||||
"notes": {
|
||||
"info": [
|
||||
"default key exchange since OpenSSH 6.4",
|
||||
"default key exchange from OpenSSH 7.4 to 8.9",
|
||||
"available since OpenSSH 7.4, Dropbear SSH 2018.76"
|
||||
]
|
||||
}
|
||||
@ -46,7 +50,7 @@
|
||||
"algorithm": "curve25519-sha256@libssh.org",
|
||||
"notes": {
|
||||
"info": [
|
||||
"default key exchange since OpenSSH 6.4",
|
||||
"default key exchange from OpenSSH 6.5 to 7.3",
|
||||
"available since OpenSSH 6.4, Dropbear SSH 2013.62"
|
||||
]
|
||||
}
|
||||
@ -69,7 +73,7 @@
|
||||
"algorithm": "ssh-ed25519",
|
||||
"notes": {
|
||||
"info": [
|
||||
"available since OpenSSH 6.5"
|
||||
"available since OpenSSH 6.5, Dropbear SSH 2020.79"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,24 @@
|
||||
[0;36m# general[0m
|
||||
[0;32m(gen) software: TinySSH noversion[0m
|
||||
[0;32m(gen) compatibility: OpenSSH 8.0-8.4, Dropbear SSH 2018.76+[0m
|
||||
[0;32m(gen) compatibility: OpenSSH 8.0-8.4, Dropbear SSH 2020.79+[0m
|
||||
[0;32m(gen) compression: disabled[0m
|
||||
|
||||
[0;36m# key exchange algorithms[0m
|
||||
[0;32m(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4, Dropbear SSH 2018.76[0m
|
||||
[0;32m `- [info] default key exchange since OpenSSH 6.4[0m
|
||||
[0;32m `- [info] default key exchange from OpenSSH 7.4 to 8.9[0m
|
||||
[0;32m(kex) curve25519-sha256@libssh.org -- [info] available since OpenSSH 6.4, Dropbear SSH 2013.62[0m
|
||||
[0;32m `- [info] default key exchange since OpenSSH 6.4[0m
|
||||
[0;32m `- [info] default key exchange from OpenSSH 6.5 to 7.3[0m
|
||||
[0;33m(kex) sntrup4591761x25519-sha512@tinyssh.org -- [warn] using experimental algorithm[0m
|
||||
`- [info] available since OpenSSH 8.0
|
||||
`- [info] the sntrup4591761 algorithm was withdrawn, as it may not provide strong post-quantum security
|
||||
|
||||
[0;36m# host-key algorithms[0m
|
||||
[0;32m(key) ssh-ed25519 -- [info] available since OpenSSH 6.5[0m
|
||||
[0;32m(key) ssh-ed25519 -- [info] available since OpenSSH 6.5, Dropbear SSH 2020.79[0m
|
||||
|
||||
[0;36m# encryption algorithms (ciphers)[0m
|
||||
[0;32m(enc) chacha20-poly1305@openssh.com -- [info] available since OpenSSH 6.5[0m
|
||||
[0;32m `- [info] default cipher since OpenSSH 6.9[0m
|
||||
[0;33m(enc) chacha20-poly1305@openssh.com -- [warn] vulnerable to the Terrapin attack (CVE-2023-48795), allowing message prefix truncation[0m
|
||||
`- [info] available since OpenSSH 6.5, Dropbear SSH 2020.79
|
||||
`- [info] default cipher since OpenSSH 6.9
|
||||
|
||||
[0;36m# message authentication code algorithms[0m
|
||||
[0;33m(mac) hmac-sha2-256 -- [warn] using encrypt-and-MAC mode[0m
|
||||
|
13
test/docker/policies/policy_test15.txt
Normal file
13
test/docker/policies/policy_test15.txt
Normal file
@ -0,0 +1,13 @@
|
||||
#
|
||||
# Docker policy: test15
|
||||
#
|
||||
|
||||
name = "Docker policy: test15"
|
||||
version = 1
|
||||
allow_algorithm_subset_and_reordering = true
|
||||
banner = "SSH-2.0-OpenSSH_8.0"
|
||||
compressions = none, zlib@openssh.com
|
||||
host keys = rsa-sha2-512, rsa-sha2-256, ssh-rsa, ecdsa-sha2-nistp256, ssh-ed25519, extra_hostkey_alg
|
||||
key exchanges = curve25519-sha256, curve25519-sha256@libssh.org, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group-exchange-sha256, diffie-hellman-group16-sha512, diffie-hellman-group18-sha512, diffie-hellman-group14-sha256, diffie-hellman-group14-sha1, extra_kex_alg
|
||||
ciphers = chacha20-poly1305@openssh.com, aes128-ctr, aes192-ctr, aes256-ctr, aes128-gcm@openssh.com, aes256-gcm@openssh.com, extra_cipher_alg
|
||||
macs = umac-64-etm@openssh.com, umac-128-etm@openssh.com, hmac-sha2-256-etm@openssh.com, hmac-sha2-512-etm@openssh.com, hmac-sha1-etm@openssh.com, umac-64@openssh.com, umac-128@openssh.com, hmac-sha2-256, hmac-sha2-512, hmac-sha1, extra_mac_alg
|
13
test/docker/policies/policy_test16.txt
Normal file
13
test/docker/policies/policy_test16.txt
Normal file
@ -0,0 +1,13 @@
|
||||
#
|
||||
# Docker policy: test16
|
||||
#
|
||||
|
||||
name = "Docker policy: test16"
|
||||
version = 1
|
||||
allow_algorithm_subset_and_reordering = true
|
||||
banner = "SSH-2.0-OpenSSH_8.0"
|
||||
compressions = none, zlib@openssh.com
|
||||
host keys = rsa-sha2-512, extra_hostkey_alg
|
||||
key exchanges = curve25519-sha256, extra_kex_alg
|
||||
ciphers = chacha20-poly1305@openssh.com, extra_cipher_alg
|
||||
macs = umac-64-etm@openssh.com, extra_mac_alg
|
15
test/docker/policies/policy_test17.txt
Normal file
15
test/docker/policies/policy_test17.txt
Normal file
@ -0,0 +1,15 @@
|
||||
#
|
||||
# Docker policy: test17
|
||||
#
|
||||
|
||||
name = "Docker policy: test17"
|
||||
version = 1
|
||||
allow_larger_keys = true
|
||||
banner = "SSH-2.0-OpenSSH_8.0"
|
||||
compressions = none, zlib@openssh.com
|
||||
host keys = rsa-sha2-512, rsa-sha2-256, ssh-rsa, ecdsa-sha2-nistp256, ssh-ed25519
|
||||
key exchanges = curve25519-sha256, curve25519-sha256@libssh.org, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group-exchange-sha256, diffie-hellman-group16-sha512, diffie-hellman-group18-sha512, diffie-hellman-group14-sha256, diffie-hellman-group14-sha1
|
||||
ciphers = chacha20-poly1305@openssh.com, aes128-ctr, aes192-ctr, aes256-ctr, aes128-gcm@openssh.com, aes256-gcm@openssh.com
|
||||
macs = umac-64-etm@openssh.com, umac-128-etm@openssh.com, hmac-sha2-256-etm@openssh.com, hmac-sha2-512-etm@openssh.com, hmac-sha1-etm@openssh.com, umac-64@openssh.com, umac-128@openssh.com, hmac-sha2-256, hmac-sha2-512, hmac-sha1
|
||||
host_key_sizes = {"ssh-rsa": {"hostkey_size": 2048}, "rsa-sha2-256": {"hostkey_size": 2048}, "rsa-sha2-512": {"hostkey_size": 2048}, "ssh-ed25519": {"hostkey_size": 256}}
|
||||
dh_modulus_sizes = {"diffie-hellman-group-exchange-sha256": 2048}
|
29
test/test_dheater.py
Normal file
29
test/test_dheater.py
Normal file
@ -0,0 +1,29 @@
|
||||
import pytest
|
||||
|
||||
from ssh_audit.ssh2_kexdb import SSH2_KexDB
|
||||
from ssh_audit.dheat import DHEat
|
||||
|
||||
|
||||
class TestDHEat:
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def init(self):
|
||||
self.SSH2_KexDB = SSH2_KexDB
|
||||
self.DHEat = DHEat
|
||||
|
||||
def test_kex_definition_completeness(self):
|
||||
alg_db = self.SSH2_KexDB.get_db()
|
||||
kex_db = alg_db['kex']
|
||||
|
||||
# Get all Diffie-Hellman algorithms defined in our database.
|
||||
dh_algs = []
|
||||
for kex in kex_db:
|
||||
if kex.startswith('diffie-hellman-'):
|
||||
dh_algs.append(kex)
|
||||
|
||||
# Ensure that each DH algorithm in our database is in either DHEat's alg_priority or gex_algs list. Also ensure that all non-group exchange algorithms are accounted for in the alg_modulus_sizes dictionary.
|
||||
for dh_alg in dh_algs:
|
||||
assert (dh_alg in self.DHEat.alg_priority) or (dh_alg in self.DHEat.gex_algs)
|
||||
|
||||
if dh_alg.find("group-exchange") == -1:
|
||||
assert dh_alg in self.DHEat.alg_modulus_sizes
|
@ -17,6 +17,7 @@ class TestErrors:
|
||||
conf = self.AuditConf('localhost', 22)
|
||||
conf.colors = False
|
||||
conf.batch = True
|
||||
conf.skip_rate_test = True
|
||||
return conf
|
||||
|
||||
def _audit(self, spy, conf=None, exit_expected=False):
|
||||
|
@ -2,6 +2,7 @@ import hashlib
|
||||
import pytest
|
||||
from datetime import date
|
||||
|
||||
from ssh_audit.builtin_policies import BUILTIN_POLICIES
|
||||
from ssh_audit.outputbuffer import OutputBuffer
|
||||
from ssh_audit.policy import Policy
|
||||
from ssh_audit.ssh2_kex import SSH2_Kex
|
||||
@ -40,43 +41,48 @@ class TestPolicy:
|
||||
def test_builtin_policy_consistency(self):
|
||||
'''Ensure that the BUILTIN_POLICIES struct is consistent.'''
|
||||
|
||||
for policy_name in Policy.BUILTIN_POLICIES:
|
||||
# Ensure that the policy name ends with " (version X)", where X is the 'version' field.
|
||||
version_str = " (version %s)" % Policy.BUILTIN_POLICIES[policy_name]['version']
|
||||
assert policy_name.endswith(version_str)
|
||||
for policy_name in BUILTIN_POLICIES:
|
||||
|
||||
# Ensure that all required fields are present.
|
||||
required_fields = ['version', 'banner', 'compressions', 'host_keys', 'optional_host_keys', 'kex', 'ciphers', 'macs', 'hostkey_sizes', 'dh_modulus_sizes', 'server_policy']
|
||||
required_fields = ['version', 'changelog', 'banner', 'compressions', 'host_keys', 'optional_host_keys', 'kex', 'ciphers', 'macs', 'hostkey_sizes', 'dh_modulus_sizes', 'server_policy']
|
||||
for field in required_fields:
|
||||
assert field in Policy.BUILTIN_POLICIES[policy_name]
|
||||
assert field in BUILTIN_POLICIES[policy_name]
|
||||
|
||||
# Ensure that the policy name ends with " (version X)", where X is the 'version' field.
|
||||
version_str = " (version %s)" % BUILTIN_POLICIES[policy_name]['version']
|
||||
assert policy_name.endswith(version_str)
|
||||
|
||||
# Ensure no extra fields are present.
|
||||
assert len(required_fields) == len(Policy.BUILTIN_POLICIES[policy_name])
|
||||
assert len(required_fields) == len(BUILTIN_POLICIES[policy_name])
|
||||
|
||||
# Ensure that the changelog field is a string and non-empty.
|
||||
assert type(BUILTIN_POLICIES[policy_name]['changelog']) is str
|
||||
assert len(BUILTIN_POLICIES[policy_name]['changelog']) > 0
|
||||
|
||||
# Ensure that at least one host key is defined.
|
||||
assert type(Policy.BUILTIN_POLICIES[policy_name]['host_keys']) is list
|
||||
assert len(Policy.BUILTIN_POLICIES[policy_name]['host_keys']) > 0
|
||||
assert type(BUILTIN_POLICIES[policy_name]['host_keys']) is list
|
||||
assert len(BUILTIN_POLICIES[policy_name]['host_keys']) > 0
|
||||
|
||||
# Ensure that at least one key exchange is defined.
|
||||
assert type(Policy.BUILTIN_POLICIES[policy_name]['kex']) is list
|
||||
assert len(Policy.BUILTIN_POLICIES[policy_name]['kex']) > 0
|
||||
assert type(BUILTIN_POLICIES[policy_name]['kex']) is list
|
||||
assert len(BUILTIN_POLICIES[policy_name]['kex']) > 0
|
||||
|
||||
# Ensure that at least one cipher is defined.
|
||||
assert type(Policy.BUILTIN_POLICIES[policy_name]['ciphers']) is list
|
||||
assert len(Policy.BUILTIN_POLICIES[policy_name]['ciphers']) > 0
|
||||
assert type(BUILTIN_POLICIES[policy_name]['ciphers']) is list
|
||||
assert len(BUILTIN_POLICIES[policy_name]['ciphers']) > 0
|
||||
|
||||
# Ensure that at least one MAC is defined
|
||||
assert type(Policy.BUILTIN_POLICIES[policy_name]['macs']) is list
|
||||
assert len(Policy.BUILTIN_POLICIES[policy_name]['macs']) > 0
|
||||
assert type(BUILTIN_POLICIES[policy_name]['macs']) is list
|
||||
assert len(BUILTIN_POLICIES[policy_name]['macs']) > 0
|
||||
|
||||
# These tests apply to server policies only.
|
||||
if Policy.BUILTIN_POLICIES[policy_name]['server_policy']:
|
||||
assert type(Policy.BUILTIN_POLICIES[policy_name]['hostkey_sizes']) is dict
|
||||
assert len(Policy.BUILTIN_POLICIES[policy_name]['hostkey_sizes']) > 0
|
||||
if BUILTIN_POLICIES[policy_name]['server_policy']:
|
||||
assert type(BUILTIN_POLICIES[policy_name]['hostkey_sizes']) is dict
|
||||
assert len(BUILTIN_POLICIES[policy_name]['hostkey_sizes']) > 0
|
||||
|
||||
# Examine all the hostkey_sizes entries...
|
||||
for hostkey_type in Policy.BUILTIN_POLICIES[policy_name]['hostkey_sizes']:
|
||||
hostkey_data = Policy.BUILTIN_POLICIES[policy_name]['hostkey_sizes'][hostkey_type]
|
||||
for hostkey_type in BUILTIN_POLICIES[policy_name]['hostkey_sizes']:
|
||||
hostkey_data = BUILTIN_POLICIES[policy_name]['hostkey_sizes'][hostkey_type]
|
||||
|
||||
# Ensure that 'hostkey_size' is always included and that it is an integer.
|
||||
assert 'hostkey_size' in hostkey_data
|
||||
@ -105,27 +111,27 @@ class TestPolicy:
|
||||
assert hostkey_data['ca_key_size'] == 256
|
||||
|
||||
# Ensure that the 'dh_modulus_size' field is a dict.
|
||||
assert type(Policy.BUILTIN_POLICIES[policy_name]['dh_modulus_sizes']) is dict
|
||||
assert type(BUILTIN_POLICIES[policy_name]['dh_modulus_sizes']) is dict
|
||||
|
||||
# The 'dh_modulus_size' field should have either one entry, or be empty.
|
||||
assert len(Policy.BUILTIN_POLICIES[policy_name]['dh_modulus_sizes']) in range(0, 2) # The endpoint in range() is not inclusive
|
||||
assert len(BUILTIN_POLICIES[policy_name]['dh_modulus_sizes']) in range(0, 2) # The endpoint in range() is not inclusive
|
||||
|
||||
# If 'diffie-hellman-group-exchange-sha256' is in the kex list, ensure that it exists in the 'dh_modulus_sizes' entry. That entry must be defined for 2048 bits or larger.
|
||||
if 'diffie-hellman-group-exchange-sha256' in Policy.BUILTIN_POLICIES[policy_name]['kex']:
|
||||
assert 'diffie-hellman-group-exchange-sha256' in Policy.BUILTIN_POLICIES[policy_name]['dh_modulus_sizes']
|
||||
assert int(Policy.BUILTIN_POLICIES[policy_name]['dh_modulus_sizes']['diffie-hellman-group-exchange-sha256']) >= 2048
|
||||
if 'diffie-hellman-group-exchange-sha256' in BUILTIN_POLICIES[policy_name]['kex']:
|
||||
assert 'diffie-hellman-group-exchange-sha256' in BUILTIN_POLICIES[policy_name]['dh_modulus_sizes']
|
||||
assert int(BUILTIN_POLICIES[policy_name]['dh_modulus_sizes']['diffie-hellman-group-exchange-sha256']) >= 2048
|
||||
|
||||
else: # Client-specific tests.
|
||||
|
||||
# These must be set to None for client policies, since they have no meaning otherwise.
|
||||
assert Policy.BUILTIN_POLICIES[policy_name]['hostkey_sizes'] is None
|
||||
assert Policy.BUILTIN_POLICIES[policy_name]['dh_modulus_sizes'] is None
|
||||
assert BUILTIN_POLICIES[policy_name]['hostkey_sizes'] is None
|
||||
assert BUILTIN_POLICIES[policy_name]['dh_modulus_sizes'] is None
|
||||
|
||||
# Ensure that each built-in policy can be loaded with Policy.load_builtin_policy().
|
||||
assert Policy.load_builtin_policy(policy_name) is not None
|
||||
|
||||
# Ensure that both server and client policy names are returned.
|
||||
server_policy_names, client_policy_names = Policy.list_builtin_policies()
|
||||
server_policy_names, client_policy_names = Policy.list_builtin_policies(False)
|
||||
assert len(server_policy_names) > 0
|
||||
assert len(client_policy_names) > 0
|
||||
|
||||
@ -144,7 +150,7 @@ ciphers = cipher_alg1, cipher_alg2, cipher_alg3
|
||||
macs = mac_alg1, mac_alg2, mac_alg3'''
|
||||
|
||||
policy = self.Policy(policy_data=policy_data)
|
||||
assert str(policy) == "Name: [Test Policy]\nVersion: [1]\nBanner: {undefined}\nCompressions: comp_alg1\nHost Keys: key_alg1\nOptional Host Keys: {undefined}\nKey Exchanges: kex_alg1, kex_alg2\nCiphers: cipher_alg1, cipher_alg2, cipher_alg3\nMACs: mac_alg1, mac_alg2, mac_alg3\nHost Key Sizes: {undefined}\nDH Modulus Sizes: {undefined}\nServer Policy: True"
|
||||
assert str(policy) == "Name: [Test Policy]\nVersion: [1]\nAllow Algorithm Subset and/or Reordering: False\nBanner: {undefined}\nCompressions: comp_alg1\nHost Keys: key_alg1\nOptional Host Keys: {undefined}\nKey Exchanges: kex_alg1, kex_alg2\nCiphers: cipher_alg1, cipher_alg2, cipher_alg3\nMACs: mac_alg1, mac_alg2, mac_alg3\nHost Key Sizes: {undefined}\nDH Modulus Sizes: {undefined}\nServer Policy: True"
|
||||
|
||||
|
||||
def test_policy_invalid_1(self):
|
||||
@ -291,7 +297,7 @@ macs = mac_alg1, mac_alg2, mac_alg3'''
|
||||
pol_data = pol_data.replace(date.today().strftime('%Y/%m/%d'), '[todays date]')
|
||||
|
||||
# Instead of writing out the entire expected policy--line by line--just check that it has the expected hash.
|
||||
assert hashlib.sha256(pol_data.encode('ascii')).hexdigest() == '4af7777fb57a1dad0cf438c899a11d4f625fd9276ea3bb5ef5c9fe8806cb47dc'
|
||||
assert hashlib.sha256(pol_data.encode('ascii')).hexdigest() == 'fb84bce442cff2bce9bf653d6373a8a938e3bfcfbd1e876f51a08c1842df3cff'
|
||||
|
||||
|
||||
def test_policy_evaluate_passing_1(self):
|
||||
@ -434,3 +440,96 @@ macs = mac_alg1, mac_alg2, XXXmismatchedXXX, mac_alg3'''
|
||||
assert len(errors) == 2
|
||||
assert error_str.find('Host keys did not match.') != -1
|
||||
assert error_str.find('MACs did not match.') != -1
|
||||
|
||||
|
||||
def test_policy_evaluate_subset_passing_1(self):
|
||||
'''Ensure that exact algorithm matches work even when subset parsing is enabled.'''
|
||||
|
||||
policy_data = '''name = "Test Policy"
|
||||
version = 1
|
||||
allow_algorithm_subset_and_reordering = true
|
||||
compressions = comp_alg1, comp_alg2
|
||||
host keys = key_alg1, key_alg2
|
||||
key exchanges = kex_alg1, kex_alg2
|
||||
ciphers = cipher_alg1, cipher_alg2, cipher_alg3
|
||||
macs = mac_alg1, mac_alg2, mac_alg3'''
|
||||
policy = self.Policy(policy_data=policy_data)
|
||||
ret, errors, error_str = policy.evaluate('SSH Server 1.0', self._get_kex())
|
||||
assert ret is True
|
||||
assert len(errors) == 0
|
||||
assert error_str == ""
|
||||
|
||||
|
||||
def test_policy_evaluate_subset_passing_2(self):
|
||||
'''Ensure that subset parsing works.'''
|
||||
|
||||
policy_data = '''name = "Test Policy"
|
||||
version = 1
|
||||
allow_algorithm_subset_and_reordering = true
|
||||
compressions = comp_alg1, comp_alg2
|
||||
host keys = key_alg2, key_alg1, key_alg0
|
||||
key exchanges = kex_alg3, kex_alg1, kex_alg2
|
||||
ciphers = cipher_alg0, cipher_alg3, cipher_alg2, cipher_alg1
|
||||
macs = mac_alg2, mac_alg1, mac_alg3, mac_alg0'''
|
||||
policy = self.Policy(policy_data=policy_data)
|
||||
ret, errors, error_str = policy.evaluate('SSH Server 1.0', self._get_kex())
|
||||
assert ret is True
|
||||
assert len(errors) == 0
|
||||
assert error_str == ""
|
||||
|
||||
|
||||
def test_policy_evaluate_subset_failing_1(self):
|
||||
'''Ensure that subset parsing returns a failure.'''
|
||||
|
||||
policy_data = '''name = "Test Policy"
|
||||
version = 1
|
||||
allow_algorithm_subset_and_reordering = true
|
||||
compressions = comp_alg1, comp_alg2
|
||||
host keys = key_alg7, key_alg8, key_alg9
|
||||
key exchanges = kex_alg7, kex_alg8, kex_alg9
|
||||
ciphers = cipher_alg7, cipher_alg8, cipher_alg9, cipher_alg10
|
||||
macs = mac_alg7, mac_alg8, mac_alg9, mac_alg10'''
|
||||
policy = self.Policy(policy_data=policy_data)
|
||||
ret, errors, error_str = policy.evaluate('SSH Server 1.0', self._get_kex())
|
||||
assert ret is False
|
||||
assert len(errors) == 4
|
||||
assert error_str.find("Ciphers did not match.") != -1
|
||||
assert error_str.find("Host keys did not match.") != -1
|
||||
assert error_str.find("MACs did not match") != -1
|
||||
assert error_str.find("Key exchanges did not match.") != -1
|
||||
|
||||
|
||||
def test_policy_evaluate_subset_failing_2(self):
|
||||
'''Ensure that subset parsing returns a failure when policy includes kex-strict-s-v00@openssh.com, but target does not.'''
|
||||
|
||||
policy_data = '''name = "Test Policy"
|
||||
version = 1
|
||||
allow_algorithm_subset_and_reordering = true
|
||||
compressions = comp_alg1, comp_alg2
|
||||
host keys = key_alg2, key_alg1, key_alg0
|
||||
key exchanges = kex_alg3, kex_alg1, kex_alg2, kex-strict-s-v00@openssh.com
|
||||
ciphers = cipher_alg0, cipher_alg3, cipher_alg2, cipher_alg1
|
||||
macs = mac_alg2, mac_alg1, mac_alg3, mac_alg0'''
|
||||
policy = self.Policy(policy_data=policy_data)
|
||||
ret, errors, error_str = policy.evaluate('SSH Server 1.0', self._get_kex())
|
||||
assert ret is False
|
||||
assert len(errors) == 1
|
||||
assert error_str.find("Key exchanges did not match.") != -1
|
||||
|
||||
|
||||
def test_policy_evaluate_subset_failing_3(self):
|
||||
'''Ensure that subset parsing returns a failure when policy includes kex-strict-c-v00@openssh.com, but target does not.'''
|
||||
|
||||
policy_data = '''name = "Test Policy"
|
||||
version = 1
|
||||
allow_algorithm_subset_and_reordering = true
|
||||
compressions = comp_alg1, comp_alg2
|
||||
host keys = key_alg2, key_alg1, key_alg0
|
||||
key exchanges = kex_alg3, kex_alg1, kex_alg2, kex-strict-c-v00@openssh.com
|
||||
ciphers = cipher_alg0, cipher_alg3, cipher_alg2, cipher_alg1
|
||||
macs = mac_alg2, mac_alg1, mac_alg3, mac_alg0'''
|
||||
policy = self.Policy(policy_data=policy_data)
|
||||
ret, errors, error_str = policy.evaluate('SSH Server 1.0', self._get_kex())
|
||||
assert ret is False
|
||||
assert len(errors) == 1
|
||||
assert error_str.find("Key exchanges did not match.") != -1
|
||||
|
@ -33,6 +33,7 @@ class TestSSH1:
|
||||
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):
|
||||
|
@ -32,6 +32,7 @@ class TestSSH2:
|
||||
conf.verbose = True
|
||||
conf.ssh1 = False
|
||||
conf.ssh2 = True
|
||||
conf.skip_rate_test = True
|
||||
return conf
|
||||
|
||||
@classmethod
|
||||
@ -64,7 +65,7 @@ class TestSSH2:
|
||||
def _kex_payload_with_gss(self):
|
||||
w = self.wbuf()
|
||||
w.write(b'\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff')
|
||||
w.write_list(['gss-gex-sha1-dZuIebMjgUqaxvbF7hDbAw==', 'gss-gex-sha1-vz8J1E9PzLr8b1K+0remTg==', 'gss-group14-sha1-dZuIebMjgUqaxvbF7hDbAw==', 'gss-group14-sha1-vz8J1E9PzLr8b1K+0remTg==', 'gss-group14-sha256-dZuIebMjgUqaxvbF7hDbAw==', 'gss-group14-sha256-vz8J1E9PzLr8b1K+0remTg==', 'gss-group16-sha512-dZuIebMjgUqaxvbF7hDbAw==', 'gss-group16-sha512-vz8J1E9PzLr8b1K+0remTg==', 'gss-group18-sha512-dZuIebMjgUqaxvbF7hDbAw==', 'gss-group18-sha512-vz8J1E9PzLr8b1K+0remTg==', 'gss-group1-sha1-dZuIebMjgUqaxvbF7hDbAw==', 'gss-group1-sha1-vz8J1E9PzLr8b1K+0remTg==', 'gss-curve448-sha512-XXX'])
|
||||
w.write_list(['gss-gex-sha1-dZuIebMjgUqaxvbF7hDbAw==', 'gss-gex-sha1-vz8J1E9PzLr8b1K+0remTg==', 'gss-group14-sha1-dZuIebMjgUqaxvbF7hDbAw==', 'gss-group14-sha1-vz8J1E9PzLr8b1K+0remTg==', 'gss-group14-sha256-dZuIebMjgUqaxvbF7hDbAw==', 'gss-group14-sha256-vz8J1E9PzLr8b1K+0remTg==', 'gss-group16-sha512-dZuIebMjgUqaxvbF7hDbAw==', 'gss-group16-sha512-vz8J1E9PzLr8b1K+0remTg==', 'gss-group18-sha512-dZuIebMjgUqaxvbF7hDbAw==', 'gss-group18-sha512-vz8J1E9PzLr8b1K+0remTg==', 'gss-group1-sha1-dZuIebMjgUqaxvbF7hDbAw==', 'gss-group1-sha1-vz8J1E9PzLr8b1K+0remTg==', 'gss-curve448-sha512-XXX', 'gss-nistp256-sha256-RANDOMCHARSTOTESTWILDCARDMATCHING'])
|
||||
w.write_list(['ssh-ed25519'])
|
||||
w.write_list(['chacha20-poly1305@openssh.com'])
|
||||
w.write_list(['chacha20-poly1305@openssh.com'])
|
||||
@ -164,7 +165,7 @@ class TestSSH2:
|
||||
self.audit(out, self._conf())
|
||||
out.write()
|
||||
lines = output_spy.flush()
|
||||
assert len(lines) == 70
|
||||
assert len(lines) == 83
|
||||
|
||||
def test_ssh2_server_invalid_first_packet(self, output_spy, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
|
35
tox.ini
35
tox.ini
@ -1,7 +1,7 @@
|
||||
[tox]
|
||||
envlist =
|
||||
py{py3}-{test,pylint,flake8,vulture}
|
||||
py{37,38,39,310,311}-{test,mypy,pylint,flake8,vulture}
|
||||
py{py3}-{test,pylint,flake8}
|
||||
py{38,39,310,311,312}-{test,mypy,pylint,flake8}
|
||||
cov
|
||||
skip_missing_interpreters = true
|
||||
|
||||
@ -9,11 +9,10 @@ skip_missing_interpreters = true
|
||||
deps =
|
||||
test: pytest
|
||||
test,cov: {[testenv:cov]deps}
|
||||
test,py{37,38,39,310,311}-{type,mypy}: colorama
|
||||
py{37,38,39,310,311}-{type,mypy}: {[testenv:mypy]deps}
|
||||
py{py3,37,38,39,310,311}-{lint,pylint},lint: {[testenv:pylint]deps}
|
||||
py{py3,37,38,39,310,311}-{lint,flake8},lint: {[testenv:flake8]deps}
|
||||
py{py3,37,38,39,310,311}-{lint,vulture},lint: {[testenv:vulture]deps}
|
||||
test,py{38,39,310,311,312}-{type,mypy}: colorama
|
||||
py{38,39,310,311,312}-{type,mypy}: {[testenv:mypy]deps}
|
||||
py{py3,38,39,310,311,312}-{lint,pylint},lint: {[testenv:pylint]deps}
|
||||
py{py3,38,39,310,311,312}-{lint,flake8},lint: {[testenv:flake8]deps}
|
||||
setenv =
|
||||
SSHAUDIT = {toxinidir}/src
|
||||
test: COVERAGE_FILE = {toxinidir}/.coverage.{envname}
|
||||
@ -25,10 +24,10 @@ commands =
|
||||
test: coverage combine
|
||||
test: coverage report --show-missing
|
||||
test: coverage html -d {toxinidir}/reports/html/coverage.{envname}
|
||||
py{37,38,39,310,311}-{type,mypy}: {[testenv:mypy]commands}
|
||||
py{py3,37,38,39,310,311}-{lint,pylint},lint: {[testenv:pylint]commands}
|
||||
py{py3,37,38,39,310,311}-{lint,flake8},lint: {[testenv:flake8]commands}
|
||||
py{py3,37,38,39,310,311}-{lint,vulture},lint: {[testenv:vulture]commands}
|
||||
py{38,39,310,311,312}-{type,mypy}: {[testenv:mypy]commands}
|
||||
py{py3,38,39,310,311,312}-{lint,pylint},lint: {[testenv:pylint]commands}
|
||||
py{py3,38,39,310,311,312}-{lint,flake8},lint: {[testenv:flake8]commands}
|
||||
|
||||
#ignore_outcome =
|
||||
# type: true
|
||||
# lint: true
|
||||
@ -75,17 +74,6 @@ deps =
|
||||
commands =
|
||||
flake8 {posargs:{env:SSHAUDIT} {toxinidir}/setup.py {toxinidir}/test {toxinidir}/ssh-audit.py} --statistics
|
||||
|
||||
[testenv:vulture]
|
||||
deps =
|
||||
vulture
|
||||
commands =
|
||||
python -c "import sys; from subprocess import Popen, PIPE; \
|
||||
a = ['vulture', '--min-confidence', '100'] + r'{posargs:{env:SSHAUDIT}}'.split(' '); \
|
||||
o = Popen(a, shell=False, stdout=PIPE).communicate()[0]; \
|
||||
l = [x for x in o.split(b'\n') if x and b'Unused import' not in x]; \
|
||||
print(b'\n'.join(l).decode('utf-8')); \
|
||||
sys.exit(1 if len(l) > 0 else 0)"
|
||||
|
||||
[pylint]
|
||||
reports = no
|
||||
#output-format = colorized
|
||||
@ -101,11 +89,14 @@ disable =
|
||||
no-else-return,
|
||||
super-with-arguments, # Can be re-factored, at some point.
|
||||
too-complex,
|
||||
too-many-arguments,
|
||||
too-many-boolean-expressions,
|
||||
too-many-branches,
|
||||
too-many-instance-attributes,
|
||||
too-many-lines,
|
||||
too-many-locals,
|
||||
too-many-nested-blocks,
|
||||
too-many-return-statements,
|
||||
too-many-statements,
|
||||
consider-using-f-string
|
||||
max-complexity = 15
|
||||
|
Reference in New Issue
Block a user