Esempio n. 1
0
def detect_service(openport):
    for service in openport.getElementsByTagName('service'):
        port = int(openport.attributes['portid'].value)
        service_type = service.attributes['name'].value
        try:
            service_name = service.attributes['product'].value
        except KeyError:
            service_name = service_type

        print(utils.normal_message(), service_name, "is open on port", port)
        # Ignore the port if its in the list of ports to skip
        if port not in config.args.skipPorts:
            # Some kind of SSH server
            if service_type == "ssh":
                print(utils.warning_message(), service_name,
                      "is recognised by nmap as an ssh server")
                """scripts_ran = service.getElementsByTagName('script')
                for script in scripts_ran:
                    print(script.attributes['id'])
                    if script.attributes['id'] == 'fingerprint-strings':
                        print(script.getElementsByTagName('elem')[0].text)"""
            # MySQL server
            if service_name == "mysql":
                print(utils.warning_message(), service_name,
                      "is recognised by nmap as a MySQL server...")
        else:
            print(
                utils.warning_message(), "Skipping", service_name, "(port",
                str(port) + ") as it has been specified "
                "as a port to skip")
        print("")
Esempio n. 2
0
def detect_os(cpe_list):
    for cpe in cpe_list:
        cpe_os_type = "cpe:/o"
        if cpe.startswith(cpe_os_type):
            print(utils.normal_message(), "Target OS appears to be",
                  cpe_utils.CPE(cpe).human())
            if cpe_utils.CPE(cpe).matches(cpe_utils.CPE("cpe:/o:microsoft:windows")) \
                    and platform.system() == "linux":
                print(utils.warning_message(),
                      "Target machine is running Microsoft Windows")
                print(utils.warning_message(),
                      "Will commence enumeration using enum4linux")
                print(utils.error_message(), "enum4linux not yet implemented")
Esempio n. 3
0
 def remove_files_over_size(self,
                            ftp_client,
                            files,
                            size=1024 * 1024 * 50) -> (list, list):
     """
     Remove any files over the specified size
     :param ftp_client: Reference to the FTP client
     :param files: Files we have retrieved from the FTP server
     :param size: The maximum size. Any files BELOW this will be accepted, any EQUAL or ABOVE will be rejected
     :return: Two lists, containing sanitised and large files respectively
     """
     sanitised_files = []
     large_files = []
     for file in files:
         try:
             # If the file is smaller than 50MiB
             if ftp_client.size(file) < size:
                 sanitised_files.append(file)
             else:
                 large_files.append(file)
         except ftplib.error_perm:
             print(
                 utils.warning_message(), "Don't have permission to access",
                 utils.color(file, None, None, "bold") + ",",
                 "could be a directory or a file we don't have permission to access"
             )
     self.logger.info("{FILE_COUNT} file(s) under 50mb".format(
         FILE_COUNT=len(sanitised_files)))
     self.logger.info("{FILE_COUNT} file(s) bigger or equal to 50mb".format(
         FILE_COUNT=len(large_files)))
     return sanitised_files, large_files
Esempio n. 4
0
    def format(self, record: logging.LogRecord) -> str:
        """
        Custom formatter for the stdout logging
        :param record: The Log to format
        :return: The formatted string
        """
        # TODO: Fix cyclic imports
        from core.utils import warning_message, error_message, normal_message
        from core.ArgHandler import get_verbose, get_very_verbose

        if record.levelno == logging.WARNING:
            if get_verbose() or get_very_verbose():
                return warning_message() + ' [{NAME}] {MESSAGE}'.format(NAME=record.name, MESSAGE=record.getMessage())
            else:
                return warning_message() + ' {MESSAGE}'.format(NAME=record.name, MESSAGE=record.getMessage())
        if record.levelno >= logging.ERROR:
            return error_message() + ' [{NAME}] {MESSAGE}'.format(NAME=record.name, MESSAGE=record.getMessage())
        return normal_message() + ' [{NAME}] {MESSAGE}'.format(NAME=record.name, MESSAGE=record.getMessage())
Esempio n. 5
0
    def execute(self, ip: str, port: int) -> None:
        """
        Enumerates files and directories on the given web server
        :param ip: IP to use
        :param port: Port to use
        """
        self.create_loot_space(ip, port)
        # List of dictionary results
        Loot.loot[ip][str(port)][self.loot_name] = []

        url = self.get_url(ip, port)

        filename = os.path.join(config.get_module_cache(self.name, ip), "enum-{PORT}.log".format(PORT=port))

        wordlist_path = config.get_module_value(self.name, "wordlist",
                                                "/usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt")

        if not os.path.exists(wordlist_path):
            msg = utils.terminal_width_string(
                "Unable to find Gobuster wordlist at {PATH}".format(PATH=wordlist_path)
            )
            self.logger.error(msg)
            return

        extensions = config.get_module_value(self.name, "extensions", ".php,.txt")

        with io.open(filename, 'wb') as writer, io.open(filename, 'rb', 1) as reader:
            # Arguments:
            # -e - expanded URL (whole path is shown)
            # -z - Don't display the progress (X/Y Z%)
            # -q - Don't print the banner for Gobuster
            # -k - Skip SSL Cert verification
            # -x - File extension(s) to scan for. Value loaded from config.ini with .php,.txt as default
            # -u - URL
            # -w - Wordlist
            command = "gobuster dir -z -q -e -k -u {URL} -w {WORDLIST} -x {EXTENSIONS}" \
                .format(URL=url, WORDLIST=wordlist_path, EXTENSIONS=extensions)
            process = subprocess.Popen(command, stdout=writer)

            # While the process return code is None
            while process.poll() is None:
                time.sleep(0.5)
                responses = reader.read().decode("UTF-8").splitlines()
                for response in responses:
                    if response.strip() is not "":
                        # Get the response code (last three chars but one)
                        code = int(response[-4:-1])
                        # Get this as a human readable response
                        human_readable_code = utils.get_http_code(code)
                        # Get the directory
                        response_dir = response.split('(')[0].strip()
                        # Add to the loot
                        result = {"Path": response_dir, "Code": code, "Code Value": human_readable_code}
                        Loot.loot[ip][str(port)][self.loot_name].append(result)
                        print(utils.warning_message(), "Found directory at {PATH} with {CODE} ({CODE_VALUE}"
                              .format(PATH=response_dir, CODE=code, CODE_VALUE=human_readable_code))
Esempio n. 6
0
def scan_target(target: str) -> None:
    """
    Scan the target passed
    :param target: Target IP/hostname to scan
    """
    try:
        # See if the target is an IP network
        ip_network = ipaddress.ip_network(target, strict=False)
        if ip_network.version is 6:
            raise NotImplementedError("IPv6 addresses are not yet supported")
        for x in range(ip_network.num_addresses):
            ip = ip_network[x]
            tgt = Target(None, ip)
            ModuleProvider.analyse(tgt)
            tgt.stop_timer()
    except ValueError:
        # It's not an IP address or a subnet,
        # so most likely a hostname

        if target.startswith("www."):
            www_warning = utils.terminal_width_string(
                "Target starts with \"www.\" - this is not recommended as it can lead to false positives in modules "
                " - for example, when checking URLs for internal links. Do you want to remove \"www.\" from the URL?"

            )
            print(utils.warning_message(), www_warning)
            agree = utils.input_message("[Y]es or [N]o: ")
            if agree.lower() == "y":
                target = target.replace("www.", "")
                print(utils.normal_message(), "Removed \"www.\" from target, now \"{TARGET}\"".format(TARGET=target))
            else:
                print(utils.warning_message(), "Retaining \"www.\" in target")

        hostname_info = socket.getaddrinfo(target, None, socket.AF_INET)
        ip = ipaddress.ip_address(hostname_info[0][4][0])
        tgt = Target(target, ip)
        ModuleProvider.analyse(tgt)
        tgt.stop_timer()
    print()
Esempio n. 7
0
    def execute(self, ip: str, port: int) -> None:
        """
        Scan the web server using Nikto
        :param ip: IP to use
        :param port: Port to use
        """
        self.create_loot_space(ip, port)

        url = self.get_url(ip, port)
        output_filename = os.path.join(get_module_cache(self.name, ip, ""),
                                       "nmap")
        filename = os.path.join(get_module_cache(self.name, ip, ""),
                                "nmap.log")

        self.logger.debug(
            "Writing XML output to {PATH}.xml|.nmap|.gnmap".format(
                PATH=output_filename))

        self.logger.info("Starting Nmap scan of {TARGET}".format(TARGET=ip))
        with io.open(filename, 'wb') as writer, io.open(filename, 'rb',
                                                        1) as reader:
            # Arguments:
            # -host - the host to scan
            # -Format - the format of the output file
            # -o - the output path
            # -ask no - don't do anything which requires user input
            # -sC -sV
            command = "nikto -host {URL} -Format xml -o {OUTPUT} -ask no"\
                .format(URL=url, OUTPUT=output_filename)
            process = subprocess.Popen(command, stdout=writer)
            # While the process return code is None
            while process.poll() is None:
                time.sleep(0.5)
            # output = reader.read().decode("UTF-8").splitlines()

        xmldoc = minidom.parse(output_filename)
        nikto_items = xmldoc.getElementsByTagName('item')
        for item in nikto_items:
            print(
                utils.warning_message(),
                item.getElementsByTagName("description")
                [0].firstChild.wholeText)
Esempio n. 8
0
def init():
    """
        Initialise all of the needed prerequisites for Lancer. This should:

        - Register the signal handler for Ctrl+C
        - Load the config file
        - Parse command line arguments
        - Show the header
        - Check that we're on a supported Python version
        - Show an option to update the VirtualTerminal registry key if on Win 10
        - Show a warning that localisation support is not yet implemented if there is a non-default -l parameter
        - Display a legal disclaimer about using Lancer for illegal use
        - Warn if the cache is over 500mb in size
        - Clear the cache if we want to
    """
    # Register the signal handler for a more graceful Ctrl+C
    signal.signal(signal.SIGINT, utils.signal_handler)

    # Load the config file
    config.load_config()

    # Parse the arguments
    ArgHandler.parse_arguments(sys.argv[1:])

    # Display the header
    utils.display_header()
    time.sleep(1.25)

    # Check we're on a supported Python version
    utils.python_version()

    # Update the Windows virtual terminal if necessary
    # If we're on Windows 10, import winutils
    if platform.system().lower() == "windows" and platform.release() == "10":
        from core.winutils import update_windows_virtual_terminal
        update_windows_virtual_terminal()

    # Language warning - not yet implemented
    if ArgHandler.get_language_code() != 'en':
        print(utils.error_message(), "Multi-language support is not yet implemented...")

    # Show a legal disclaimer
    disclaimer = utils.terminal_width_string(
        "Legal Disclaimer: Usage of Lancer for attacking targets without prior mutual"
        " authorisation is illegal. It is the end user's responsibility to adhere to all local"
        " and international laws. The developers of this tool assume no liability and are not"
        " responsible for any misuse or damage caused by the use of this program."
    )
    print(utils.error_message(), disclaimer)
    print()

    # Cache warning
    # If it is more than 1GB, we display an error-style warning
    root_directory = Path(config.get_cache_path())
    size = sum(f.stat().st_size for f in root_directory.glob('**/*') if f.is_file()) / 1048576  # Bytes -> MB
    if size >= 2048:
        print(utils.error_message(), "Cache is {SIZE}gb in size. It is recommended to clear it with --clear-cache."
              .format(SIZE="{:.1f}".format(size/1024)))
    # If it is more than 500, we display a warning
    elif size >= 512:
        print(utils.warning_message(), "Cache is {SIZE}mb in size. You can clear it with --clear-cache."
              .format(SIZE="{:.1f}".format(size)))

    # Clear the cache
    if ArgHandler.get_clear_cache():
        files = os.listdir(config.get_cache_path())
        for filename in files:
            file_path = os.path.join(config.get_cache_path(), filename)

            if os.path.isfile(file_path) or os.path.islink(file_path):
                os.unlink(file_path)
            elif os.path.isdir(file_path) and file_path != config.get_current_cache_path():
                shutil.rmtree(file_path)
        print(utils.normal_message(), "Removed {NUM} items from the cache".format(NUM=len(files)))

    # Check if we are admin, display a relevant message
    if utils.is_user_admin():
        print(utils.normal_message(), "Lancer running with elevated permissions")
    else:
        non_admin_warning = utils.terminal_width_string("Lancer doesn't appear to being run with elevated"
                                                        " permissions. Some functionality may not work"
                                                        " correctly")
        print(utils.warning_message(), non_admin_warning)

    # Display warning about your IP address
    ip_address = utils.terminal_width_string(
        "Your IP Address has been detected as {IP}. This can be changed with -a [IP]"
    )
    print(utils.normal_message(), ip_address.format(IP=get_ip()))
    print()

    # Preload all of the modules
    ModuleProvider.load()
Esempio n. 9
0
def test_warning_message():
    out = utils.warning_message()
    assert "[*]" in out
Esempio n. 10
0
    def execute(self, ip: str, port: int) -> None:
        # Don't specify filename on output_filename as all formats are specified
        output_filename = os.path.join(get_module_cache(self.name, ip, ""), "nmap")
        filename = os.path.join(get_module_cache(self.name, ip, ""), "nmap.log")

        self.logger.debug("Writing output to {PATH}.xml|.nmap|.gnmap".format(PATH=output_filename))

        # TODO: Optional UDP scan

        self.logger.info("Starting Nmap scan of {TARGET}".format(TARGET=ip))

        with io.open(filename, 'wb') as writer, io.open(filename, 'rb', 1) as reader:
            # Arguments:
            # -v  - Verbose output
            # -sT - TCP scan
            # -sV - Version detection
            # -oA - Output in all formats
            command = "nmap -v -sT -sV -oA {OUTPUT_FILE} {TARGET}".format(TARGET=ip, OUTPUT_FILE=output_filename)
            process = subprocess.Popen(command, stdout=writer)
            # While the process return code is None
            while process.poll() is None:
                # Parse the program as it runs
                output = reader.read().decode("UTF-8").splitlines()
                for line in output:
                    # If we've got an open port, print it
                    if line.startswith("Discovered open port "):
                        port = line[21:line.index("/")]
                        print(utils.warning_message(), "Discovered port open on {PORT}".format(PORT=port))
                    # Get time remaining so there is still some output
                    if " done; ETC: " in line:
                        # TODO: Regex instead of this disgusting mess
                        percentage_index = line.index("%")
                        # Get 5 characters before the percentage (xx.xx and the percent)
                        percentage = line[percentage_index-5:percentage_index+1]
                        # Get time remaining in format x:xx:xx
                        time_left = line.replace(" remaining)", "")
                        time_left = time_left[-7:]

                        print(utils.warning_message(), "Scan {PERC} complete - {TIME} left"
                              .format(PERC=percentage, TIME=time_left))

                # Wait for 0.25 seconds
                time.sleep(0.25)
        self.logger.info("Finished Nmap scan of {TARGET}".format(TARGET=ip))

        # Parse the XML output
        xml = minidom.parse("{FILE}.xml".format(FILE=output_filename))
        # Get the host scanned
        hostslist = xml.getElementsByTagName('hosts')
        # We only scan one host at a time
        if int(hostslist[0].attributes['down'].value) > 0:
            # Unreachable, return rest of the processing
            self.logger.warning("{TARGET} was unreachable".format(TARGET=ip))
            return

        # Get all of the open ports
        port_list = xml.getElementsByTagName('port')
        self.logger.info("{PORT_COUNT} ports are open".format(PORT_COUNT=len(port_list)))

        # Get all of the CPE versions detected
        # cpe_list = list(dict.fromkeys([x.firstChild.nodeValue for x in xml.getElementsByTagName('cpe')]))
        # for cpe in cpe_list:
        #    self.logger.info("Detected {CPE}".format(CPE=CPE(cpe).human()))

        # searchsploit_nmap_scan(out_file)

        # Loop through the open ports
        for open_port in port_list:
            # Get the service type
            for svc in open_port.getElementsByTagName('service'):
                # Parse the values
                service = svc.attributes['name'].value
                port = open_port.attributes['portid'].value

                # If the IP/port is not in the Loot dictionary, add it
                if ip not in Loot.loot:
                    Loot.loot[ip] = {}
                if port not in Loot.loot[ip]:
                    Loot.loot[ip][port] = {}

                # Add to the event Queue so we get a notification
                EventQueue.push(service=service, port=int(port))