mirror of
https://github.com/jtesta/ssh-audit.git
synced 2024-11-25 20:11:40 +01:00
Extract banner and recognize other SSH1 banners (e.g, 1.3-1.5).
This commit is contained in:
parent
f7cd4fd954
commit
19ee986e3d
60
ssh-audit.py
60
ssh-audit.py
@ -24,7 +24,7 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
"""
|
"""
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import os, io, sys, socket, struct, random, errno, getopt
|
import os, io, sys, socket, struct, random, errno, getopt, re
|
||||||
|
|
||||||
VERSION = 'v1.0.20160902'
|
VERSION = 'v1.0.20160902'
|
||||||
SSH_BANNER = 'SSH-2.0-OpenSSH_7.3'
|
SSH_BANNER = 'SSH-2.0-OpenSSH_7.3'
|
||||||
@ -209,6 +209,53 @@ class SSH(object):
|
|||||||
MSG_KEXDH_INIT = 30
|
MSG_KEXDH_INIT = 30
|
||||||
MSG_KEXDH_REPLY = 32
|
MSG_KEXDH_REPLY = 32
|
||||||
|
|
||||||
|
class Banner(object):
|
||||||
|
def __init__(self, protocol, software, comments):
|
||||||
|
self.__protocol = protocol
|
||||||
|
self.__software = software
|
||||||
|
self.__comments = comments
|
||||||
|
|
||||||
|
@property
|
||||||
|
def protocol(self):
|
||||||
|
return self.__protocol
|
||||||
|
|
||||||
|
@property
|
||||||
|
def software(self):
|
||||||
|
return self.__software
|
||||||
|
|
||||||
|
@property
|
||||||
|
def comments(self):
|
||||||
|
return self.__comments
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
out = 'SSH-{0}.{1}'.format(self.protocol[0], self.protocol[1])
|
||||||
|
if self.software is not None:
|
||||||
|
out += '-{0}'.format(self.software)
|
||||||
|
if self.comments:
|
||||||
|
out += ' {0}'.format(self.comments)
|
||||||
|
return out
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
p = '{0}.{1}'.format(self.protocol[0], self.protocol[1])
|
||||||
|
out = 'protocol={0}'.format(p)
|
||||||
|
if self.software:
|
||||||
|
out += ', software={0}'.format(self.software)
|
||||||
|
if self.comments:
|
||||||
|
out += ', comments={0}'.format(self.comments)
|
||||||
|
return '<{0}({1})>'.format(self.__class__.__name__, out)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def parse(cls, banner):
|
||||||
|
mx = re.match(r'^SSH-(\d)\.\s*?(\d+)(|-([^\s]*)(\s+(.*))?)$', banner)
|
||||||
|
if mx is None:
|
||||||
|
return None
|
||||||
|
protocol = (int(mx.group(1)), int(mx.group(2)))
|
||||||
|
software = (mx.group(4) or '').strip() or None
|
||||||
|
if software is None and mx.group(3).startswith('-'):
|
||||||
|
software = ''
|
||||||
|
comments = (mx.group(6) or '').strip() or None
|
||||||
|
return cls(protocol, software, comments)
|
||||||
|
|
||||||
class Socket(ReadBuf, WriteBuf):
|
class Socket(ReadBuf, WriteBuf):
|
||||||
SM_BANNER_SENT = 1
|
SM_BANNER_SENT = 1
|
||||||
|
|
||||||
@ -239,9 +286,10 @@ class SSH(object):
|
|||||||
line = self.read_line()
|
line = self.read_line()
|
||||||
if len(line.strip()) == 0:
|
if len(line.strip()) == 0:
|
||||||
continue
|
continue
|
||||||
if line.startswith('SSH-'):
|
if self.__banner is None:
|
||||||
self.__banner = line
|
self.__banner = SSH.Banner.parse(line)
|
||||||
else:
|
if self.__banner is not None:
|
||||||
|
continue
|
||||||
self.__header.append(line)
|
self.__header.append(line)
|
||||||
return self.__banner, self.__header
|
return self.__banner, self.__header
|
||||||
|
|
||||||
@ -617,8 +665,8 @@ def output(banner, header, kex):
|
|||||||
if len(header) > 0:
|
if len(header) > 0:
|
||||||
out.info('(gen) header: ' + '\n'.join(header))
|
out.info('(gen) header: ' + '\n'.join(header))
|
||||||
if banner is not None:
|
if banner is not None:
|
||||||
out.good('(gen) banner: ' + banner)
|
out.good('(gen) banner: {0}'.format(banner))
|
||||||
if banner.startswith('SSH-1.99-'):
|
if banner.protocol[0] == 1:
|
||||||
out.fail('(gen) protocol SSH1 enabled')
|
out.fail('(gen) protocol SSH1 enabled')
|
||||||
if kex is not None:
|
if kex is not None:
|
||||||
output_compatibility(kex)
|
output_compatibility(kex)
|
||||||
|
Loading…
Reference in New Issue
Block a user