The_Defalt

smtp_user_enumerator.py

Jul 25th, 2018
1,536
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #! /usr/bin/python
  2.  
  3. # whats up its defalt
  4. # this script an SMTP user enumerator
  5. # it can use VRFY, EXPN, and RCPT TO methods
  6. # the SMTPUserEnumerator class can also be imported into other scripts
  7. # happy hacking! :) -defalt
  8.  
  9. import socket
  10.  
  11. class SMTPUserEnumerator():
  12.     def __init__(self, target, userlist, port=25, scantype="vrfy", mailfrom="root"):
  13.         # set init args as attributes
  14.         self.target = target
  15.         self.userlist = userlist
  16.         self.port = port
  17.         self.scantype = scantype
  18.         self.mailfrom = [mailfrom, True] # boolean used for one-time sending of MAIL FROM command for RCPT scan
  19.  
  20.         self.sock = None # attribute for socket connected to target, generate with buildSock()
  21.         self.targetBanner = None # banner from target server, stored upon vuln test
  22.  
  23.     def readUsers(self): # func to read file of usernames
  24.         with open(self.userlist, 'r') as file:
  25.             users = file.read().strip().split('\n') # read all, strip last newline, then split at every newline
  26.         self.userlist = users # store list of usernames in self.userlist
  27.         return
  28.  
  29.     def buildSock(self): # func to build socket
  30.         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  31.         s.connect((self.target, self.port)) # connect socket object to target
  32.         self.sock = s # store socket object in self.sock
  33.         banner = self.sock.recv(1024)[4::] # receive banner
  34.         if self.targetBanner == None: # if we haven't already stored the banner
  35.             self.targetBanner = banner # keep it
  36.         return
  37.  
  38.     def closeSock(self): # func to close self.sock
  39.         self.sock.close()
  40.         self.sock = None
  41.         return
  42.  
  43.     def testScanType(self): # func to test vulnerability to chosen scan
  44.         if self.scantype == "vrfy": # VRFY scan
  45.             self.sock.send("VRFY\n") # send empty VRFY command to target
  46.             response = self.sock.recv(1024) # get response
  47.             self.sock.send("QUIT\n") # quit SMTP
  48.             self.closeSock() # shutdown socket
  49.             if "501" in response: # if SMTP 501 syntax error
  50.                 return True # VRFY command is available
  51.             else:
  52.                 return False
  53.         elif self.scantype == "expn": # EXPN scan
  54.             self.sock.send("EXPN\n") # send EXPN command
  55.             response = self.sock.recv(1024)
  56.             self.sock.send("QUIT\n")
  57.             self.closeSock()
  58.             if "502" in response: # if SMTP 502 command not recognized
  59.                 return False # EXPN is not available
  60.             else:
  61.                 return True
  62.         elif self.scantype == "rcpt": # RCPT TO scan
  63.             self.sock.send("MAIL FROM:%s\n" %(self.mailfrom[0])) # send MAIL FROM command, use self.mailfrom for user
  64.             self.sock.recv(1024)
  65.             self.sock.send("RCPT TO:%s\n" %(self.mailfrom[0])) # send RCPT TO command, use self.mailfrom again
  66.             response = self.sock.recv(1024)
  67.             self.sock.send("QUIT\n")
  68.             self.closeSock()
  69.             if ("250" in response) or ("550" in response): # if RCPT TO is a recognized command (regardless if mailfrom user exists)
  70.                 return True # RCPT TO is available
  71.             else:
  72.                 return False
  73.  
  74.     def vrfyProbe(self, user): # func to probe target with VRFY
  75.         self.sock.send("VRFY %s\n" %(user)) # send VRFY command w/ username to target
  76.         response = self.sock.recv(1024)
  77.         if ("250" in response) or ("252" in response): # if 220 OK (or 252 because an unknown user returns 550)
  78.             return True # user exists
  79.         else:
  80.             return False
  81.  
  82.     def expnProbe(self, user): # func to probe target with EXPN
  83.         self.sock.send("EXPN %s\n" %(user)) # send EXPN command w/ username to target
  84.         response = self.sock.recv(1024)
  85.         if "250" in response: # if 250 OK
  86.             return True # user exists
  87.         else:
  88.             return False
  89.  
  90.     def rcptProbe(self, user): # func to probe target with RCPT TO
  91.         if self.mailfrom[1]: # if self.mailfrom[1] is True
  92.             self.sock.send("MAIL FROM:%s\n" %(self.mailfrom[0])) # send MAIL FROM command w/ MAIL FROM username to target
  93.             self.sock.recv(1024)
  94.             self.mailfrom[1] = False # set self.mailfrom[1] to False (this MAIL FROM command will only be sent once)
  95.         self.sock.send("RCPT TO:%s\n" %(user)) # send RCPT TO command w/ username
  96.         response = self.sock.recv(1024)
  97.         if "250" in response: # if 250 OK
  98.             return True # user exists
  99.         else:
  100.             return False
  101.  
  102.     def probeTarget(self, user): # func to evaluate scan type and probe for a username accordingly
  103.         if self.scantype == "vrfy":
  104.             result = self.vrfyProbe(user)
  105.         elif self.scantype == "expn":
  106.             result = self.expnProbe(user)
  107.         elif self.scantype == "rcpt":
  108.             result = self.rcptProbe(user)
  109.         return result
  110.  
  111. if __name__ == "__main__":
  112.     import os
  113.     import sys
  114.     import argparse
  115.     from datetime import datetime
  116.  
  117.     parser = argparse.ArgumentParser(description="SMTP User Enumeration Tool")
  118.     parser.add_argument("-t", "--target", help="IP address of target SMTP server", action="store", dest="target", default=False)
  119.     parser.add_argument("-p", "--port", help="Port number of target SMTP server (default: 25)", action="store", dest="port", default=25)
  120.     parser.add_argument("-u", "--userlist", help="Path to wordlist of usernames to probe for", action="store", dest="file", default=False)
  121.     parser.add_argument("--mailfrom", help="Change username used for MAIL FROM command (used in RCPT scan|default: root)", action="store", dest="user", default="root")
  122.     parser.add_argument("--scan-vrfy", help="Use VRFY enumeration method", action="store_true", dest="vrfy", default=False)
  123.     parser.add_argument("--scan-expn", help="Use EXPN enumeration method", action="store_true", dest="expn", default=False)
  124.     parser.add_argument("--scan-rcpt", help="Use RCPT TO enumeration method", action="store_true", dest="rcpt", default=False)
  125.     args = parser.parse_args()
  126.  
  127.     if len(sys.argv) == 1: # if no flags/switches are used
  128.         parser.print_help() # print help page
  129.         sys.exit(0) # exit
  130.  
  131.     if not args.target: # if no target given (default is False, not False == True)
  132.         parser.error("No target IP address given") # raise parser error for no target IP address given
  133.         sys.exit(1)
  134.     try:
  135.         socket.inet_aton(args.target) # attempt to run IP through socket.inet_aton
  136.     except socket.error: # if there are any socket errors
  137.         parser.error("Given target IP address is invalid") # the given IP is invalid
  138.         sys.exit(1)
  139.  
  140.     try:
  141.         if (int(args.port) < 1) or (int(args.port) > 65536): # convert args.port to integer and make sure it fits port range
  142.             raise Exception # if it doesn't fit the port range, raise an exception
  143.     except: # if an error is generated by the int conversion (a non-int being entered) or the range doesn't fit
  144.         parser.error("Given target port number is invalid") # the port number is invalid
  145.         sys.exit(1)
  146.  
  147.     if not args.file: # if no wordlist given
  148.         parser.error("No wordlist given")
  149.         sys.exit(1)
  150.     elif not os.path.isfile(args.file): # if a wordlist was given but doesn't exist
  151.         parser.error("Given wordlist does not exist")
  152.         sys.exit(1)
  153.  
  154.     types = [args.vrfy, args.expn, args.rcpt] # put all scan-type values in a list for easier evaluation
  155.     if (types.count(True) > 1) or (types.count(True) == 0): # if more than 1 True value OR if 0 True values
  156.         parser.error("Scan type selection invalid (choose one)") # the scan choice is invalid
  157.         sys.exit(1)
  158.  
  159.     # evaluate scan types in the order they were taken from args
  160.     if types[0]: # args.vrfy is True
  161.         scantype = "vrfy" # set scantype to vrfy
  162.     elif types[1]:
  163.         scantype = "expn"
  164.     elif types[2]:
  165.         scantype = "rcpt"
  166.  
  167.     print "[*] %s scan chosen for use against %s:%s" %(scantype.upper(), args.target, str(args.port)) # give info on scan back to user
  168.     # create enumerator object and apply all validated input
  169.     enumerator = SMTPUserEnumerator(args.target, args.file, port=int(args.port), scantype=scantype, mailfrom=args.user)
  170.  
  171.     print "[*] Checking for vulnerability to %s scan... " %(scantype.upper()),;sys.stdout.flush() # begin vuln check
  172.     try:
  173.         enumerator.buildSock() # build sock to target
  174.         check = enumerator.testScanType() # call testScanType() and store result in check
  175.         if check: # if testScanType() returned True
  176.             print "[GOOD]" # target is vulnerable
  177.         else: # if False is returned
  178.             print "[BAD]" # target is not vulnerable
  179.             sys.exit(1)
  180.     except Exception: # if an error happens (bogus target info is entered)
  181.         print "[FAIL]" # check failed
  182.         sys.exit(1) # exit
  183.  
  184.     print "[*] Parsing list of users... ",;sys.stdout.flush() # tell user that file is being read
  185.     try:
  186.         enumerator.readUsers() # call readUsers() to read, parse, and store file contents
  187.         print "[DONE]"
  188.     except: # if something goes wrong
  189.         print "[FAIL]" # fail out and exit
  190.         sys.exit(1)
  191.  
  192.     print "[*] Trying %s users... \n" %(str(len(enumerator.userlist))) # print number of usernames to try
  193.     startTime = datetime.now() # start clock for scan duration
  194.     enumerator.buildSock() # call buildSock() to reconnect to target
  195.     print "Target banner: %s" %(enumerator.targetBanner) # print target banner taken on vuln check
  196.     for i in range(len(enumerator.userlist)): # enumerate through usernames
  197.         result = enumerator.probeTarget(enumerator.userlist[i]) # call probeTarget() and pass current username
  198.         if result: # if its a good username
  199.             print "Found: %s" %(enumerator.userlist[i]) # report in console
  200.     enumerator.closeSock() # close connection to target once scan is done
  201.     stopTime = datetime.now() # stop clock for scan duration
  202.  
  203.     print "\n[*] Enumeration complete!" # complete scan
  204.     print "[*] Duration: %s" %(str(stopTime-startTime)) # calculate and print scan duration
RAW Paste Data