mirror of
https://github.com/jtesta/ssh-audit.git
synced 2026-05-26 16:01:23 +02:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c33f419224 | |||
| 6ee4899b4f | |||
| 20fbb706b0 | |||
| 73b669b49d |
@@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
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)
|
Copyright (C) 2017 Andris Raugulis (moo@arthepsy.eu)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ ifeq ($(VERSION),)
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
all:
|
all:
|
||||||
|
./add_builtin_man_page.sh
|
||||||
docker buildx create --name multiarch --use || exit 0
|
docker buildx create --name multiarch --use || exit 0
|
||||||
docker buildx build \
|
docker buildx build \
|
||||||
--platform linux/amd64,linux/arm64,linux/arm/v7 \
|
--platform linux/amd64,linux/arm64,linux/arm/v7 \
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
all:
|
all:
|
||||||
|
./add_builtin_man_page.sh
|
||||||
rm -rf /tmp/pypi_upload
|
rm -rf /tmp/pypi_upload
|
||||||
virtualenv -p /usr/bin/python3 /tmp/pypi_upload/
|
virtualenv -p /usr/bin/python3 /tmp/pypi_upload/
|
||||||
cp -R src /tmp/pypi_upload/
|
cp -R src /tmp/pypi_upload/
|
||||||
|
|||||||
@@ -57,7 +57,8 @@ usage: ssh-audit.py [options] <host>
|
|||||||
-L, --list-policies list all the official, built-in policies
|
-L, --list-policies list all the official, built-in policies
|
||||||
--lookup=<alg1,alg2,...> looks up an algorithm(s) without
|
--lookup=<alg1,alg2,...> looks up an algorithm(s) without
|
||||||
connecting to a server
|
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
|
-M, --make-policy=<policy.txt> creates a policy based on the target server
|
||||||
(i.e.: the target server has the ideal
|
(i.e.: the target server has the ideal
|
||||||
configuration that other servers should
|
configuration that other servers should
|
||||||
@@ -181,6 +182,8 @@ For convenience, a web front-end on top of the command-line tool is available at
|
|||||||
### v3.2.0-dev (???)
|
### v3.2.0-dev (???)
|
||||||
- Expanded filter of CBC ciphers to flag for the Terrapin vulnerability. It now includes more rarely found ciphers.
|
- Expanded filter of CBC ciphers to flag for the Terrapin vulnerability. It now includes more rarely found ciphers.
|
||||||
- Color output is disabled if the `NO_COLOR` environment variable is set (see https://no-color.org/).
|
- Color output is disabled if the `NO_COLOR` environment variable is set (see https://no-color.org/).
|
||||||
|
- 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.
|
||||||
|
- The built-in man page (`-m`, `--manual`) is now available on Docker, PyPI, and Snap builds, in addition to the Windows build.
|
||||||
|
|
||||||
### v3.1.0 (2023-12-20)
|
### 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)).
|
- Added test for the Terrapin message prefix truncation vulnerability ([CVE-2023-48795](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-48795)).
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#
|
#
|
||||||
# The MIT License (MIT)
|
# 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>)
|
# 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
|
# 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
|
# PURPOSE
|
||||||
# Since Windows lacks a manual reader it's necessary to provide an alternative
|
# Since some platforms lack a manual reader it's necessary to provide an
|
||||||
# means of reading the man page.
|
# alternative means of reading the man page.
|
||||||
#
|
#
|
||||||
# This script should be run as part of the ssh-audit packaging process for
|
# 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
|
# Docker, PyPI, Snap, and Windows. It populates the 'BUILTIN_MAN_PAGE'
|
||||||
# the contents of the man page. Windows users can then print the content of
|
# variable in 'globals.py' with the contents of the man page. Users can then
|
||||||
# 'WINDOWS_MAN_PAGE' by invoking ssh-audit with the manual parameters
|
# see the man page with "ssh-audit [--manual|-m]".
|
||||||
# (--manual / -m).
|
|
||||||
#
|
#
|
||||||
# Cygwin is required.
|
# Linux or Cygwin is required to run this script.
|
||||||
#
|
#
|
||||||
# USAGE
|
# 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
|
git checkout "${GLOBALS_PY}" > /dev/null 2>&1
|
||||||
|
|
||||||
# Remove the Windows man page placeholder from 'globals.py'.
|
# 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}..."
|
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
|
# escape sequence. Not required under Cygwin because man outputs ANSI escape
|
||||||
# codes automatically.
|
# codes automatically.
|
||||||
|
|
||||||
echo WINDOWS_MAN_PAGE = '"""' >> "${GLOBALS_PY}"
|
echo BUILTIN_MAN_PAGE = '"""' >> "${GLOBALS_PY}"
|
||||||
|
|
||||||
if [[ "${PLATFORM}" == CYGWIN* ]]; then
|
if [[ "${PLATFORM}" == CYGWIN* ]]; then
|
||||||
MANWIDTH=80 MAN_KEEP_FORMATTING=1 man "${MAN_PAGE}" | sed $'s/\u2010/-/g' >> "${GLOBALS_PY}"
|
MANWIDTH=80 MAN_KEEP_FORMATTING=1 man "${MAN_PAGE}" | sed $'s/\u2010/-/g' >> "${GLOBALS_PY}"
|
||||||
+4
-1
@@ -3,7 +3,7 @@
|
|||||||
#
|
#
|
||||||
# The MIT License (MIT)
|
# 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
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -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 snapcraft.yaml 2> /dev/null
|
||||||
git checkout src/ssh_audit/globals.py 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.
|
# Get the version from the globals.py file.
|
||||||
version=$(grep VERSION src/ssh_audit/globals.py | awk 'BEGIN {FS="="} ; {print $2}' | tr -d '[:space:]')
|
version=$(grep VERSION src/ssh_audit/globals.py | awk 'BEGIN {FS="="} ; {print $2}' | tr -d '[:space:]')
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#
|
#
|
||||||
# The MIT License (MIT)
|
# 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
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -77,7 +77,7 @@ fi
|
|||||||
git checkout src/ssh_audit/globals.py 2> /dev/null
|
git checkout src/ssh_audit/globals.py 2> /dev/null
|
||||||
|
|
||||||
# Update the man page.
|
# Update the man page.
|
||||||
./update_windows_man_page.sh
|
./add_builtin_man_page.sh
|
||||||
retval=$?
|
retval=$?
|
||||||
if [[ ${retval} != 0 ]]; then
|
if [[ ${retval} != 0 ]]; then
|
||||||
echo "Failed to run ./update_windows_man_page.sh"
|
echo "Failed to run ./update_windows_man_page.sh"
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ SSH_HEADER = 'SSH-{0}-OpenSSH_8.2'
|
|||||||
# The URL to the Github issues tracker.
|
# The URL to the Github issues tracker.
|
||||||
GITHUB_ISSUES_URL = 'https://github.com/jtesta/ssh-audit/issues'
|
GITHUB_ISSUES_URL = 'https://github.com/jtesta/ssh-audit/issues'
|
||||||
|
|
||||||
# The man page. Only filled in on Windows systems.
|
# The man page. Only filled in on Docker, PyPI, Snap, and Windows builds.
|
||||||
WINDOWS_MAN_PAGE = ''
|
BUILTIN_MAN_PAGE = ''
|
||||||
|
|
||||||
# True when installed from a Snap package, otherwise False.
|
# True when installed from a Snap package, otherwise False.
|
||||||
SNAP_PACKAGE = False
|
SNAP_PACKAGE = False
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ class HostKeyTest:
|
|||||||
hostkey_min_good = 256
|
hostkey_min_good = 256
|
||||||
hostkey_min_warn = 224
|
hostkey_min_warn = 224
|
||||||
hostkey_warn_str = HostKeyTest.SMALL_ECC_MODULUS_WARNING
|
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_good = 256
|
||||||
cakey_min_warn = 224
|
cakey_min_warn = 224
|
||||||
cakey_warn_str = HostKeyTest.SMALL_ECC_MODULUS_WARNING
|
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):
|
elif (0 < ca_modulus_size < cakey_min_good) and (cakey_warn_str not in key_warn_comments):
|
||||||
key_warn_comments.append(cakey_warn_str)
|
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 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:
|
if host_key_type in HostKeyTest.RSA_FAMILY:
|
||||||
for rsa_type in HostKeyTest.RSA_FAMILY:
|
for rsa_type in HostKeyTest.RSA_FAMILY:
|
||||||
|
|||||||
@@ -212,6 +212,15 @@ class KexDH: # pragma: nocover
|
|||||||
# CA's modulus. Bingo.
|
# CA's modulus. Bingo.
|
||||||
ca_key_n, ca_key_n_len, ptr = KexDH.__get_bytes(ca_key, ptr) # pylint: disable=unused-variable
|
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:
|
else:
|
||||||
self.out.d("Certificate type %u found; this is not usually valid in the context of a host key! Skipping it..." % cert_type)
|
self.out.d("Certificate type %u found; this is not usually valid in the context of a host key! Skipping it..." % cert_type)
|
||||||
|
|
||||||
|
|||||||
+11
-13
@@ -39,7 +39,7 @@ from typing import cast, Callable, Optional, Union, Any # noqa: F401
|
|||||||
from ssh_audit.globals import SNAP_PACKAGE
|
from ssh_audit.globals import SNAP_PACKAGE
|
||||||
from ssh_audit.globals import SNAP_PERMISSIONS_ERROR
|
from ssh_audit.globals import SNAP_PERMISSIONS_ERROR
|
||||||
from ssh_audit.globals import VERSION
|
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.algorithm import Algorithm
|
||||||
from ssh_audit.algorithms import Algorithms
|
from ssh_audit.algorithms import Algorithms
|
||||||
from ssh_audit.auditconf import AuditConf
|
from ssh_audit.auditconf import AuditConf
|
||||||
@@ -1416,23 +1416,21 @@ def target_worker_thread(host: str, port: int, shared_aconf: AuditConf) -> Tuple
|
|||||||
return ret, string_output
|
return ret, string_output
|
||||||
|
|
||||||
|
|
||||||
def windows_manual(out: OutputBuffer) -> int:
|
def builtin_manual(out: OutputBuffer) -> int:
|
||||||
'''Prints the man page on Windows. Returns an exitcodes.* flag.'''
|
'''Prints the man page (Docker, PyPI, Snap, and Windows builds only). Returns an exitcodes.* flag.'''
|
||||||
|
|
||||||
retval = exitcodes.GOOD
|
|
||||||
|
|
||||||
if sys.platform != 'win32':
|
builtin_man_page = BUILTIN_MAN_PAGE
|
||||||
out.fail("The '-m' and '--manual' parameters are reserved for use on Windows only.\nUsers of other operating systems should read the man page.")
|
if builtin_man_page == "":
|
||||||
retval = exitcodes.FAILURE
|
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 retval
|
return exitcodes.FAILURE
|
||||||
|
|
||||||
# If colors are disabled, strip the ANSI color codes from the man page.
|
# If colors are disabled, strip the ANSI color codes from the man page.
|
||||||
windows_man_page = WINDOWS_MAN_PAGE
|
|
||||||
if not out.use_colors:
|
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)
|
out.info(builtin_man_page)
|
||||||
return retval
|
return exitcodes.GOOD
|
||||||
|
|
||||||
|
|
||||||
def get_permitted_syntax_for_gex_test() -> Dict[str, str]:
|
def get_permitted_syntax_for_gex_test() -> Dict[str, str]:
|
||||||
@@ -1526,7 +1524,7 @@ def main() -> int:
|
|||||||
# to output a plain text version of the man page.
|
# to output a plain text version of the man page.
|
||||||
if (sys.platform == 'win32') and ('colorama' not in sys.modules):
|
if (sys.platform == 'win32') and ('colorama' not in sys.modules):
|
||||||
out.use_colors = False
|
out.use_colors = False
|
||||||
retval = windows_manual(out)
|
retval = builtin_manual(out)
|
||||||
out.write()
|
out.write()
|
||||||
sys.exit(retval)
|
sys.exit(retval)
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
.TH SSH-AUDIT 1 "January 28, 2024"
|
.TH SSH-AUDIT 1 "February 16, 2024"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
\fBssh-audit\fP \- SSH server & client configuration auditor
|
\fBssh-audit\fP \- SSH server & client configuration auditor
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@@ -104,7 +104,7 @@ Look up the security information of an algorithm(s) in the internal database. D
|
|||||||
.TP
|
.TP
|
||||||
.B -m, \-\-manual
|
.B -m, \-\-manual
|
||||||
.br
|
.br
|
||||||
Print the man page (Windows only).
|
Print the man page (Docker, PyPI, Snap, and Windows builds only).
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B -M, \-\-make-policy=<custom_policy.txt>
|
.B -M, \-\-make-policy=<custom_policy.txt>
|
||||||
|
|||||||
Reference in New Issue
Block a user