Beispiel #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)
Beispiel #2
0
def __load_init_modules() -> None:
    """
    Load all of the Init Modules
    """
    global logger
    path = "plugins/initmodules"
    for file in os.listdir(path):
        if not os.path.isfile(os.path.join(path, file)):
            continue
        if not file[-3:] == ".py":
            continue
        if file.endswith("__init__.py"):
            continue
        logger.debug("Importing {FILE}".format(FILE=file))
        module = importlib.import_module(
            "plugins.initmodules.{CLASS}".format(CLASS=file[0:-3]))
        instance = getattr(module, file[0:-3])()

        logger.info("Successfully imported {MODULE} ({MODULE_DESC})".format(
            MODULE=instance.name, MODULE_DESC=instance.description))
        LOADED_INIT_MODULES.append(instance)
    print(
        utils.normal_message(),
        "Successfully imported {COUNT} init modules".format(
            COUNT=len(LOADED_INIT_MODULES)))
Beispiel #3
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("")
Beispiel #4
0
def detect_apps(cpe_list):
    for cpe in cpe_list:
        cpe_app_type = "cpe:/a"
        if cpe.startswith(cpe_app_type):
            print(utils.normal_message(),
                  "Installed application is reported as",
                  cpe_utils.CPE(cpe).human())
Beispiel #5
0
def parse_nmap_scan(out_file):
    xmldoc = minidom.parse(out_file)
    hostslist = xmldoc.getElementsByTagName('hosts')
    # We only scan one host at a time
    if int(hostslist[0].attributes['down'].value) > 0:
        print(utils.error_message(), config.current_target, "was unreachable")
    else:
        port_list = xmldoc.getElementsByTagName('port')
        print("")

        print(utils.normal_message(), len(port_list), "ports are open")

        cpe_list = [
            x.firstChild.nodeValue for x in xmldoc.getElementsByTagName('cpe')
        ]

        detector.detect_os(cpe_list)
        detector.detect_apps(cpe_list)

        # New line for nicer formatting
        print("")

        searchsploit_nmap_scan(out_file)

        for open_port in port_list:
            detector.detect_service(open_port)
Beispiel #6
0
def main():
    """
        Start Lancer
    """

    # Get start time
    start_time = time.monotonic()

    scan_targets()

    print(utils.normal_message(), "Lancer has finished system scanning")
    elapsed_time = time.monotonic() - start_time

    generate_reports()

    print(utils.normal_message(), "Lancer took {TIME} to complete".
          format(TIME=time.strftime("%H:%M:%S", time.gmtime(elapsed_time))))
Beispiel #7
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")
Beispiel #8
0
def searchsploit_nmap_scan(nmap_file):
    print(utils.normal_message(),
          "Checking searchsploit for detected version vulnerabilities...")
    # TODO: Searchsploit doesn't seem that intelligent with the
    #       nmap file parsing. Loop through each program individually
    #       and pass in the program name extracted from nmap
    # searchsploit -t [PROGRAM]
    if utils.program_installed("searchsploit", False):
        searchsploit_output = subprocess.check_output(
            ['searchsploit', '--nmap', nmap_file]).decode('UTF-8')
        print()
        print(searchsploit_output)
    print("")
Beispiel #9
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())
Beispiel #10
0
def __execute_init_module(target: Target) -> None:
    """
    Run InitModules against the Target
    :param target: Target object to scan
    """
    if len(LOADED_INIT_MODULES) > 0:
        module = LOADED_INIT_MODULES[0]

        if module.can_execute_module() is ModuleExecuteState.CanExecute:
            print(utils.normal_message(),
                  "Executing {PROGRAM}".format(PROGRAM=module.name))
            module.execute(target.get_address(), 0)
        else:
            print(
                utils.error_message(),
                "Unable to meet dependencies for {MODULE}. Quitting".format(
                    MODULE=module.name))
            sys.exit(ExitCode.CriticalDependencyNotInstalled)
    else:
        print(utils.error_message(), "No Init Modules loaded. Quitting")
        sys.exit(ExitCode.EntryPointModulesNotLoaded)
Beispiel #11
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()
Beispiel #12
0
def __execute_modules(target: Target):
    """
    Run Modules against the Target
    :param target: Target object to scan
    """
    global LOADED_MODULES
    global logger

    # Order according to priority
    LOADED_MODULES = sorted(LOADED_MODULES,
                            key=operator.attrgetter('priority'),
                            reverse=True)

    # Add null service and port to ensure that the Hostname/Geo modules always run
    # TODO: Convert to InitModules
    EventQueue.push("", 0)

    # Get the current event from the Queue
    while EventQueue.events_in_queue():

        current_event = EventQueue.pop()

        logger.info("Processing {SERVICE}:{PORT}".format(
            SERVICE=current_event.service, PORT=current_event.port))

        if current_event.port not in ArgHandler.get_skip_ports():
            # Iterate through every single instance of our modules
            for module in LOADED_MODULES:
                # Check if we can run it
                run_state = module.can_execute_module()
                if run_state is ModuleExecuteState.CanExecute:
                    ip = str(target.ip)

                    if module.should_execute(current_event.service,
                                             current_event.port):
                        print(
                            utils.normal_message(),
                            "Executing {PROGRAM}".format(PROGRAM=module.name))
                        module.execute(ip, current_event.port)
Beispiel #13
0
def scan_targets() -> None:
    """
    Open the target file, Nmap file or get the target to scan
    """
    # Detect if we have a target list or just a single target
    if ArgHandler.get_target_file() is not None:
        # Target list
        targets = ArgHandler.get_target_file().read().splitlines()

        for target in targets:
            if len(target.strip()) == 0:
                continue
            # Comments start with a hashtag
            if len(target.strip()) > 0 and target[0] == "#":
                continue

            scan_target(target)
    elif ArgHandler.get_nmap_file() is not None:
        print(utils.normal_message(), "Loading nmap file")
        raise NotImplementedError("Loading from an Nmap file with -TF is not yet implemented")
        # nmap.parse_nmap_scan(ArgHandler.get_nmap_file())
    else:
        scan_target(ArgHandler.get_target())
Beispiel #14
0
def parse_arguments(args) -> None:
    """
    Parse the command line arguments
    :param args: The arguments to parse
    """

    global __args
    parser = create_parser()

    if len(args) is 0:
        print(error_message(), "No arguments supplied, showing help...\n")
        time.sleep(0.5)
        parser.print_help()
        sys.exit(1)

    if len(args) is 1 and "--version" in args:
        print(normal_message(), "Lancer {VERSION}".format(VERSION=__version__))
        sys.exit(0)

    if len(args) is 1 and "-h" or "--help" in args:
        parser.print_help()
        sys.exit(0)

    __args = parser.parse_args(args)
Beispiel #15
0
def test_normal_message():
    out = utils.normal_message()
    assert "[+]" in out
Beispiel #16
0
    Generate the reports
    """
    # TODO: Load report generators dynamicaly
    logger.debug("Generating reports")
    report = JSONReport()
    report.generate_report(Loot.loot)

    report = TerminalReport()
    report.generate_report(Loot.loot)
    logger.debug("Finished generating reports")


if __name__ == "__main__":
    init()
    logger = config.get_logger("Main")
    try:
        main()
    except NotImplementedError as e:
        logger.error(e)
        sys.exit(ExitCode.NotImplemented)
    except Exception as e:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        tb = traceback.format_exception(exc_type, exc_value, exc_traceback)
        print(utils.error_message(), "Unknown error encountered ({ERR}) - please report this via Github\n{EXCEPTION}"
              .format(ERR=e.args[0], EXCEPTION="".join(tb)))
        sys.exit(ExitCode.UnknownError)
    finally:
        print()
        print(utils.normal_message(), "Thank you for using Lancer")
        config.save_config()
Beispiel #17
0
def create_parser() -> argparse.ArgumentParser:
    """
    Create a parser with the current command line options
    :return: Configured ArgumentParser
    """
    example = 'Examples:\n'
    example += normal_message(
    ) + ' ./lancer -T 10.10.10.100 -v -l de -a 10.8.0.1\n'
    example += normal_message(
    ) + ' ./lancer -TF targets.lan -vv --cache-root ./cache/\n'
    example += terminal_width_string(
        normal_message() +
        ' ./lancer -TN nmap-10.0.0.1.xml --skip-ports 445 80 -L 5')

    description = normal_message() + " Lancer - system vulnerability scanner\n"
    description += terminal_width_string(
        normal_message() +
        " See the config.ini file at {CONFIG_PATH} for more options.".format(
            CONFIG_PATH=get_config_path()))

    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description=description,
        epilog=example,
        add_help=False)

    main_args = parser.add_argument_group("Required Arguments")
    main_args.description = "Specify the target or targets that Lancer will scan."
    mex_group = main_args.add_mutually_exclusive_group(required=True)
    mex_group.add_argument(
        "-T",
        "--target",
        metavar="TARGET",
        dest='target',
        type=str,
        help=
        "The hostname, IPv4 address or a subnet of of IPv4 addresses you wish to analyse."
    )
    mex_group.add_argument(
        "-TF",
        "--target-file",
        metavar="FILE",
        dest="host_file",
        type=argparse.FileType('r'),
        help="File containing a list of target IP addresses.")
    mex_group.add_argument(
        "-TN",
        "--target-nmap",
        metavar="FILE",
        dest='nmapFile',
        type=str,
        help=
        "Skip an internal Nmap scan by providing the path to an Nmap XML file. It is"
        " recommended to run version detection (-sV argument)")

    modules = parser.add_argument_group("Module Arguments")
    #modules.add_argument("--cache-root", metavar="PATH", dest='cache_root', default='',
    #                     help="[NOT YET IMPLEMENTED] "
    #                          "The root of the cache. This is where all of the data for the programs run is stored,"
    #                          " which may be useful if you wish to document or save all of the data in a separate"
    #                          " location.")
    #modules.add_argument("-L", "--level", metavar="LEVEL", dest='intrusive_level', default=3,
    #                     help="[NOT YET IMPLEMENTED] "
    #                          "The intrusion level of this iteration. A level of 1 means the least intrusive scripts"
    #                          " will be run, such as Nmap on quiet mode and a few HTTP requests. A level of 5 will mean"
    #                          " that intrusive exploits will be run against the computer to determine how vulnerable it"
    #                          " is. A full list of modules and their intrusion levels can be found on the Github Wiki."
    #                          " This defaults to 3 - moderately intrusive.")
    #modules.add_argument("-a", "--address", metavar="IP", dest='address', default='',
    #                     help="[NOT YET IMPLEMENTED] "
    #                          "Overrides the detected IP address with your own which is supplied.")
    modules.add_argument(
        "--skip-ports",
        nargs='+',
        type=int,
        metavar="PORTS",
        dest='skipPorts',
        default=[],
        help=
        "Set the ports to ignore. These ports will have no enumeration taken against them,"
        " except for the initial discovery via Nmap. This can be used to run a custom scan and"
        " pass the results to Lancer. Best used in conjunction with -TN/--target-nmap."
    )

    output = parser.add_argument_group("Output Arguments")
    output.description = "Control the output of Lancer."
    verbose_group = output.add_mutually_exclusive_group(required=False)
    verbose_group.add_argument(
        "-v",
        "--verbose",
        dest='verbose',
        action="store_true",
        default=False,
        help=
        "Use a verbose output. This will output results and information as modules run,"
        " which can be useful if you don't wish to wait for a report at the end."
    )
    verbose_group.add_argument(
        "-vv",
        "--very-verbose",
        dest='very_verbose',
        action="store_true",
        default=False,
        help=
        "Use a very verbose output. This will output virtually every single event that"
        " Lancer logs. Useful for debugging.")
    #output.add_argument("-o", "--output", metavar="FILE", dest="host_file", type=argparse.FileType('w'),
    #                    help="[NOT YET IMPLEMENTED] "
    #                         "Output the human-readable contents of the Lancer scan to a file. Best used in "
    #                         " conjunction with -v/-vv")
    output.add_argument("--version",
                        dest='show_version',
                        action="store_true",
                        default='',
                        help="Shows the current version of Lancer.")

    optional_args = parser.add_argument_group("Optional Arguments")
    optional_args.add_argument(
        "-l",
        "--language",
        metavar="LANGUAGE",
        dest="language_code",
        default="en",
        type=str,
        help="[NOT YET IMPLEMENTED] "
        "Language you want Lancer to use in. The  language code uses ISO 639-1. Defaults to"
        " English.")

    optional_args.add_argument(
        "-h",
        "--help",
        action="store_true",
        dest="help",
        help="Shows the different arguments available for Lancer.")
    optional_args.add_argument("--clear-cache",
                               action="store_true",
                               dest="clear_cache",
                               help="Clear the cache before executing")

    return parser
Beispiel #18
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()