Пример #1
0
def nmap_scan():
    print(utils.normal_message(), "Starting scan of", config.current_target)

    # Check if Nmap is installed - critical program. If this fails, the program will exit
    utils.program_installed("nmap", True)

    out_file = os.path.join(
        config.get_module_cache("nmap", config.current_target), "nmap.xml")

    if config.args.verbose:
        print(utils.normal_message(), "Nmap data will be written to", out_file)

    nmap_args = ['nmap', '-sC', '-sV', '-oX', out_file, config.current_target]

    if config.args.scan_udp:
        print(utils.normal_message(),
              "Scanning UDP ports, this may take a long time")
        nmap_args.append('-sU')

    print(utils.normal_message(),
          "Scanning open ports on",
          config.current_target + "...",
          end=' ')

    with Spinner():
        output = subprocess.check_output(nmap_args).decode('UTF-8')
    print("")

    # if config.args.show_output:
    #    print("")
    #    print(output)

    print(utils.normal_message(), "Scan complete")

    parse_nmap_scan(out_file)
Пример #2
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))
Пример #3
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)
Пример #4
0
    def download_file(self, ip: str, port: str, ftp_client: ftplib.FTP,
                      filename: str) -> None:
        """
        Download the specified file
        :param ip: IP of the FTP server
        :param port: Port of the server
        :param ftp_client: Reference to FTP client
        :param filename: Filename path we want
        """
        try:
            file_size = ftp_client.size(filename) / 1024 / 1024
        except ftplib.error_perm:
            self.logger.error(
                "Permission denied to access {FILE}".format(FILE=filename))
            return

        self.logger.debug("Downloading {FILE} size {SIZE}".format(
            FILE=filename, SIZE="{:.1f}mb".format(file_size)))

        #with Spinner():

        local_filename = os.path.join(
            config.get_module_cache(self.name, ip, port), filename)

        if not os.path.exists(os.path.dirname(local_filename)):
            os.mkdir(os.path.dirname(local_filename))
        file = open(local_filename, 'wb')
        # TODO: Sometimes hangs when it reaches a file that it can't download. Find a fix (parse ftp.dir?)
        try:
            ftp_client.retrbinary('RETR ' + filename, file.write)
            self.logger.info(
                "Downloaded {FILE} ({SIZE}mb) to FTP cache".format(
                    FILE=filename, SIZE="{:.1f}mb".format(file_size)))
        except ftplib.error_perm:

            self.logger.error(
                "Permission denied to access {FILE}".format(FILE=filename))
        file.close()
Пример #5
0
def test_get_module_cache_no_port():
    path = config.get_module_cache("Test", "127.0.0.1")
    assert "Test" in path
    assert path.endswith("Test")
Пример #6
0
def test_get_module_cache():
    path = config.get_module_cache("Test", "127.0.0.1", "22")
    assert "Test" in path
    assert "22" in path
Пример #7
0
    def execute(self, ip: str, port: int) -> None:
        """
        Get all of the webpage links (non recursive for now)
        :param ip: IP to use
        :param port: Port to use
        :return:
        """

        self.create_loot_space(ip, port)

        Loot.loot[ip][str(port)][self.loot_name]["Internal"] = []
        Loot.loot[ip][str(port)][self.loot_name]["External"] = []

        if port == 443:
            url = "https://{IP}".format(IP=ip)
        elif port == 80:
            url = "http://{IP}".format(IP=ip)
        else:
            url = "http://{IP}:{PORT}".format(IP=ip, PORT=port)

        try:
            response = requests.get(url, allow_redirects=True)

            for link in BeautifulSoup(response.text,
                                      features="html.parser",
                                      parse_only=SoupStrainer('a')):
                if link.has_attr('href'):
                    parse = urlparse(link['href'])
                    loot_url = parse[1] + parse[2]
                    if self.is_internal_url(ip, parse[1]):
                        if loot_url not in Loot.loot[ip][str(port)][
                                self.loot_name]["Internal"]:
                            self.logger.debug(
                                "{URL} is an internal URL".format(
                                    URL=loot_url))
                            Loot.loot[ip][str(port)][
                                self.loot_name]["Internal"].append(loot_url)

                            with open(
                                    os.path.join(
                                        config.get_module_cache(
                                            self.name, ip, str(port)),
                                        "internal.txt"), "a") as file:
                                file.write("{URL}\n".format(URL=loot_url))
                    else:
                        if loot_url not in Loot.loot[ip][str(port)][
                                self.loot_name]["External"]:
                            self.logger.debug(
                                "{URL} is an external URL".format(
                                    URL=loot_url))
                            Loot.loot[ip][str(port)][
                                self.loot_name]["External"].append(loot_url)

                            with open(
                                    os.path.join(
                                        config.get_module_cache(
                                            self.name, ip, str(port)),
                                        "external.txt"), "a") as file:
                                file.write("{URL}\n".format(URL=loot_url))

            self.logger.info(
                "Found {INTERNAL} internal links and {EXTERNAL} external links"
                .format(
                    INTERNAL=len(
                        Loot.loot[ip][str(port)][self.loot_name]["Internal"]),
                    EXTERNAL=len(
                        Loot.loot[ip][str(port)][self.loot_name]["External"])))
        except requests.exceptions.ConnectionError:
            self.logger.error("Unable to connect to {URL}".format(URL=url))
Пример #8
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))