Beispiel #1
0
    def run(self):

        if self.args.list:
            self.settings.show_list_exploits()

        else:
            if self.args.target.lower().startswith('http'):
                target = Target(ip=None, port=None, url=self.args.target)
            else:
                ip, port = self.args.target.split(':', maxsplit=1)
                target = Target(ip=ip, port=port, url=None, ssl=self.args.ssl)

            exploit = self.settings.get_exploit(self.args.exploit)

            OutputUtils.title('Exploitation Attempt: {description}'.format(
                description=exploit.description))
            out = exploit.run(target)

            if out is None:
                sys.exit(1)

            if exploit.check_success():
                logger.success('{description}: Target is EXPLOITABLE !'.format(
                    description=exploit.description))
                logger.info('Exploit code available in: {directory}'.format(
                    directory=exploit.directory))

            else:
                logger.error(
                    '{description}: Target seems NOT exploitable'.format(
                        description=exploit.description))
Beispiel #2
0
    def run(self):

        if self.args.list:
            self.settings.show_list_exploits()

        else:
            # Instantiate Target using URL or IP:PORT
            if self.args.target.lower().startswith('http'):
                target = Target(ip=None, port=None, url=self.args.target)
            else:
                ip, port = self.args.target.split(':', maxsplit=1)
                target = Target(ip=ip, port=port, url=None, ssl=self.args.ssl)

            # Get Exploit (or detection script) for selected vulnerability
            exploit = self.settings.get_exploit(self.args.vuln)

            # Check if specified mode is available for specified vuln
            if not exploit.is_mode_supported(self.args.mode):
                logger.error(
                    'Supplied mode ({mode}) is not supported for vulnerability {vuln}'
                    .format(mode=self.args.mode, vuln=self.args.vuln))
                sys.exit(1)

            # Print title and main information
            OutputUtils.title('Vulnerability: {description}'.format(
                description=exploit.description))
            logger.info(
                'Target product: {product}'.format(product=exploit.product))
            logger.info('Vulnerability type: {vuln}'.format(vuln=exploit.type))
            logger.info('Selected mode: {mode}'.format(mode=self.args.mode))
            if self.args.mode == 'exploit' and exploit.type == 'rce':
                logger.info('RCE output available: {rceoutput}'.format(
                    rceoutput='Y' if exploit.exploit_rce_output else 'N'))

            # Run exploit/detection script
            out = exploit.run(target, self.args.mode, self.args.cmd)

            if out is None:
                sys.exit(1)

            # Automatically check success when:
            # - Run in detection mode
            # - Run in exploit mode without --cmd provided (automatic exploit test)
            if self.args.mode == 'detect' or \
               (self.args.mode == 'exploit' and (not self.args.cmd or len(self.args.cmd) == 0)):
                if exploit.check_success(self.args.mode):
                    logger.success(
                        '{description}: Target is EXPLOITABLE !'.format(
                            description=exploit.description))
                    logger.info('Code available in: {directory}'.format(
                        directory=exploit.directory))
                else:
                    logger.error(
                        '{description}: Target seems NOT exploitable'.format(
                            description=exploit.description))
Beispiel #3
0
    def __run_for_multi_targets(self, args):
        """Run attack against multiple targets from the database"""

        # Get Mission from which targets must be extracted
        mission = self.sqlsess.query(Mission)\
                    .filter(Mission.name == args.mission).first()
        if mission:
            logger.info('Extracting targets from mission "{mission}" ...'.format(
                mission=mission.name))
        else:
            raise AttackException('Mission {mission} does not exist into the ' \
                'database'.format(mission=args.mission))

        # Initialize Services requester and add filter if provided
        req = ServicesRequester(self.sqlsess)
        req.select_mission(args.mission)

        if args.filters_combined:
            for filt in args.filter:
                logger.info('Applying filters on mission scope: {filter}'.format(
                    filter=filt))
            if len(args.filter) > 1:
                logger.info('Note: Logical OR is applied between each filter')
            req.add_filter(args.filters_combined)

        # Retrieve targeted services from database
        services = req.get_results()
        if not services:
            raise AttackException('There is no matching service to target into the ' \
                'database')

        # Add each targeted service into Attack scope 
        for service in services:

            # Update credentials, options, products if specified in command-line
            if args.creds:
                for c in args.creds[service.name]: 
                    service.add_credential(c.clone())
            if args.users:
                for u in args.users[service.name]: 
                    service.add_credential(u.clone())
            if args.products:
                for p in args.products[service.name]: 
                    service.add_product(p.clone())
            if args.options:
                for o in args.options[service.name]: 
                    service.add_option(o.clone())

            # Initialize Target 
            try:
                target = Target(service, self.settings.services)
            except TargetException as e:
                logger.error(e)
                continue

            self.attack_scope.add_target(target)

        # Run the attack
        self.attack_scope.attack()
Beispiel #4
0
    def checkAndInitializeTarget(self, ip, port, service, url):
        """
		Initialize the target based either on IP:PORT or on URL, plus service
		@Args 		ip: 		IP address (None if url given)
					port: 		Port number (None if url given)
					service:	Service name
					url:		URL (None if ip+port given)
		@Returns 	Boolean indicating status
		"""
        self.target = Target(ip, port, service, url)
        if not self.target.is_reachable:
            self.output.printError('Target seems not to be reachable...')
            sys.exit(0)
Beispiel #5
0
    def __run_for_single_target(self, args):
        """Run attack against a single target specified into args"""
        
        req = ServicesRequester(self.sqlsess)
        mission = None

        # Get Mission if target must be added into a mission scope
        if args.add:
            mission = self.sqlsess.query(Mission).filter(Mission.name == args.add).first()
            if not mission:
                raise AttackException('The specified mission does not exist in the ' \
                    'database. You should create it if needed')

        # Create new Service/Host objects (if service already exist, 
        # will be merged by ServicesRequester.add_target)
        url = args.target_ip_or_url if args.target_mode == TargetMode.URL else ''
        ip  = args.target_ip_or_url if args.target_mode == TargetMode.IP else ''
        service = Service(
            name=args.service,
            port=int(args.target_port),
            protocol=self.settings.services.get_protocol2(args.service),
            url=url)
        host = Host(ip=ip) # Will be updated when initializing Target()
        host.services.append(service)

        # Update context (credentials, options, products) if specified in command-line
        if args.creds:
            for c in args.creds[args.service]:
                self.sqlsess.add(c)
                service.credentials.append(c)
        if args.users:
            for u in args.users[args.service]: 
                self.sqlsess.add(u)
                service.credentials.append(u)
        if args.products:
            for p in args.products[args.service]: 
                self.sqlsess.add(p)
                service.products.append(p)
        if args.options:
            for o in args.options[args.service]: 
                self.sqlsess.add(o)
                service.options.append(o)

        # Initialize Target
        try:
            target = Target(service, self.settings.services)
        except TargetException as e:
            logger.error(e)
            sys.exit(1)

        # Check Target and update its information:
        # - Reverse DNS lookup: by default
        # - Port check: always
        # - Nmap service detection: by default
        # - HTML title grabbing: always
        # - Web technologies detection: always
        # - Context initialization via SmartStart: always
        reachable = target.smart_check(
            reverse_dns_lookup=(args.reverse_dns is None or args.reverse_dns == 'on'),
            availability_check=True, 
            nmap_banner_grabbing=(args.nmap_banner_grab is None \
                or args.nmap_banner_grab == 'on'),
            html_title_grabbing=True,
            web_technos_detection=True,
            smart_context_initialize=True)

        # Display availability status, exit if not reachable
        if args.target_mode == TargetMode.IP:
            msg = 'Target service {neg}reachable: {target}'.format(
                neg='not ' if not reachable else '',
                target=target)
        else:
            msg = 'Target URL {url} is {neg}reachable'.format(
                url=target.get_url(),
                neg='not ' if not reachable else '')

        if reachable:
            logger.success(msg)
        else: 
            logger.error(msg)
            return

        # Commit the target with updated information inside the appropriate 
        # mission in the database
        if mission:
            logger.info('Results from this attack will be saved under mission ' \
                '"{mission}" in database'.format(mission=mission.name))
            req.select_mission(mission.name)
            req.add_target(target)

        # Run the attack
        self.attack_scope.add_target(target)
        self.attack_scope.attack()
        return
Beispiel #6
0
    def parse(self, http_recheck=True):
        """
        Parse the Shodan results

        :param bool http_recheck: If set to True, TCP ports are re-checked for HTTP(s)

        :return: Hosts 
        :rtype: list(Host)|None
        """

        results = list()
        host_id = 0

        for ip in self.ips_list:
            host_id += 1

            # Lookup the host
            query = None
            try:
                query = self.api.host(ip)
            except Exception as e:
                logger.error(
                    "Error when querying shodan for IP {ip}: {exc}".format(
                        ip=ip, exc=e))
                return None

            logger.info('Importing Shodan results from https://www.shodan.io/host/' \
                    '{ip}'.format(ip=ip))

            #print(query)

            # Get host information
            hostname = query["hostnames"][0] if query["hostnames"] else ip
            os = query.get(
                "os",
                '')  # Shodan is often missing OS detection in my tests...
            os_vendor = ''
            os_family = ''
            if os:
                os_vendor = OSUtils.get_os_vendor(os)
                os_family = OSUtils.get_os_family(os)
            device_type = ''

            services = query["data"]

            # Create Host object
            host = Host(
                ip=ip,
                hostname=hostname,
                os=os,
                os_vendor=os_vendor,
                os_family=os_family,
                mac='',
                vendor='',
                type=device_type,
            )

            logger.info('[Host {current_host}/{total_host}] Parsing host: ' \
                '{ip}{hostname} ...'.format(
                    current_host=host_id,
                    total_host=len(self.ips_list),
                    ip=host.ip,
                    hostname=' ('+host.hostname+')' if host.hostname != host.ip else ''))

            # Loop over ports/services
            port_id = 0
            for service in services:
                port_id += 1
                module = service["_shodan"]["module"]
                name = get_service_name(module)
                port = service.get("port", None)
                protocol = service.get("transport", None)
                url = ''
                comment = ''
                html_title = ''
                http_headers = ''

                # Print current processed service
                print()
                logger.info('[Host {current_host}/{total_host} | ' \
                    'Service {current_svc}/{total_svc}] Parsing service: ' \
                    'host {ip} | port {port}/{proto} | service {service} ...'.format(
                        current_host=host_id,
                        total_host=len(self.ips_list),
                        current_svc=port_id,
                        total_svc=len(services),
                        ip=host.ip,
                        port=port,
                        proto=protocol,
                        service=name))

                # Get banner
                product_name = service.get('product', '')
                product_version = service.get('version', '')
                banner = '{name}{version}'.format(
                    name=product_name,
                    version=' {}'.format(product_version)
                    if product_version else '')

                # # Deduce OS from banner if possible
                # if not host.os:
                #     host.os = OSUtils.os_from_nmap_banner(banner)
                #     if host.os:
                #         host.os_vendor = OSUtils.get_os_vendor(host.os)
                #         host.os_family = OSUtils.get_os_family(host.os)

                # Get URL for http services
                if name == 'http':
                    if 'https' in module or 'ssl' in module:
                        proto = 'https'
                    else:
                        proto = 'http'

                    url = "{proto}://{host}:{port}".format(proto=proto,
                                                           host=hostname,
                                                           port=port)

                # Recheck for HTTP/HTTPS for services undetermined by Shodan
                if http_recheck \
                    and protocol == "tcp" \
                    and not self.services_config.is_service_supported(name, multi=False):
                    url = WebUtils.is_returning_http_data(ip, port)
                    if url:
                        logger.success("{url} seems to return HTTP data, marking it " \
                            "as http service".format(url=url))
                        name = "http"

                # Get page title and HTTP headers for HTTP services
                if "http" in name:
                    if 'http' in service:
                        html_title = service['http'].get('title', '')
                    http_headers = service.get('data', '')

                # Only keep services supported by Jok3r
                if not self.services_config.is_service_supported(name,
                                                                 multi=False):
                    logger.warning("Service not supported: host {ip} | port "
                                   "{port}/{proto} | service {service}".format(
                                       ip=ip,
                                       port=port,
                                       proto=protocol,
                                       service=name))
                    continue

                # Create Service object
                if protocol and port:
                    service = Service(
                        name=name,
                        name_original=module,
                        port=port,
                        protocol={
                            "tcp": Protocol.TCP,
                            "udp": Protocol.UDP
                        }.get(protocol),
                        url=url,
                        up=True,
                        banner=banner,
                        comment=comment,
                        html_title=html_title,
                        http_headers=http_headers,
                    )

                    host.services.append(service)

                    # Target smart check:
                    # - Check if service is still reachable (possible that it has been
                    #   shut down since Shodan scan)
                    # - Perform web technologies detection: We could use the technologies
                    #   returned by Shodan API in host['data'][id]['http']['components'],
                    #   however it does not detect the version if it is possible
                    # - Initialize the context of the target via SmartModules, based on the
                    #   information already known (i.e. banner, web technologies...)
                    target = Target(service, self.services_config)
                    up = target.smart_check(
                        reverse_dns_lookup=False,  # Done by Shodan
                        availability_check=
                        True,  # Check if service is still reachable
                        nmap_banner_grabbing=False,  # Done by Shodan
                        html_title_grabbing=False,  # Done by Shodan
                        web_technos_detection=True,
                        smart_context_initialize=True)
                    # TODO: Add an option to disable web technos detections by Jok3r
                    # and only use technos names returned by Shodan (to speed up import
                    # if needed)
                    if not up:
                        logger.warning('Service not reachable')

            if host.services:
                results.append(host)

        return results
    def __run_for_single_target(self, args):
        """
        Run attack against a single target specified into argss
        """
        req = ServicesRequester(self.sqlsess)
        mission = None

        # Get Mission if target must be added into a mission scope
        if args.add:
            mission = self.sqlsess.query(Mission).filter(
                Mission.name == args.add).first()
            if not mission:
                raise AttackException(
                    'The specified mission does not exist in the database. You should create it if needed'
                )

        # Create new Service/Host objects (if service already exist, will be merged by ServicesRequester.add_target)
        service = Service(
            name=args.service,
            port=int(args.target_port),
            protocol={
                'tcp': Protocol.TCP,
                'udp': Protocol.UDP
            }.get(self.settings.services.get_protocol(args.service)),
            url=args.target_ip_or_url
            if args.target_mode == TargetMode.URL else '')
        host = Host(
            ip=args.target_ip_or_url if args.target_mode == TargetMode.IP else
            '')  # Will be updated when initializing Target()
        host.services.append(service)

        # Update credentials and options if needed
        for c in self.creds[args.service]:
            service.credentials.append(c)
        for u in self.users[args.service]:
            service.credentials.append(u)
        for o in self.options[args.service]:
            service.options.append(o)

        # Initialize Target and check if reachable
        target = Target(service, self.settings.services)
        if args.disable_banner_grab:
            logger.info('Check if target is reachable...')
        else:
            logger.info(
                'Check if target is reachable and grab banner using Nmap...')
        reachable = target.smart_check(
            grab_banner_nmap=not args.disable_banner_grab)

        if args.target_mode == TargetMode.IP:
            msg = 'Target {neg}reachable: host {ip} | port {port}/{proto} | service {service}'.format(
                neg='not ' if not reachable else '',
                ip=target.get_ip(),
                port=target.get_port(),
                proto=target.get_protocol(),
                service=target.get_service_name())
        else:
            msg = 'Target URL {url} is {neg}reachable'.format(
                url=target.get_url(), neg='not ' if not reachable else '')

        if reachable:
            service.up = True
            logger.success(msg)
        else:
            raise AttackException(msg)

        # Commit new data into database if target must be added to a mission
        if mission:
            logger.info(
                'Results from this attack will be saved under mission "{mission}" in database'
                .format(mission=mission.name))
            req.select_mission(mission.name)
            req.add_target(target)

        # Run the attack
        self.attack_scope.add_target(target)
        self.attack_scope.attack()
    def __run_for_multi_targets(self, args):
        """
        Run attack against multiple targets from the database
        """

        # Get Mission from which targets must be extracted
        mission = self.sqlsess.query(Mission).filter(
            Mission.name == args.mission).first()
        if mission:
            logger.info(
                'Extracting targets from mission "{mission}" ...'.format(
                    mission=mission.name))
        else:
            raise AttackException(
                'Mission {mission} does not exist into the database'.format(
                    mission=args.mission))

        # Initialize Services requester and add filter if provided
        requester = ServicesRequester(self.sqlsess)
        requester.select_mission(args.mission)
        if args.filters_combined:
            for filt in args.filter:
                logger.info(
                    'Applying filters on mission scope: {filter}'.format(
                        filter=filt))
            if len(args.filter) > 1:
                logger.info('Logical or is applied between each filter')
            requester.add_filter(args.filters_combined)

        # Retrieve targeted services from database
        services = requester.get_results()
        if not services:
            raise AttackException(
                'There is no matching service to target into the database')

        # Add each targeted service into Attack scope
        logger.info('Checking if targets are reachable...')
        for service in services:
            # Update credentials and options if needed
            for c in self.creds[service.name]:
                service.credentials.append(c)
            for u in self.users[service.name]:
                service.credentials.append(u)
            for o in self.options[service.name]:
                service.options.append(o)

            # Initialize Target and check if reachable
            target = Target(service, self.settings.services)
            service.up = target.smart_check(grab_banner_nmap=False)
            self.sqlsess.commit()

            msg = 'host {ip} | port {port}/{proto} | service {service}'.format(
                ip=target.get_ip(),
                port=target.get_port(),
                proto=target.get_protocol(),
                service=target.get_service_name())
            if service.up:
                logger.success('Target reachable: ' + msg)
            else:
                logger.warning('Target not reachable (skipped): ' + msg)
                continue

            # Update info into database if needed
            #requester.add_target(target)

            self.attack_scope.add_target(target)

        self.attack_scope.attack()
Beispiel #9
0
    def add_service(self,
                    ip,
                    port,
                    protocol,
                    service,
                    services_config,
                    grab_banner_nmap=True,
                    reverse_dns=True,
                    availability_check=True):
        """
        Add a service into the current mission scope in database.

        :param str ip: IP address to add
        :param int port: Port number
        :param str protocol: Protocol (tcp/udp)
        :param str service: Service name
        :param lib.core.ServicesConfig services_config: Services configuration object
        :param bool grab_banner_nmap: If set to True, run Nmap to grab server banner
        :param bool reverse_dns: If set to True, perform a reverse DNS lookup
        :param bool availability_check: If set to True, check if port is open
        :return: Status
        :rtype: bool
        """
        proto = {
            'tcp': Protocol.TCP,
            'udp': Protocol.UDP
        }.get(protocol, Protocol.TCP)

        service = Service(port=int(port), protocol=proto, name=service)
        service.host = Host(ip=ip)
        try:
            target = Target(service, services_config)
        except Exception as e:
            logger.error(e)
            return False

        matching_service = self.sqlsess.query(Service).join(Host).join(Mission)\
                                .filter(Mission.name == self.current_mission)\
                                .filter(Host.ip == service.host.ip)\
                                .filter(Service.port == int(port))\
                                .filter(Service.protocol == proto).first()

        if matching_service:
            logger.warning('Service already present into database')
            return False

        else:

            up = target.smart_check(reverse_dns, availability_check,
                                    grab_banner_nmap)

            if up:
                # Add service in db (and host if not existing)
                matching_host = self.sqlsess.query(Host).join(Mission)\
                                    .filter(Mission.name == self.current_mission)\
                                    .filter(Host.ip == service.host.ip).first()
                new_host = Host(ip=service.host.ip,
                                hostname=service.host.hostname,
                                os=service.host.os,
                                os_vendor=service.host.os_vendor,
                                os_family=service.host.os_family,
                                mac=service.host.mac,
                                vendor=service.host.vendor,
                                type=service.host.type)

                if matching_host:
                    matching_host.merge(new_host)
                    self.sqlsess.commit()
                    service.host = matching_host
                else:
                    mission = self.sqlsess.query(Mission)\
                                  .filter(Mission.name == self.current_mission).first()
                    new_host.mission = mission
                    service.host = new_host
                    self.sqlsess.add(new_host)

                self.sqlsess.add(service)
                self.sqlsess.commit()

                logger.success('Service added: host {ip} | port {port}/{proto} | ' \
                    'service {service}'.format(
                        ip=service.host.ip,
                        port=port,
                        proto=protocol,
                        service=service.name))
                return True

            else:
                logger.error(
                    'Service is not reachable, therefore it is not added')
                return False
Beispiel #10
0
    def add_url(self,
                url,
                services_config,
                reverse_dns=True,
                availability_check=True,
                grab_banner_nmap=True,
                web_technos_detection=True):
        """
        Add a URL into the current mission scope in database.

        :param str url: URL to add
        :param lib.core.ServicesConfig services_config: Services configuration object
        :param bool reverse_dns: If set to True, perform a reverse DNS lookup
        :param bool availability_check: If set to True, check if port is open
        :param bool grab_banner_nmap: If set to True, run Nmap to grab server banner  
        :param bool web_technos_detection: If set to True, try to detect web technos
        :return: Status
        :rtype: bool
        """
        matching_service = self.sqlsess.query(Service).join(Host).join(Mission)\
            .filter(Mission.name == self.current_mission)\
            .filter((Service.url == url) | \
                (Service.url == WebUtils.remove_ending_slash(url))).first()

        if matching_service:
            logger.warning('URL already present into database')
            return False

        else:
            service = Service(name='http', protocol=Protocol.TCP, url=url)
            service.host = Host()  # Update in target.smart_check()
            try:
                target = Target(service, services_config)
            except Exception as e:
                logger.error(e)
                return False

            up = target.smart_check(reverse_dns, availability_check,
                                    grab_banner_nmap, web_technos_detection)

            if up:
                matching_host = self.sqlsess.query(Host).join(Mission)\
                                            .filter(Mission.name == self.current_mission)\
                                            .filter(Host.ip == service.host.ip).first()
                new_host = Host(ip=service.host.ip,
                                hostname=service.host.hostname,
                                os=service.host.os,
                                os_vendor=service.host.os_vendor,
                                os_family=service.host.os_family,
                                mac=service.host.mac,
                                vendor=service.host.vendor,
                                type=service.host.type)

                if matching_host:
                    matching_host.merge(new_host)
                    self.sqlsess.commit()
                    service.host = matching_host
                else:
                    mission = self.sqlsess.query(Mission)\
                                  .filter(Mission.name == self.current_mission).first()
                    new_host.mission = mission
                    service.host = new_host
                    self.sqlsess.add(new_host)

                self.sqlsess.add(service)
                self.sqlsess.commit()
                logger.success('Service/URL added: {url}'.format(url=url))
                return True

            else:
                logger.error('URL is not reachable, therefore it is not added')
                return False
Beispiel #11
0
    def __run_for_single_target(self, args):
        """Run attack against a single target specified into args"""
        
        req = ServicesRequester(self.sqlsess)
        mission = None

        # Get Mission if target must be added into a mission scope
        if args.add:
            mission = self.sqlsess.query(Mission).filter(Mission.name == args.add).first()
            if not mission:
                raise AttackException('The specified mission does not exist in the ' \
                    'database. You should create it if needed')

        # Create new Service/Host objects (if service already exist, 
        # will be merged by ServicesRequester.add_target)
        url = args.target_ip_or_url if args.target_mode == TargetMode.URL else ''
        ip  = args.target_ip_or_url if args.target_mode == TargetMode.IP else ''
        service = Service(name=args.service,
                          port=int(args.target_port),
                          protocol=self.settings.services.get_protocol2(args.service),
                          url=url)
        host = Host(ip=ip) # Will be updated when initializing Target()
        host.services.append(service)

        # Update credentials, options, products if specified in command-line
        for c in self.creds[args.service]    : service.credentials.append(c)
        for u in self.users[args.service]    : service.credentials.append(u)
        for p in self.products[args.service] : service.products.append(p)
        for o in self.options[args.service]  : service.options.append(o)

        # Initialize Target
        try:
            target = Target(service, self.settings.services)
        except TargetException as e:
            logger.error(e)
            sys.exit(1)

        # Check if target is reachable 
        # (by default, perform reverve DNS lookup & Nmap banner grabbing)
        reachable = target.smart_check(
            reverse_dns=(args.reverse_dns is None or args.reverse_dns == 'on'),
            availability_check=True,
            grab_banner_nmap=(args.nmap_banner_grab is None \
                or args.nmap_banner_grab == 'on'))

        if args.target_mode == TargetMode.IP:
            msg = 'Target {neg}reachable: {target}'.format(
                neg='not ' if not reachable else '',
                target=target)
        else:
            msg = 'Target URL {url} is {neg}reachable'.format(
                url=target.get_url(),
                neg='not ' if not reachable else '')

        if reachable:
            logger.success(msg)
        else: 
            # Skip target if not reachable
            logger.error(msg)
            return

        # Commit new data into database if target must be added to a mission
        if mission:
            logger.info('Results from this attack will be saved under mission ' \
                '"{mission}" in database'.format(mission=mission.name))
            req.select_mission(mission.name)
            req.add_target(target)

        # Run the attack
        self.attack_scope.add_target(target)
        self.attack_scope.attack()
Beispiel #12
0
    def parse(self, 
              http_recheck=True, 
              html_title_grabbing=True,
              nmap_banner_grabbing=False,
              web_technos_detection=True):
        """
        Parse the Nmap results

        :param bool http_recheck: If set to True, TCP ports are re-checked for HTTP(s)
        :param bool html_title_grabbing: If set to True, grab title of HTML page (text in
            <title> tags) and put it as comment for HTTP service
        :param bool nmap_banner_grabbing: If set to True, run Nmap to grab 
            service banner for each service where it is missing (might be useful if 
            imported Nmap results come from a scan run without -sV/-A)
        :param bool web_technos_detection: If set to True, try to detect web technos
            for HTTP service

        :return: Hosts 
        :rtype: list(Host)|None
        """
        try:
            nmap_report = NmapParser.parse_fromfile(self.nmap_file)
        except Exception as e:
            logger.error('Error when parsing the Nmap file: {0}'.format(e))
            return None

        results = list()
        host_id = 0
        for h in nmap_report.hosts:

            host_id += 1

            # Get the fingerprinted OS if available
            os = ''
            os_vendor = ''
            os_family = ''
            device_type = ''
            if h.os_fingerprinted is True \
                    and h.os_match_probabilities() is not None \
                    and len(h.os_match_probabilities()) > 0:
                os_matchs = h.os_match_probabilities()
                if len(os_matchs) > 0:
                    os = os_matchs[0].name
                    if os_matchs[0].osclasses is not None \
                            and len(os_matchs[0].osclasses) > 0:
                        os_vendor = os_matchs[0].osclasses[0].vendor
                        os_family = os_matchs[0].osclasses[0].osfamily
                        device_type = OSUtils.get_device_type(
                            os,
                            os_family,
                            os_matchs[0].osclasses[0].type)

            # Create Host object
            host = Host(ip=h.ipv4, 
                        hostname=h.hostnames[0] if h.hostnames else h.ipv4,
                        os=os,
                        os_vendor=os_vendor,
                        os_family=os_family,
                        mac=h.mac,
                        vendor=h.vendor,
                        type=device_type)
            logger.info('[File {file} | Host {current_host}/{total_host}] ' \
                'Parsing host: {ip}{hostname} ...'.format(
                    file=FileUtils.extract_filename(self.nmap_file),
                    current_host=host_id,
                    total_host=len(nmap_report.hosts),
                    ip=host.ip, 
                    hostname=' ('+host.hostname+')' if host.hostname != host.ip else ''))

            # Loop over open ports
            port_id = 0
            for p in h.get_open_ports():
                port_id += 1
                s = h.get_service(p[0], protocol=p[1])
                name = get_service_name(s.service)
                url = ''
                comment = ''
                html_title = ''

                # Print current processed service
                print()
                logger.info('[File {file} | Host {current_host}/{total_host} | ' \
                    'Service {current_svc}/{total_svc}] Parsing service: ' \
                    'host {ip} | port {port}/{proto} | service {service} ...'.format(
                        file=FileUtils.extract_filename(self.nmap_file),
                        current_host=host_id,
                        total_host=len(nmap_report.hosts),
                        current_svc=port_id,
                        total_svc=len(h.get_open_ports()),
                        ip=h.ipv4, 
                        port=s.port, 
                        proto=s.protocol, 
                        service=name))

                # Get URL for http services
                if name == 'http':
                    if 'https' in s.service \
                       or 'ssl' in s.service \
                       or s.tunnel in ('ssl', 'tls'):
                        proto = 'https'
                    else:
                        proto = 'http'
                    url = '{proto}://{host}:{port}'.format(
                        proto=proto, host=host.hostname, port=s.port)

                # Recheck for HTTP/HTTPS for services undetermined by Nmap
                if http_recheck \
                   and s.protocol == 'tcp' \
                   and not self.services_config.is_service_supported(name, multi=False):

                    url = WebUtils.is_returning_http_data(host.ip, s.port)
                    if url:
                        logger.success('{url} seems to return HTTP data, marking it ' \
                            'as http service'.format(url=url))
                        name = 'http'

                # Only keep services supported by Jok3r
                if not self.services_config.is_service_supported(name, multi=False):
                    logger.warning('Service not supported: host {ip} | port ' \
                        '{port}/{proto} | service {service}'.format(
                            ip = h.ipv4, port=s.port, proto=s.protocol, service=name))
                    continue

                # # Deduce OS from banner if possible
                # if not os:
                #     host.os = OSUtils.os_from_nmap_banner(s.banner)
                #     if host.os:
                #         host.os_vendor = OSUtils.get_os_vendor(host.os)
                #         host.os_family = OSUtils.get_os_family(host.os)

                # Clean Nmap banner
                banner = NetUtils.clean_nmap_banner(s.banner)

                # Create Service object
                service = Service(
                    name=name,
                    name_original=s.service,
                    port=s.port,
                    protocol={'tcp': Protocol.TCP,'udp': Protocol.UDP}.get(s.protocol),
                    url=url,
                    up=True,
                    banner=banner,
                    comment=comment,
                    html_title=html_title)
                host.services.append(service)

                # Target smart check:
                # - Nmap banner grabbing if specified by user and banner is missing in 
                #   imported results;
                # - HTML title and HTTP response headers grabbing for HTTP service;
                # - Web technologies detection for HTTP service, except if disabled by
                #   user;
                # - Initialize the context of the target via SmartModules, based on the
                #   information already known (i.e. banner, web technologies...)
                target = Target(service, self.services_config)
                up = target.smart_check(
                    reverse_dns_lookup=False, # Done by Nmap 
                    availability_check=False, # Done by Nmap
                    nmap_banner_grabbing=nmap_banner_grabbing, # Default: False
                    html_title_grabbing=html_title_grabbing,
                    web_technos_detection=web_technos_detection, # Default: True
                    smart_context_initialize=True)
                if not up:
                    logger.warning('Service not reachable')

            if host.services:
                results.append(host)

        return results