daily pastebin goal
10%
SHARE
TWEET

smtp_user_enumerator.py

The_Defalt Jul 25th, 2018 (edited) 287 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
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