Recognize libssh (software, history, compatibility, security, etc). Closes #8.

This commit is contained in:
Andris Raugulis 2016-10-04 10:27:27 +03:00
parent 0c98bc1397
commit 5de7b913fd
2 changed files with 109 additions and 44 deletions

View File

@ -472,6 +472,7 @@ class SSH(object):
class Product(object):
OpenSSH = 'OpenSSH'
DropbearSSH = 'Dropbear SSH'
LibSSH = 'libssh'
class Software(object):
def __init__(self, vendor, product, version, patch, os):
@ -632,6 +633,12 @@ class SSH(object):
v = None
os = cls._extract_os(banner.comments)
return cls(v, p, mx.group(1), patch, os)
mx = re.match(r'^libssh-([\d\.]+\d+)(.*)', software)
if mx:
patch = cls._fix_patch(mx.group(2))
v, p = None, SSH.Product.LibSSH
os = cls._extract_os(banner.comments)
return cls(v, p, mx.group(1), patch, os)
mx = re.match(r'^RomSShell_([\d\.]+\d+)(.*)', software)
if mx:
patch = cls._fix_patch(mx.group(2))
@ -718,21 +725,34 @@ class SSH(object):
class Security(object):
CVE = {
'Dropbear SSH': [
['0.44', '2015.71', 1, 'CVE-2016-3116', 5.5, 'bypass command restrictions via xauth command injection.'],
['0.28', '2013.58', 1, 'CVE-2013-4434', 5.0, 'discover valid usernames through different time delays.'],
['0.28', '2013.58', 1, 'CVE-2013-4421', 5.0, 'cause DoS (memory consumption) via a compressed packet.'],
['0.52', '2011.54', 1, 'CVE-2012-0920', 7.1, 'execute arbitrary code or bypass command restrictions.'],
['0.40', '0.48.1', 1, 'CVE-2007-1099', 7.5, 'conduct a MitM attack (no warning for hostkey mismatch).'],
['0.28', '0.47', 1, 'CVE-2006-1206', 7.5, 'cause DoS (slot exhaustion) via large number of connections.'],
['0.39', '0.47', 1, 'CVE-2006-0225', 4.6, 'execute arbitrary commands via scp with crafted filenames.'],
['0.28', '0.46', 1, 'CVE-2005-4178', 6.5, 'execute arbitrary code via buffer overflow vulnerability.'],
['0.28', '0.42', 1, 'CVE-2004-2486', 7.5, 'execute arbitrary code via DSS verification code.'],
]
['0.44', '2015.71', 1, 'CVE-2016-3116', 5.5, 'bypass command restrictions via xauth command injection'],
['0.28', '2013.58', 1, 'CVE-2013-4434', 5.0, 'discover valid usernames through different time delays'],
['0.28', '2013.58', 1, 'CVE-2013-4421', 5.0, 'cause DoS (memory consumption) via a compressed packet'],
['0.52', '2011.54', 1, 'CVE-2012-0920', 7.1, 'execute arbitrary code or bypass command restrictions'],
['0.40', '0.48.1', 1, 'CVE-2007-1099', 7.5, 'conduct a MitM attack (no warning for hostkey mismatch)'],
['0.28', '0.47', 1, 'CVE-2006-1206', 7.5, 'cause DoS (slot exhaustion) via large number of connections'],
['0.39', '0.47', 1, 'CVE-2006-0225', 4.6, 'execute arbitrary commands via scp with crafted filenames'],
['0.28', '0.46', 1, 'CVE-2005-4178', 6.5, 'execute arbitrary code via buffer overflow vulnerability'],
['0.28', '0.42', 1, 'CVE-2004-2486', 7.5, 'execute arbitrary code via DSS verification code']],
'libssh': [
['0.1', '0.7.2', 1, 'CVE-2016-0739', 4.3, 'conduct a MitM attack (weakness in DH key generation)'],
['0.5.1', '0.6.4', 1, 'CVE-2015-3146', 5.0, 'cause DoS via kex packets (null pointer dereference)'],
['0.5.1', '0.6.3', 1, 'CVE-2014-8132', 5.0, 'cause DoS via kex init packet (dangling pointer)'],
['0.4.7', '0.6.2', 1, 'CVE-2014-0017', 1.9, 'leak data via PRNG state reuse on forking servers'],
['0.4.7', '0.5.3', 1, 'CVE-2013-0176', 4.3, 'cause DoS via kex packet (null pointer dereference)'],
['0.4.7', '0.5.2', 1, 'CVE-2012-6063', 7.5, 'cause DoS or execute arbitrary code via sftp (double free)'],
['0.4.7', '0.5.2', 1, 'CVE-2012-4562', 7.5, 'cause DoS or execute arbitrary code (overflow check)'],
['0.4.7', '0.5.2', 1, 'CVE-2012-4561', 5.0, 'cause DoS via unspecified vectors (invalid pointer)'],
['0.4.7', '0.5.2', 1, 'CVE-2012-4560', 7.5, 'cause DoS or execute arbitrary code (buffer overflow)'],
['0.4.7', '0.5.2', 1, 'CVE-2012-4559', 6.8, 'cause DoS or execute arbitrary code (double free)']]
}
TXT = {
'Dropbear SSH': [
['0.28', '0.34', 1, 'remote root exploit', 'remote format string buffer overflow exploit (exploit-db#387).'],
]
['0.28', '0.34', 1, 'remote root exploit', 'remote format string buffer overflow exploit (exploit-db#387)']],
'libssh': [
['0.3.3', '0.3.3', 1, 'null pointer check', 'missing null pointer check in "crypt_set_algorithms_server"'],
['0.3.3', '0.3.3', 1, 'integer overflow', 'integer overflow in "buffer_get_data"'],
['0.3.3', '0.3.3', 3, 'heap overflow', 'heap overflow in "packet_decrypt"']]
}
class Socket(ReadBuf, WriteBuf):
@ -964,29 +984,29 @@ class KexDB(object):
ALGORITHMS = {
'kex': {
'diffie-hellman-group1-sha1': [['2.3.0,d0.28', '6.6', '6.9'], [FAIL_OPENSSH67_UNSAFE, FAIL_OPENSSH70_LOGJAM], [WARN_MODULUS_SIZE, WARN_HASH_WEAK]],
'diffie-hellman-group14-sha1': [['3.9,d0.53'], [], [WARN_HASH_WEAK]],
'diffie-hellman-group1-sha1': [['2.3.0,d0.28,l10.2', '6.6', '6.9'], [FAIL_OPENSSH67_UNSAFE, FAIL_OPENSSH70_LOGJAM], [WARN_MODULUS_SIZE, WARN_HASH_WEAK]],
'diffie-hellman-group14-sha1': [['3.9,d0.53,l10.6.0'], [], [WARN_HASH_WEAK]],
'diffie-hellman-group14-sha256': [['7.3,d2016.73']],
'diffie-hellman-group16-sha512': [['7.3,d2016.73']],
'diffie-hellman-group18-sha512': [['7.3']],
'diffie-hellman-group-exchange-sha1': [['2.3.0', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_HASH_WEAK]],
'diffie-hellman-group-exchange-sha256': [['4.4'], [], [WARN_MODULUS_CUSTOM]],
'ecdh-sha2-nistp256': [['5.7,d2013.62'], [WARN_CURVES_WEAK]],
'ecdh-sha2-nistp256': [['5.7,d2013.62,l10.6.0'], [WARN_CURVES_WEAK]],
'ecdh-sha2-nistp384': [['5.7,d2013.62'], [WARN_CURVES_WEAK]],
'ecdh-sha2-nistp521': [['5.7,d2013.62'], [WARN_CURVES_WEAK]],
'curve25519-sha256@libssh.org': [['6.5,d2013.62']],
'curve25519-sha256@libssh.org': [['6.5,d2013.62,l10.6.0']],
'kexguess2@matt.ucc.asn.au': [['d2013.57']],
},
'key': {
'rsa-sha2-256': [['7.2']],
'rsa-sha2-512': [['7.2']],
'ssh-ed25519': [['6.5']],
'ssh-ed25519': [['6.5,l10.7.0']],
'ssh-ed25519-cert-v01@openssh.com': [['6.5']],
'ssh-rsa': [['2.5.0,d0.28']],
'ssh-dss': [['2.1.0,d0.28', '6.9'], [FAIL_OPENSSH70_WEAK], [WARN_MODULUS_SIZE, WARN_RNDSIG_KEY]],
'ecdsa-sha2-nistp256': [['5.7,d2013.62'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
'ecdsa-sha2-nistp384': [['5.7,d2013.62'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
'ecdsa-sha2-nistp521': [['5.7,d2013.62'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
'ssh-rsa': [['2.5.0,d0.28,l10.2']],
'ssh-dss': [['2.1.0,d0.28,l10.2', '6.9'], [FAIL_OPENSSH70_WEAK], [WARN_MODULUS_SIZE, WARN_RNDSIG_KEY]],
'ecdsa-sha2-nistp256': [['5.7,d2013.62,l10.6.4'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
'ecdsa-sha2-nistp384': [['5.7,d2013.62,l10.6.4'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
'ecdsa-sha2-nistp521': [['5.7,d2013.62,l10.6.4'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
'ssh-rsa-cert-v00@openssh.com': [['5.4', '6.9'], [FAIL_OPENSSH70_LEGACY], []],
'ssh-dss-cert-v00@openssh.com': [['5.4', '6.9'], [FAIL_OPENSSH70_LEGACY], [WARN_MODULUS_SIZE, WARN_RNDSIG_KEY]],
'ssh-rsa-cert-v01@openssh.com': [['5.6']],
@ -996,10 +1016,10 @@ class KexDB(object):
'ecdsa-sha2-nistp521-cert-v01@openssh.com': [['5.7'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
},
'enc': {
'none': [['1.2.2,d2013.56'], [FAIL_PLAINTEXT]],
'3des-cbc': [['1.2.2,d0.28', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_CIPHER_WEAK, WARN_CIPHER_MODE, WARN_BLOCK_SIZE]],
'none': [['1.2.2,d2013.56,l10.2'], [FAIL_PLAINTEXT]],
'3des-cbc': [['1.2.2,d0.28,l10.2', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_CIPHER_WEAK, WARN_CIPHER_MODE, WARN_BLOCK_SIZE]],
'3des-ctr': [['d0.52']],
'blowfish-cbc': [['1.2.2,d0.28', '6.6,d0.52', '7.1,d0.52'], [FAIL_OPENSSH67_UNSAFE, FAIL_DBEAR53_DISABLED], [WARN_OPENSSH72_LEGACY, WARN_CIPHER_MODE, WARN_BLOCK_SIZE]],
'blowfish-cbc': [['1.2.2,d0.28,l10.2', '6.6,d0.52', '7.1,d0.52'], [FAIL_OPENSSH67_UNSAFE, FAIL_DBEAR53_DISABLED], [WARN_OPENSSH72_LEGACY, WARN_CIPHER_MODE, WARN_BLOCK_SIZE]],
'twofish-cbc': [['d0.28', 'd2014.66'], [FAIL_DBEAR67_DISABLED], [WARN_CIPHER_MODE]],
'twofish128-cbc': [['d0.47', 'd2014.66'], [FAIL_DBEAR67_DISABLED], [WARN_CIPHER_MODE]],
'twofish256-cbc': [['d0.47', 'd2014.66'], [FAIL_DBEAR67_DISABLED], [WARN_CIPHER_MODE]],
@ -1009,27 +1029,27 @@ class KexDB(object):
'arcfour': [['2.1.0', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_CIPHER_WEAK]],
'arcfour128': [['4.2', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_CIPHER_WEAK]],
'arcfour256': [['4.2', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_CIPHER_WEAK]],
'aes128-cbc': [['2.3.0,d0.28', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_CIPHER_MODE]],
'aes192-cbc': [['2.3.0', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_CIPHER_MODE]],
'aes256-cbc': [['2.3.0,d0.47', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_CIPHER_MODE]],
'aes128-cbc': [['2.3.0,d0.28,l10.2', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_CIPHER_MODE]],
'aes192-cbc': [['2.3.0,l10.2', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_CIPHER_MODE]],
'aes256-cbc': [['2.3.0,d0.47,l10.2', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_CIPHER_MODE]],
'rijndael128-cbc': [['2.3.0', '3.0.2'], [FAIL_OPENSSH31_REMOVE], [WARN_CIPHER_MODE]],
'rijndael192-cbc': [['2.3.0', '3.0.2'], [FAIL_OPENSSH31_REMOVE], [WARN_CIPHER_MODE]],
'rijndael256-cbc': [['2.3.0', '3.0.2'], [FAIL_OPENSSH31_REMOVE], [WARN_CIPHER_MODE]],
'rijndael-cbc@lysator.liu.se': [['2.3.0', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_CIPHER_MODE]],
'aes128-ctr': [['3.7,d0.52']],
'aes192-ctr': [['3.7']],
'aes256-ctr': [['3.7,d0.52']],
'aes128-ctr': [['3.7,d0.52,l10.4.1']],
'aes192-ctr': [['3.7,l10.4.1']],
'aes256-ctr': [['3.7,d0.52,l10.4.1']],
'aes128-gcm@openssh.com': [['6.2']],
'aes256-gcm@openssh.com': [['6.2']],
'chacha20-poly1305@openssh.com': [['6.5'], [], [], [INFO_OPENSSH69_CHACHA]],
},
'mac': {
'none': [['d2013.56'], [FAIL_PLAINTEXT]],
'hmac-sha1': [['2.1.0,d0.28'], [], [WARN_ENCRYPT_AND_MAC, WARN_HASH_WEAK]],
'hmac-sha1': [['2.1.0,d0.28,l10.2'], [], [WARN_ENCRYPT_AND_MAC, WARN_HASH_WEAK]],
'hmac-sha1-96': [['2.5.0,d0.47', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_ENCRYPT_AND_MAC, WARN_HASH_WEAK]],
'hmac-sha2-256': [['5.9,d2013.56'], [], [WARN_ENCRYPT_AND_MAC]],
'hmac-sha2-256': [['5.9,d2013.56,l10.7.0'], [], [WARN_ENCRYPT_AND_MAC]],
'hmac-sha2-256-96': [['5.9', '6.0'], [FAIL_OPENSSH61_REMOVE], [WARN_ENCRYPT_AND_MAC]],
'hmac-sha2-512': [['5.9,d2013.56'], [], [WARN_ENCRYPT_AND_MAC]],
'hmac-sha2-512': [['5.9,d2013.56,l10.7.0'], [], [WARN_ENCRYPT_AND_MAC]],
'hmac-sha2-512-96': [['5.9', '6.0'], [FAIL_OPENSSH61_REMOVE], [WARN_ENCRYPT_AND_MAC]],
'hmac-md5': [['2.1.0,d0.28', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_ENCRYPT_AND_MAC, WARN_HASH_WEAK]],
'hmac-md5-96': [['2.5.0', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_ENCRYPT_AND_MAC, WARN_HASH_WEAK]],
@ -1053,6 +1073,8 @@ class KexDB(object):
def get_ssh_version(version_desc):
if version_desc.startswith('d'):
return (SSH.Product.DropbearSSH, version_desc[1:])
elif version_desc.startswith('l1'):
return (SSH.Product.LibSSH, version_desc[2:])
else:
return (SSH.Product.OpenSSH, version_desc)
@ -1116,6 +1138,8 @@ def get_alg_since_text(alg_desc):
ssh_prefix, ssh_version = get_ssh_version(v)
if not ssh_version:
continue
if ssh_prefix in [SSH.Product.LibSSH]:
continue
if ssh_version.endswith('C'):
ssh_version = '{0} (client only)'.format(ssh_version[:-1])
tv.append('{0} {1}'.format(ssh_prefix, ssh_version))
@ -1142,12 +1166,15 @@ def get_alg_pairs(kex, pkm):
def get_alg_recommendations(software, kex, pkm, for_server=True):
alg_pairs = get_alg_pairs(kex, pkm)
vproducts = [SSH.Product.OpenSSH,
SSH.Product.DropbearSSH,
SSH.Product.LibSSH]
if software is not None:
if software.product not in [SSH.Product.OpenSSH, SSH.Product.DropbearSSH]:
if software.product not in vproducts:
software = None
if software is None:
ssh_timeframe = get_ssh_timeframe(alg_pairs, for_server)
for product in [SSH.Product.OpenSSH, SSH.Product.DropbearSSH]:
for product in vproducts:
if product not in ssh_timeframe:
continue
version = ssh_timeframe[product][0]
@ -1302,7 +1329,8 @@ def output_security_sub(sub, software, padlen):
continue
target, name = line[2:4]
is_server, is_client = target & 1 == 1, target & 2 == 2
if is_client:
is_local = target & 4 == 4
if not is_server:
continue
p = '' if out.batch else ' ' * (padlen - len(name))
if sub == 'cve':

View File

@ -2,10 +2,11 @@
# -*- coding: utf-8 -*-
import pytest
class TestVersionCompare(object):
@pytest.fixture(autouse=True)
def init(self, ssh_audit):
self.ssh = ssh_audit.SSH
self.ssh = ssh_audit.SSH
def get_dropbear_software(self, v):
b = self.ssh.Banner.parse('SSH-2.0-dropbear_{0}'.format(v))
@ -15,26 +16,30 @@ class TestVersionCompare(object):
b = self.ssh.Banner.parse('SSH-2.0-OpenSSH_{0}'.format(v))
return self.ssh.Software.parse(b)
def get_libssh_software(self, v):
b = self.ssh.Banner.parse('SSH-2.0-libssh-{0}'.format(v))
return self.ssh.Software.parse(b)
def test_dropbear_compare_version_pre_years(self):
s = self.get_dropbear_software('0.44')
assert s.compare_version('0.43') > 0
assert s.compare_version('0.44') == 0
assert s.compare_version('0.45') < 0
assert s.between_versions('0.43', '0.45') == True
assert s.between_versions('0.43', '0.45')
def test_dropbear_compare_version_with_years(self):
s = self.get_dropbear_software('2015.71')
assert s.compare_version('2014.67') > 0
assert s.compare_version('2015.71') == 0
assert s.compare_version('2016.74') < 0
assert s.between_versions('2014.67', '2016.74') == True
assert s.between_versions('2014.67', '2016.74')
def test_dropbear_compare_version_mixed(self):
s = self.get_dropbear_software('0.53.1')
assert s.compare_version('0.53') > 0
assert s.compare_version('0.53.1') == 0
assert s.compare_version('2011.54') < 0
assert s.between_versions('0.53', '2011.54') == True
assert s.between_versions('0.53', '2011.54')
def test_dropbear_compare_version_patchlevel(self):
s1 = self.get_dropbear_software('0.44')
@ -85,9 +90,8 @@ class TestVersionCompare(object):
assert s.compare_version('3.7') > 0
assert s.compare_version('3.7.1') == 0
assert s.compare_version('3.8') < 0
assert s.between_versions('3.7', '3.8') == True
assert s.between_versions('3.7', '3.8')
def test_openssh_compare_version_patchlevel(self):
s1 = self.get_openssh_software('2.1.1')
s2 = self.get_openssh_software('2.1.1p2')
@ -130,3 +134,36 @@ class TestVersionCompare(object):
if i + 1 < l:
vnext = versions[i + 1]
assert s.compare_version(vnext) < 0
def test_libssh_compare_version_simple(self):
s = self.get_libssh_software('0.3')
assert s.compare_version('0.2') > 0
assert s.compare_version('0.3') == 0
assert s.compare_version('0.3.1') < 0
assert s.between_versions('0.2', '0.3.1')
def test_libssh_compare_version_sequential(self):
versions = []
for v in ['0.2', '0.3']:
versions.append(v)
for i in range(1, 5):
versions.append('0.3.{0}'.format(i))
for i in range(0, 9):
versions.append('0.4.{0}'.format(i))
for i in range(0, 6):
versions.append('0.5.{0}'.format(i))
for i in range(0, 6):
versions.append('0.6.{0}'.format(i))
for i in range(0, 4):
versions.append('0.7.{0}'.format(i))
l = len(versions)
for i in range(l):
v = versions[i]
s = self.get_libssh_software(v)
assert s.compare_version(v) == 0
if i - 1 >= 0:
vbefore = versions[i - 1]
assert s.compare_version(vbefore) > 0
if i + 1 < l:
vnext = versions[i + 1]
assert s.compare_version(vnext) < 0