mirror of
https://github.com/jtesta/ssh-audit.git
synced 2024-12-23 06:27:41 +01:00
Add SSH1 and SSH2 forcing options. By default, both are allowed.
This commit is contained in:
parent
9030e71892
commit
cb19718568
80
ssh-audit.py
80
ssh-audit.py
@ -36,8 +36,10 @@ def usage(err=None):
|
|||||||
out.head('# {0} {1}, moo@arthepsy.eu'.format(p, VERSION))
|
out.head('# {0} {1}, moo@arthepsy.eu'.format(p, VERSION))
|
||||||
if err is not None:
|
if err is not None:
|
||||||
out.fail('\n' + err)
|
out.fail('\n' + err)
|
||||||
out.info('\nusage: {0} [-bnv] [-l <level>] <host[:port]>\n'.format(p))
|
out.info('\nusage: {0} [-12bnv] [-l <level>] <host[:port]>\n'.format(p))
|
||||||
out.info(' -h, --help print this help')
|
out.info(' -h, --help print this help')
|
||||||
|
out.info(' -1, --ssh1 force ssh version 1 only')
|
||||||
|
out.info(' -2, --ssh2 force ssh version 2 only')
|
||||||
out.info(' -b, --batch batch output')
|
out.info(' -b, --batch batch output')
|
||||||
out.info(' -n, --no-colors disable colors')
|
out.info(' -n, --no-colors disable colors')
|
||||||
out.info(' -v, --verbose verbose output')
|
out.info(' -v, --verbose verbose output')
|
||||||
@ -46,6 +48,45 @@ def usage(err=None):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
class AuditConf(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.__host = None
|
||||||
|
self.__port = 22
|
||||||
|
self.__ssh1 = False
|
||||||
|
self.__ssh2 = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def host(self):
|
||||||
|
return self.__host
|
||||||
|
|
||||||
|
@host.setter
|
||||||
|
def host(self, v):
|
||||||
|
self.__host = v
|
||||||
|
|
||||||
|
@property
|
||||||
|
def port(self):
|
||||||
|
return self.__port
|
||||||
|
|
||||||
|
@port.setter
|
||||||
|
def port(self, v):
|
||||||
|
self.__port = v
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ssh1(self):
|
||||||
|
return self.__ssh1
|
||||||
|
|
||||||
|
@ssh1.setter
|
||||||
|
def ssh1(self, v):
|
||||||
|
self.__ssh1 = v
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ssh2(self):
|
||||||
|
return self.__ssh2
|
||||||
|
|
||||||
|
@ssh2.setter
|
||||||
|
def ssh2(self, v):
|
||||||
|
self.__ssh2 = v
|
||||||
|
|
||||||
class Output(object):
|
class Output(object):
|
||||||
LEVELS = ['info', 'warn', 'fail']
|
LEVELS = ['info', 'warn', 'fail']
|
||||||
COLORS = {'head': 36, 'good': 32, 'warn': 33, 'fail': 31}
|
COLORS = {'head': 36, 'good': 32, 'warn': 33, 'fail': 31}
|
||||||
@ -1159,16 +1200,20 @@ def parse_int(v):
|
|||||||
|
|
||||||
|
|
||||||
def parse_args():
|
def parse_args():
|
||||||
host, port = None, 22
|
conf = AuditConf()
|
||||||
try:
|
try:
|
||||||
sopts = 'hbnvl:'
|
sopts = 'h12bnvl:'
|
||||||
lopts = ['help', 'batch', 'no-colors', 'verbose', 'level=']
|
lopts = ['help', 'ssh1', 'ssh2', 'batch', 'no-colors', 'verbose', 'level=']
|
||||||
opts, args = getopt.getopt(sys.argv[1:], sopts, lopts)
|
opts, args = getopt.getopt(sys.argv[1:], sopts, lopts)
|
||||||
except getopt.GetoptError as err:
|
except getopt.GetoptError as err:
|
||||||
usage(str(err))
|
usage(str(err))
|
||||||
for o, a in opts:
|
for o, a in opts:
|
||||||
if o in ('-h', '--help'):
|
if o in ('-h', '--help'):
|
||||||
usage()
|
usage()
|
||||||
|
elif o in ('-1', '--ssh1'):
|
||||||
|
conf.ssh1 = True
|
||||||
|
elif o in ('-2', '--ssh2'):
|
||||||
|
conf.ssh2 = True
|
||||||
elif o in ('-b', '--batch'):
|
elif o in ('-b', '--batch'):
|
||||||
out.batch = True
|
out.batch = True
|
||||||
out.verbose = True
|
out.verbose = True
|
||||||
@ -1183,16 +1228,23 @@ def parse_args():
|
|||||||
if len(args) == 0:
|
if len(args) == 0:
|
||||||
usage()
|
usage()
|
||||||
s = args[0].split(':')
|
s = args[0].split(':')
|
||||||
host = s[0].strip()
|
host, port = s[0].strip(), 22
|
||||||
if len(s) > 1:
|
if len(s) > 1:
|
||||||
port = parse_int(s[1])
|
port = parse_int(s[1])
|
||||||
if not host or port <= 0:
|
if not host or port <= 0:
|
||||||
usage('port {0} is not valid'.format(port))
|
usage('port {0} is not valid'.format(port))
|
||||||
return host, port
|
conf.host = host
|
||||||
|
conf.port = port
|
||||||
|
if not (conf.ssh1 or conf.ssh2):
|
||||||
|
conf.ssh1 = True
|
||||||
|
conf.ssh2 = True
|
||||||
|
return conf
|
||||||
|
|
||||||
|
|
||||||
def audit(host, port, sshv=2):
|
def audit(conf, sshv=None):
|
||||||
s = SSH.Socket(host, port)
|
s = SSH.Socket(conf.host, conf.port)
|
||||||
|
if sshv is None:
|
||||||
|
sshv = 2 if conf.ssh2 else 1
|
||||||
err = None
|
err = None
|
||||||
banner, header = s.get_banner(sshv)
|
banner, header = s.get_banner(sshv)
|
||||||
if banner is None:
|
if banner is None:
|
||||||
@ -1201,8 +1253,8 @@ def audit(host, port, sshv=2):
|
|||||||
packet_type, payload = s.read_packet(sshv)
|
packet_type, payload = s.read_packet(sshv)
|
||||||
if packet_type < 0:
|
if packet_type < 0:
|
||||||
if payload == b'Protocol major versions differ.':
|
if payload == b'Protocol major versions differ.':
|
||||||
if sshv == 2:
|
if sshv == 2 and conf.ssh1:
|
||||||
audit(host, port, 1)
|
audit(conf, 1)
|
||||||
return
|
return
|
||||||
err = '[exception] error reading packet ({0})'.format(payload)
|
err = '[exception] error reading packet ({0})'.format(payload)
|
||||||
else:
|
else:
|
||||||
@ -1226,11 +1278,7 @@ def audit(host, port, sshv=2):
|
|||||||
output(banner, header, kex=kex)
|
output(banner, header, kex=kex)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
host, port = parse_args()
|
|
||||||
audit(host, port)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
out = Output()
|
out = Output()
|
||||||
main()
|
conf = parse_args()
|
||||||
|
audit(conf)
|
||||||
|
Loading…
Reference in New Issue
Block a user