def smart_check(self, grab_banner_nmap=False):
        """
        Check if the target is reachable and update target info
        :return: Boolean indicating status
        """
        # If no IP, means that DNS lookup has failed
        if not self.service.host.ip: 
            return False

        # For HTTP: Check URL availability, grab headers, grab HTML title
        if self.service.url: 
            is_reachable, status, resp_headers = WebUtils.is_url_reachable(self.service.url)
            self.service.up = is_reachable
            if resp_headers:
                self.service.http_headers = '\n'.join("{}: {}".format(key,val) for (key,val) in resp_headers.items())
            else:
                self.service.http_headers = ''

            self.service.comment = WebUtils.grab_html_title(self.service.url)
        # For any other service: Simple port check
        elif self.service.protocol == Protocol.TCP:
            self.service.up = NetUtils.is_tcp_port_open(str(self.service.host.ip), self.service.port)
        else:
            self.service.up = NetUtils.is_udp_port_open(str(self.service.host.ip), self.service.port)

        # Banner grabbing via Nmap (for TCP only) only if there is no banner already stored in db
        if grab_banner_nmap and self.service.up  and self.service.protocol == Protocol.TCP and not self.service.banner:
            self.service.banner = NetUtils.grab_banner_nmap(str(self.service.host.ip), self.service.port)

        return self.service.up
Esempio n. 2
0
    def initWithIP(self):
        """
		Initialize the target with an IP:PORT
		@Returns 	boolean
		"""
        # Check port
        if self.port < 0 or self.port > 65535:
            return False

        # Case where ip is actually a hostname
        if not NetUtils.isValidIP(self.ip):
            self.host = self.ip
            # DNS lookup to get IP corresponding to host (if several, just take the first one)
            ips = DnsUtils.dnsLookup(self.host)
            if not ips:
                return False
            self.ip = ips[0]
        else:
            self.host = self.ip

        # TODO: Optional Check
        # Check if IP:PORT is reachable
        if not self.no_port_check:
            if self.protocol == 'tcp':
                if not NetUtils.isTcpPortOpen(self.ip, self.port):
                    self.is_reachable = False
                    return False
            else:
                if not NetUtils.isUdpPortOpen(self.ip, self.port):
                    self.is_reachable = False
                    return False
        self.is_reachable = True
        return True
Esempio n. 3
0
    def __init_with_url(self):
        """
        Initialize with an URL (when targeting HTTP).
        This method updates: URL, Hostname, IP, Port

        :raises TargetException: Exception raised if DNS lookup fails
        """
        self.service.url = WebUtils.add_prefix_http(self.service.url)
        self.service.url = WebUtils.remove_ending_slash(self.service.url)
        url = urlparse(self.service.url)

        if NetUtils.is_valid_ip(url.hostname):
            self.service.host.ip = url.hostname
            self.service.host.hostname = url.hostname  # updated in smart_check

        else:
            self.service.host.ip = NetUtils.dns_lookup(url.hostname)
            if not self.service.host.ip:
                raise TargetException('Unable to resolve {}'.format(
                    url.hostname))
            self.service.host.hostname = url.hostname

        if not self.service.port:
            self.service.port = WebUtils.get_port_from_url(self.service.url)
            if not NetUtils.is_valid_port(self.service.port):
                raise TargetException('Invalid port number {}'.format(
                    self.service.port))
Esempio n. 4
0
    def __init_with_ip(self):
        """
        Initialize with an IP address or a hostname.
        This method updates: Hostname, IP

        :raises TargetException: Exception raised if DNS lookup fails
        """
        if NetUtils.is_valid_ip(self.service.host.ip):
            self.service.host.hostname = str(self.service.host.ip)
            # updated in smart_check
        else:
            # host.ip actually stores a hostname at this point, a DNS lookup is needed
            self.service.host.hostname = self.service.host.ip
            self.service.host.ip = NetUtils.dns_lookup(
                self.service.host.hostname)
            if self.service.host.ip:
                logger.info('DNS lookup on {hostname} -> IP: {ip}'.format(
                    hostname=self.service.host.hostname,
                    ip=self.service.host.ip))
            else:
                raise TargetException('Unable to resolve {}'.format(
                    self.service.host.hostname))

        # Forge URL for http services
        if self.service.name == 'http':
            if self.get_specific_option_value('https'):
                proto = 'https'
            else:
                proto = 'http'

            self.service.url = '{proto}://{ip}:{port}'.format(
                proto=proto, ip=self.service.host.ip, port=self.service.port)
Esempio n. 5
0
    def check_arg_ip_port(self, ip_port):
        ip, port = ip_port.split(':', maxsplit=1)
        if not NetUtils.is_valid_port(port):
            raise argparse.ArgumentTypeError('Invalid port number')
        if not NetUtils.is_valid_ip(ip):
            raise argparse.ArgumentTypeError('Invalid IP address')

        return ip, port
 def __init_with_ip(self):
     """
     """
     if NetUtils.is_valid_ip(self.service.host.ip):
         self.service.host.hostname = NetUtils.reverse_dns_lookup(str(self.service.host.ip))
     else:
         # host.ip actually stores a hostname at this point
         self.service.host.ip = NetUtils.dns_lookup(str(self.service.host.ip)) 
         self.service.host.hostname = self.service.host.ip
    def add_service(self, ip, hostname, port, protocol, service):
        protocol = {
            'tcp': Protocol.TCP,
            'udp': Protocol.UDP
        }.get(protocol, Protocol.TCP)
        matching_service = self.sqlsess.query(Service).join(Host).join(Mission)\
                                       .filter(Mission.name == self.current_mission)\
                                       .filter(Host.ip == ip)\
                                       .filter(Service.port == int(port))\
                                       .filter(Service.protocol == protocol).first()
        if protocol == Protocol.TCP:
            up = NetUtils.is_tcp_port_open(ip, port)
        else:
            up = NetUtils.is_udp_port_open(ip, port)

        if matching_service:
            logger.warning('Service already present into database')
        else:
            if up:
                logger.info(
                    'Grabbing banner from {ip}:{port} with Nmap...'.format(
                        ip=ip, port=port))
                banner = NetUtils.grab_banner_nmap(ip, port)
                logger.info('Banner: {}'.format(banner or 'None'))
                os = NetUtils.os_from_nmap_banner(banner)
                if os:
                    logger.info('Detected Host OS: {}'.format(os))
            else:
                logger.warning('Port seems to be closed !')

            # Add service in db (and host if not existing)
            service = Service(name=service,
                              port=int(port),
                              protocol=protocol,
                              up=up,
                              banner=banner)

            matching_host = self.sqlsess.query(Host).join(Mission)\
                                        .filter(Mission.name == self.current_mission)\
                                        .filter(Host.ip == ip).first()
            new_host = Host(ip=ip, hostname=hostname, os=os)
            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')
Esempio n. 8
0
    def __init_with_ip_port(self, ip, port, ssl):
        if not NetUtils.is_valid_ip(ip):
            raise TargetException('Invalid IP address')
        if not NetUtils.is_valid_port(port):
            raise TargetException('Invalid port number {}'.format(port))

        self.ip = ip
        self.port = port
        self.url = 'http{s}://{ip}:{port}'.format(s='s' if self.ssl else '',
                                                  ip=self.ip,
                                                  port=self.port)
Esempio n. 9
0
 def __translate_ip(self, value):
     """
     Translate IP address or IP range into Sqlalchemy filter.
     Range must be in CIDR format, e.g. 1.1.1.1/24
     """
     if NetUtils.is_valid_ip(value):
         return (Host.ip == value)
     elif NetUtils.is_valid_ip_range(value):
         return (Host.is_in_ip_range(value))
     else:
         raise FilterException(
             '{value} invalid IP/range'.format(value=value))
Esempio n. 10
0
 def __translate_port(self, value):
     """
     Translate port number or ports range into Sqlalchemy filter.
     Ports range in format: 8000-9000
     """
     if NetUtils.is_valid_port(value):
         return (Service.port == int(value))
     elif NetUtils.is_valid_port_range(value):
         minport, maxport = value.split('-')
         return (Sevrice.port.between(int(minport), int(maxport)))
     else:
         raise FilterException(
             '{value} invalid port/range'.format(value=value))
Esempio n. 11
0
 def __translate_ip(self, value):
     """
     value can be:
         - Single IP
         - IP range - format: 1.1.1.1/24
     """
     if NetUtils.is_valid_ip(value):
         return (Host.ip == value)
     elif NetUtils.is_valid_ip_range(value):
         return (Host.is_in_ip_range(value))
     else:
         raise FilterException(
             '{value} invalid IP/range'.format(value=value))
Esempio n. 12
0
 def __translate_port(self, value):
     """
     value can be:
         - Single port number
         - Port range - format 8000-8100
     """
     if NetUtils.is_valid_port(value):
         return (Service.port == int(value))
     elif NetUtils.is_valid_port_range(value):
         minport, maxport = value.split('-')
         return (Service.port.between(int(minport), int(maxport)))
     else:
         raise FilterException(
             '{value} invalid port/range'.format(value=value))
Esempio n. 13
0
    def __run_nmap(self):
        """
        Run Nmap against service to retrieve:
            - Service banner
            - OS info (os name, os vendor, os family) if possible
            - Device info (MAC, vendor, device type) if possible

        Updated in this method:
            - self.service.banner
            - self.service.host.os
            - self.service.host.os_vendor
            - self.service.host.os_family
            - self.service.host.mac
            - self.service.host.vendor
            - self.service.host.type
        """
        # Run Nmap scan
        nmap_info = NetUtils.grab_nmap_info(str(self.service.host.ip),
                                            self.service.port)

        # Get original service name as returned by Nmap
        self.service.name_original = nmap_info['service_name']

        # Get banner
        self.service.banner = NetUtils.clean_nmap_banner(nmap_info['banner'])
        logger.info('Banner = {banner}'.format(banner=self.service.banner))

        # Get OS information
        if nmap_info['os']:
            if not self.service.host.os:
                logger.info('Detected OS = {os}'.format(os=nmap_info['os']))

            elif self.service.host.os != nmap_info['os']:
                logger.info('Detected OS has changed = {os}'.format(
                    os=nmap_info['os']))

            self.service.host.os = nmap_info['os']
            self.service.host.os_vendor = nmap_info['os_vendor']
            self.service.host.os_family = nmap_info['os_family']

        # Get device information
        if nmap_info['mac']:
            self.service.host.mac = nmap_info['mac']

        if nmap_info['vendor']:
            self.service.host.vendor = nmap_info['vendor']

        if nmap_info['type']:
            self.service.host.type = nmap_info['type']
    def __init_with_url(self):
        """
        """
        self.service.url = WebUtils.add_prefix_http(self.service.url)
        url = urlparse(self.service.url)

        if NetUtils.is_valid_ip(url.hostname):
            self.service.host.ip = url.hostname
            self.service.host.hostname = NetUtils.reverse_dns_lookup(url.hostname)
        else:
            self.service.host.ip = NetUtils.dns_lookup(url.hostname)
            self.service.host.hostname = url.hostname

        if not self.service.port:
            self.service.port = WebUtils.get_port_from_url(self.service.url)
Esempio n. 15
0
    def __init_with_url(self, url):
        p = urlparse(url)
        if NetUtils.is_valid_ip(p.hostname):
            self.ip = p.hostname
        else:
            self.ip = NetUtils.dns_lookup(p.hostname)
        if not self.ip:
            raise TargetException('Unable to resolve {}'.format(p.hostname))

        self.port = NetUtils.get_port_from_url(url)
        if not NetUtils.is_valid_port(self.port):
            raise TargetException('Invalid port number {}'.format(self.port))

        if url.lower().startswith('https://'):
            self.ssl = True
Esempio n. 16
0
 def __replace_tag_localip(self):
     """
     Replace tag [LOCALIP] by the local IP address in self.formatted_cmdline.
     """
     pattern = re.compile('\[LOCALIP\]', re.IGNORECASE)
     self.formatted_cmdline = pattern.sub(NetUtils.get_local_ip_address(), 
                                          self.formatted_cmdline)
Esempio n. 17
0
    def __availability_check(self):
        """
        Check if TCP/UDP port is open

        Updated in this method:
            - self.service.up
        """
        if self.service.protocol == Protocol.TCP:
            # For TCP: simple port check
            self.service.up = NetUtils.is_tcp_port_open(
                str(self.service.host.ip), self.service.port)

        else:
            # For UDP: simple port check
            self.service.up = NetUtils.is_udp_port_open(
                str(self.service.host.ip), self.service.port)
    def do_hosts(self, args):
        """Hosts in the current mission scope"""
        print()
        req = HostsRequester(self.sqlsess)
        req.select_mission(self.current_mission)

        # Logical AND is applied between all specified filtering options
        filter_ = Filter(FilterOperator.AND)
        if args.addrs:
            for addr in args.addrs:
                if NetUtils.is_valid_ip(addr) or NetUtils.is_valid_ip_range(addr):
                    filter_.add_condition(Condition(addr, FilterData.IP))
                else:
                    filter_.add_condition(Condition(addr, FilterData.HOST))
        if args.search:
            filter_search = Filter(FilterOperator.OR)
            filter_search.add_condition(Condition(args.search, FilterData.HOST))
            filter_search.add_condition(Condition(args.search, FilterData.OS))
            filter_search.add_condition(Condition(args.search, FilterData.COMMENT_HOST))
            filter_.add_condition(filter_search)
        if args.order:
            req.order_by(args.order)

        try:
            req.add_filter(filter_)
        except FilterException as e:
            logger.error(e)
            return

        # Operations
        if args.comment:
            if not req.filter_applied:
                if not Output.prompt_confirm('No filter applied. Are you sure you want to edit comment for ALL hosts in current mission ?', default=False):
                    logger.info('Canceled')
                    return
            req.edit_comment(args.comment)
        elif args.delete:
            if not req.filter_applied:
                if not Output.prompt_confirm('No filter applied. Are you sure you want to delete ALL hosts and related services in current mission', default=False):
                    logger.info('Canceled')
                    return
            req.delete()
        else:
            req.show()

        print()
Esempio n. 19
0
    def run(self, target):
        try:
            self.command = Command(self.rawcmd, self.type)
        except CommandException as e:
            logger.error(e)
            return None

        # Build script to run
        if self.type == 'rce-blind':
            logger.warning('WARNING: This attack box must be reachable from the target !')
            logger.info('If target is vulnerable, exploit will try to ping local ' \
                'IP = {localip} from target'.format(
                    localip=NetUtils.get_local_ip_address()))

            print(self.command.get_cmdline(target))

            script = SCRIPT_RCE_BLIND.format(
                exploit_dir=self.directory,
                command=self.command.get_cmdline(target))

        elif self.type == 'rce-standard':
            script = self.command.get_cmdline(target)

        else:
            logger.error('Unsupported exploit type')
            return None

        # Run subprocess
        try:
            logger.info('Exploit will be run from directory: {directory}'.format(
                directory=self.directory))

            proc = subprocess.Popen(script, 
                                    shell=True, 
                                    stdout=subprocess.PIPE, 
                                    stderr=subprocess.STDOUT)

            # Agressivelly get the output
            while True:
                out = proc.stdout.read(1)
                # We put that inside try block to avoid utf8 decoding error
                try:
                    out = out.decode(sys.stdout.encoding)
                    sys.stdout.write(out)
                    self.output += out
                except:
                    pass

                # Break if process has finished
                if out == ''  and proc.poll() != None:
                    break

        except Exception as e:
            logger.error('Error when trying to run command: {exception}'.format(
                exception=e))
            return None

        return self.output
Esempio n. 20
0
    def __reverse_dns_lookup(self):
        """
        Attempt to perform reverse DNS lookup (i.e. IP -> Hostname)

        Updated in this method:
            - self.service.host.hostname
        """
        hostname = NetUtils.reverse_dns_lookup(self.service.host.ip)

        if hostname != self.service.host.ip:
            logger.info('{ip} -> {hostname}'.format(ip=self.service.host.ip,
                                                    hostname=hostname))
        else:
            logger.info('No DNS name found for IP')

        self.service.host.hostname = hostname
Esempio n. 21
0
    def smart_check(self,
                    reverse_dns=True,
                    availability_check=True,
                    grab_banner_nmap=False,
                    web_technos_detection=True):
        """
        Check if the target is reachable and update target information

        :param bool reverse_dns: Set to True to attempt performing reverse DNS lookup 
            when no hostname is specified (only IP)
        :param bool availability_check: Set to True to check for availability of the
            target, and also grab headers and HTML title for HTTP services
        :param bool grab_banner_nmap: Set to True to grab the Nmap banner (for TCP)
        :param bool web_technos_detection: Set to True to run WebTechnoDetector if 
            target service is HTTP
        :return: Result of check
        :rtype: bool
        """

        # If no IP, means that DNS lookup has failed
        if not self.service.host.ip:
            return False

        # Perform reverse DNS lookup if hostname not defined
        # Note: If lookup fails, it fallbacks to IP
        if reverse_dns:
            if self.service.host.hostname == self.service.host.ip:
                logger.info('Reverse DNS lookup for {ip}...'.format(
                    ip=str(self.service.host.ip)))
                hostname = NetUtils.reverse_dns_lookup(self.service.host.ip)

                if hostname != self.service.host.ip:
                    logger.info('{ip} -> {hostname}'.format(
                        ip=self.service.host.ip, hostname=hostname))
                else:
                    logger.info('No DNS name found for IP')

                self.service.host.hostname = hostname

        # Perform availability check
        if availability_check:
            logger.info('Check if service is reachable...')

            # For HTTP: Check URL availability, grab headers, grab HTML title
            if self.service.url:
                is_reachable, status, resp_headers = WebUtils.is_url_reachable(
                    self.service.url)
                self.service.up = is_reachable

                if is_reachable:
                    if resp_headers:
                        self.service.http_headers = '\n'.join('{}: {}'.format(key,val) \
                            for (key,val) in resp_headers.items())
                    else:
                        self.service.http_headers = ''

                    if not self.service.html_title:
                        self.service.html_title = WebUtils.grab_html_title(
                            self.service.url)

            # For any other service: Simple port check
            elif self.service.protocol == Protocol.TCP:
                self.service.up = NetUtils.is_tcp_port_open(
                    str(self.service.host.ip), self.service.port)
            else:
                self.service.up = NetUtils.is_udp_port_open(
                    str(self.service.host.ip), self.service.port)

            if not self.service.up:
                return False

        else:
            self.service.up = True  # consider it as up anyway

        # Banner grabbing via Nmap (for TCP only) only if there is no banner
        # already stored in db
        if grab_banner_nmap \
           and self.service.up  \
           and self.service.protocol == Protocol.TCP \
           and not self.service.banner:

            logger.info(
                'Grab banner for [{service}] via Nmap...'.format(service=self))
            self.service.banner = NetUtils.clean_nmap_banner(
                NetUtils.grab_banner_nmap(str(self.service.host.ip),
                                          self.service.port))
            logger.info('Banner: {banner}'.format(banner=self.service.banner))

            # Try to deduce OS from banner if possible
            if not self.service.host.os:
                detected_os = NetUtils.os_from_nmap_banner(self.service.banner)
                if detected_os:
                    self.service.host.os = detected_os
                    logger.info('Detected OS from banner = {os}'.format(
                        os=detected_os))

        # Web technologies detection for HTTP
        if self.service.name == 'http' and web_technos_detection:
            logger.info('Web technologies detection using Wappalyzer...')
            detector = WebTechnoDetector(self.service.url)
            technos = detector.detect()
            self.service.web_technos = str(technos)
            detector.print_technos()

            # Try to deduce OS from detected web technologies
            if not detected_os:
                detected_os = detector.get_os()
                if detected_os:
                    self.service.host.os = detected_os
                    logger.info(
                        'Detected OS from web technologies = {os}'.format(
                            os=detected_os))

        return self.service.up
Esempio n. 22
0
    def get_cmdline(self, target):
        cmdline = self.rawcmd

        # Replace tag [IP]
        pattern = re.compile('\[IP\]', re.IGNORECASE)
        cmdline = pattern.sub(target.ip, cmdline)

        # Replace tag [PORT]
        pattern = re.compile('\[PORT\]', re.IGNORECASE)
        cmdline = pattern.sub(target.port, cmdline)

        # Replace tag [URL]
        pattern = re.compile('\[URL\]', re.IGNORECASE)
        cmdline = pattern.sub(target.url, cmdline)

        # Replace tag [URIPATH]
        pattern = re.compile('\[URIPATH\]', re.IGNORECASE)
        try:
            o = urllib.parse.urlparse(url)
            uripath = o.path or '/'
        except:
            uripath = '/'
        cmdline = pattern.sub(uripath, cmdline)

        # Replace tag [SSL true="..."]
        pattern = re.compile(
            r'\[SSL\s+true\s*=\s*[\'"](?P<option>.*?)[\'"]\s*\]',
            re.IGNORECASE)

        m = pattern.search(cmdline)
        if target.ssl == True:
            cmdline = pattern.sub(m.group('option'), cmdline)
        else:
            cmdline = pattern.sub('', cmdline)

        # Replace tag [CMD]
        if '[cmd]' in cmdline.lower():
            try:
                pattern = re.compile('\[CMD\]', re.IGNORECASE)
                if CMD[self.type]['linux'] != CMD[self.type]['windows']:
                    cmdline_lin = pattern.sub(CMD[self.type]['linux'], cmdline)
                    cmdline_win = pattern.sub(CMD[self.type]['windows'],
                                              cmdline)
                    cmdline = '{0}; {1}'.format(cmdline_lin, cmdline_win)
                else:
                    cmdline = pattern.sub(CMD[self.type]['linux'], cmdline)

            except Exception as e:
                raise CommandException(e)
        elif '[cmdlinux]' in cmdline.lower(
        ) or '[cmdwindows]' in cmdline.lower():
            pattern = re.compile('\[CMDLINUX\]', re.IGNORECASE)
            cmdline = pattern.sub(CMD[self.type]['linux'], cmdline)

            pattern = re.compile('\[CMDWINDOWS\]', re.IGNORECASE)
            cmdline = pattern.sub(CMD[self.type]['windows'], cmdline)

        # Replace tag [LOCALIP]
        localip = NetUtils.get_local_ip_address()
        if localip == '127.0.0.1':
            raise CommandException('Unable to get local IP address')
        pattern = re.compile('\[LOCALIP\]', re.IGNORECASE)
        cmdline = pattern.sub(localip, cmdline)

        return cmdline
Esempio n. 23
0
    def run(self, target, mode, rce_command=''):
        """
        :param Target targer: Target instance
        :param str mode: mode can be either "detect" or "exploit"
        :param str rce_command: RCE command to run when running exploit (requires mode=exploit)
        """
        try:
            if mode == 'detect':
                self.command = Command(self.detection_rawcmd, self.type)
            elif mode == 'exploit':
                self.command = Command(self.exploit_rawcmd, self.type, self.exploit_rce_output)
        except CommandException as e:
            logger.error(e)
            return None

        # Build script to run
        if mode == 'exploit':
            if self.type == 'rce':
                if not self.exploit_rce_output:
                    logger.warning('The exploit will attempt to execute command on remote system but no '
                        'output will be available !')
                    # For RCE exploit without command output in test mode (no rce_command provided):
                    # Use script template that check for reverse connection with ICMP ping and HTTP requests
                    if len(rce_command) == 0:
                        logger.warning('WARNING: This attack box must be reachable from the target !')
                        logger.info('No command supplied to run through RCE, automatic exploit test will be started...')
                        logger.info('If target is vulnerable, exploit will try to ping (ICMP Echo request) and '
                            'to send HTTP request to local IP = {localip} from target'.format(
                                localip=NetUtils.get_local_ip_address()))

                        cmdline = self.command.get_cmdline(target)
                        print(cmdline)

                        script = SCRIPT_RCE_BLIND.format(
                            exploit_dir=self.directory,
                            command=cmdline)
                    else:
                        cmdline = self.command.get_cmdline(target, rce_command)
                        print(cmdline)
                        script  = 'cd {exploit_dir}; '.format(exploit_dir=self.directory)
                        script += cmdline
                else:
                    if len(rce_command) == 0:
                        logger.info('No command supplied to run through RCE, automatic exploit test will be started...')
                        logger.info('If target is vulnerable, exploit will try to run an echo command on target')
                        cmdline = self.command.get_cmdline(target)
                        print(cmdline)
                        script  = 'cd {exploit_dir}; '.format(exploit_dir=self.directory)
                        script += cmdline
                    else:
                        cmdline = self.command.get_cmdline(target, rce_command)
                        print(cmdline)
                        script  = 'cd {exploit_dir}; '.format(exploit_dir=self.directory)
                        script += cmdline                        

            else:
                cmdline = self.command.get_cmdline(target)
                print(cmdline)
                script  = 'cd {exploit_dir}; '.format(exploit_dir=self.directory)
                script += cmdline
        else:
            logger.warning('The script will attempt to detect if remote system is vulnerable without '
                'actually exploiting the vulnerability.')
            logger.warning('WARNING: False Positive is possible !')
            cmdline = self.command.get_cmdline(target)
            script  = 'cd {exploit_dir}; '.format(exploit_dir=self.directory)
            script += cmdline            

        # Run subprocess
        try:
            logger.info('{script} will be run from directory: {directory}'.format(
                script='Exploit' if mode == 'exploit' else 'Detection script',
                directory=self.directory))

            proc = subprocess.Popen(script, 
                                    shell=True, 
                                    executable='/bin/bash',
                                    stdout=subprocess.PIPE, 
                                    stderr=subprocess.STDOUT)

            # Agressivelly get the output
            while True:
                out = proc.stdout.read(1)
                # We put that inside try block to avoid utf8 decoding error
                try:
                    out = out.decode(sys.stdout.encoding)
                    sys.stdout.write(out)
                    self.output += out
                except:
                    pass

                # Break if process has finished
                if out == ''  and proc.poll() != None:
                    break

        except Exception as e:
            logger.error('Error when trying to run command: {exception}'.format(
                exception=e))
            return None

        return self.output
Esempio n. 24
0
    def parse(self, http_recheck=True, grab_html_title=True):
        """
        Parse the Nmap results

        :param bool http_recheck: If set to True, TCP ports are re-checked for HTTP(s)
        :param bool grab_html_title: If set to True, grab title of HTML page (text in
            <title> tags) and put it as comment 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()
        for h in nmap_report.hosts:

            # 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:
                os_matchs = h.os_match_probabilities()
                os = os_matchs[0].name if len(os_matchs) > 0 else ''
                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 = NetUtils.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 '',
                        os=os,
                        os_vendor=os_vendor,
                        os_family=os_family,
                        mac=h.mac,
                        vendor=h.vendor,
                        type=device_type)
            logger.info('Parsing host: {ip}{hostname} ...'.format(
                ip=host.ip,
                hostname=' (' + host.hostname + ')' if host.hostname else ''))

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

                # 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.ip,
                                                           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.hostname or host.ip, s.port)
                    if url:
                        logger.success('{url} seems to return HTTP data, marking it ' \
                            'as http service'.format(url=url))
                        name = 'http'

                # Grab page title for HTTP services
                if grab_html_title and name == 'http':
                    html_title = WebUtils.grab_html_title(url)

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

                # Deduce OS from banner if possible
                if not os:
                    host.os = NetUtils.os_from_nmap_banner(s.banner)

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

                # Create Service object
                service = Service(name=name,
                                  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)

                # Already add specific option https=True if possible
                if name == 'http' and url.startswith('https://'):
                    service.options.append(Option(name='https', value='true'))

                host.services.append(service)

            if host.services:
                results.append(host)

        return results
Esempio n. 25
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
Esempio n. 26
0
 def __replace_tag_localip(self):
     pattern = re.compile('\[LOCALIP\]', re.IGNORECASE)
     self.parsed_cmdline = pattern.sub(NetUtils.get_local_ip_address(),
                                       self.parsed_cmdline)
    def add_url(self, url):
        matching_service = self.sqlsess.query(Service).join(Host).join(Mission)\
                                       .filter(Mission.name == self.current_mission)\
                                       .filter(Service.url == url).first()
        if matching_service:
            logger.warning('URL already present into database')
        else:

            # Parse URL: Get IP, hostname, port
            parsed = urlparse(url)
            if NetUtils.is_valid_ip(parsed.hostname):
                ip = parsed.hostname
                hostname = NetUtils.reverse_dns_lookup(parsed.hostname)
            else:
                ip = NetUtils.dns_lookup(parsed.hostname)
                if not ip:
                    logger.error('Host cannot be resolved')
                    return
                hostname = parsed.hostname
            port = WebUtils.get_port_from_url(url)

            # Check URL, grab headers, html title
            is_reachable, status, resp_headers = WebUtils.is_url_reachable(url)
            if is_reachable:
                comment = WebUtils.grab_html_title(url)
                if resp_headers:
                    http_headers = '\n'.join("{}: {}".format(key, val)
                                             for (key,
                                                  val) in resp_headers.items())

                logger.info('HTTP Headers:')
                print(http_headers)
                logger.info('Title: {}'.format(comment))
                logger.info(
                    'Grabbing banner from {ip}:{port} with Nmap...'.format(
                        ip=ip, port=port))
                banner = NetUtils.grab_banner_nmap(ip, port)
                logger.info('Banner: {}'.format(banner or 'None'))
                os = NetUtils.os_from_nmap_banner(banner)
                if os:
                    logger.info('Detected Host OS: {}'.format(os))
            else:
                comment = 'Not reachable'
                banner = http_headers = ''
                logger.warning('URL seems not to be reachable')

            # Add service in db (and host if not existing)
            service = Service(name='http',
                              port=port,
                              protocol=Protocol.TCP,
                              url=url,
                              up=is_reachable,
                              http_headers=http_headers,
                              banner=banner,
                              comment=comment)

            matching_host = self.sqlsess.query(Host).join(Mission)\
                                        .filter(Mission.name == self.current_mission)\
                                        .filter(Host.ip == ip).first()
            new_host = Host(ip=ip, hostname=hostname, os=os)
            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')
Esempio n. 28
0
    def get_cmdline(self, target, rce_command=''):
        """
        :param Target target: Target instance
        :param str rce_command: Command to execute on vulnerable system through RCE vuln
        """
        cmdline = self.rawcmd

        # Replace tag [IP]
        pattern = re.compile('\[IP\]', re.IGNORECASE)
        cmdline = pattern.sub(target.ip, cmdline)

        # Replace tag [PORT]
        pattern = re.compile('\[PORT\]', re.IGNORECASE)
        cmdline = pattern.sub(str(target.port), cmdline)

        # Replace tag [URL]
        pattern = re.compile('\[URL\]', re.IGNORECASE)
        cmdline = pattern.sub(str(target.url), cmdline)

        # Replace tag [BASEURL]
        pattern = re.compile('\[BASEURL\]', re.IGNORECASE)
        baseurl = target.url.split('//')[0] + '//' + target.url.split(
            '//')[1].split('/')[0]
        cmdline = pattern.sub(str(baseurl), cmdline)

        # Replace tag [URIPATH]
        pattern = re.compile('\[URIPATH\]', re.IGNORECASE)
        try:
            o = urllib.parse.urlparse(url)
            uripath = o.path or '/'
        except:
            uripath = '/'
        cmdline = pattern.sub(uripath, cmdline)

        # Replace tag [SSL true="..."]
        pattern = re.compile(
            r'\[SSL\s+true\s*=\s*[\'"](?P<option>.*?)[\'"]\s*\]',
            re.IGNORECASE)

        m = pattern.search(cmdline)
        if m:
            if target.ssl == True:
                cmdline = pattern.sub(m.group('option'), cmdline)
            else:
                cmdline = pattern.sub('', cmdline)

        # Replace tag [CMD]
        if '[cmd]' in cmdline.lower():
            try:
                pattern = re.compile('\[CMD\]', re.IGNORECASE)

                # If command provided by user, replace tag by this command, otherwise
                # use the predefined commands for automatic test
                if len(rce_command) > 0:
                    cmdline = pattern.sub(rce_command, cmdline)
                else:
                    if self.type == 'rce' and not self.exploit_rce_output:
                        cmdline_lin = pattern.sub(CMD['rce-blind']['linux'],
                                                  cmdline)
                        cmdline_lin2 = pattern.sub(CMD['rce-blind']['linux2'],
                                                   cmdline)
                        cmdline_lin3 = pattern.sub(CMD['rce-blind']['linux3'],
                                                   cmdline)
                        cmdline_win = pattern.sub(CMD['rce-blind']['windows'],
                                                  cmdline)
                        cmdline = '{0}; {1}; {2}; {3}'.format(
                            cmdline_lin, cmdline_lin2, cmdline_lin3,
                            cmdline_win)
                    else:
                        cmdline = pattern.sub(CMD['rce-standard']['linux'],
                                              cmdline)

            except Exception as e:
                raise CommandException(e)

        # Special case where Linux/Windows command line differ
        elif '[cmdlinux]' in cmdline.lower(
        ) or '[cmdwindows]' in cmdline.lower():
            try:
                pattern_linux = re.compile('\[CMDLINUX\]', re.IGNORECASE)
                pattern_windows = re.compile('\[CMDWINDOWS\]', re.IGNORECASE)

                # If command provided by user, replace both tag by this command, otherwise
                # use the predefined commands for automatic test
                if len(rce_command) > 0:
                    cmdline = pattern_linux.sub(rce_command, cmdline)
                    cmdline = pattern_windows.sub(rce_command, cmdline)
                else:
                    if self.type == 'rce' and not self.exploit_rce_output:
                        # Replace [CMDLINUX]
                        cmdline_lin = pattern_linux.sub(
                            CMD['rce-blind']['linux'], cmdline)
                        cmdline_lin2 = pattern_linux.sub(
                            CMD['rce-blind']['linux2'], cmdline)
                        cmdline_lin3 = pattern_linux.sub(
                            CMD['rce-blind']['linux3'], cmdline)
                        cmdline = '{0}; {1}; {2}'.format(
                            cmdline_lin, cmdline_lin2, cmdline_lin3)

                        # Replace [CMDWINDOWS]
                        cmdline = pattern_windows.sub(
                            CMD['rce-blind']['windows'], cmdline)
                    else:
                        # Replace [CMDLINUX]
                        cmdline = pattern_linux.sub(
                            CMD['rce-standard']['linux'], cmdline)

                        # Replace [CMDWINDOWS]
                        cmdline = pattern_windows.sub(
                            CMD['rce-standard']['windows'], cmdline)
            except Exception as e:
                raise CommandException(e)

        # Replace tag [LOCALIP]
        localip = NetUtils.get_local_ip_address()
        if localip == '127.0.0.1':
            raise CommandException('Unable to get local IP address')
        pattern = re.compile('\[LOCALIP\]', re.IGNORECASE)
        cmdline = pattern.sub(localip, cmdline)

        return cmdline
    def do_services(self, args):
        """Services in the current mission scope"""
        print()
        req = ServicesRequester(self.sqlsess)
        req.select_mission(self.current_mission)

        # Logical AND is applied between all specified filtering options
        filter_ = Filter(FilterOperator.AND)
        if args.names:
            for n in args.names:
                if not self.settings.services.is_service_supported(n, multi=False):
                    logger.error('Service {name} is not valid/supported'.format(name=n.lower()))
                    return
            filter_.add_condition(Condition(args.names, FilterData.SERVICE_EXACT))

        if args.order:
            req.order_by(args.order)

        if args.hostname:
            # OR between submitted hostnames
            filter_.add_condition(Condition(args.hostname.split(','), FilterData.HOST))
        if args.ip:
            # OR between submitted ips/ranges
            filter_.add_condition(Condition(args.ip.split(','), FilterData.IP))
        if args.port:
            # OR between ports/port-ranges
            filter_.add_condition(Condition(args.port.split(','), FilterData.PORT))
        if args.proto:
            filter_.add_condition(Condition(args.proto, FilterData.PROTOCOL))
        if args.up:
            filter_.add_condition(Condition(args.up, FilterData.UP))
        if args.search:
            filter_search = Filter(FilterOperator.OR)
            filter_search.add_condition(Condition(args.search, FilterData.HOST))
            filter_search.add_condition(Condition(args.search, FilterData.BANNER))
            filter_search.add_condition(Condition(args.search, FilterData.URL))
            filter_search.add_condition(Condition(args.search, FilterData.COMMENT_SERVICE))
            filter_.add_condition(filter_search)

        try:
            req.add_filter(filter_)
        except FilterException as e:
            logger.error(e)
            return

        # Operations
        if args.add:
            host, port, service = args.add
            if NetUtils.is_valid_ip(host):
                ip = host
                hostname = NetUtils.reverse_dns_lookup(ip) 
                logger.info('Reverse DNS lookup on IP {ip}: {hostname}'.format(ip=ip, hostname=hostname))
            else:
                ip = NetUtils.dns_lookup(host)
                if not ip:
                    logger.error('Cannot resolve hostname')
                    return
                hostname = host
                logger.info('DNS lookup on {hostname}: IP {ip}'.format(hostname=host, ip=ip))

            if not NetUtils.is_valid_port(port):
                logger.error('Port is invalid, not in range [0-65535]')
            elif not self.settings.services.is_service_supported(service, multi=False):
                logger.error('Service {name} is not valid/supported'.format(name=service.lower()))
            else:
                req.add_service(ip, hostname, port, self.settings.services.get_protocol(service), service)
        elif args.url:
            args.url = WebUtils.add_prefix_http(args.url)
            if not WebUtils.is_valid_url(args.url):
                logger.error('URL is invalid')
            else:
                req.add_url(args.url)
        elif args.delete:
            if not req.filter_applied:
                if not Output.prompt_confirm('No filter applied. Are you sure you want to delete ALL services in current mission ?', default=False):
                    logger.info('Canceled')
                    return
            req.delete()
        elif args.comment:
            if not req.filter_applied:
                if not Output.prompt_confirm('No filter applied. Are you sure you want to edit comment for ALL services in current mission ?', default=False):
                    logger.info('Canceled')
                    return
            req.edit_comment(args.comment)
        elif args.https:
            if not req.filter_applied:
                if not Output.prompt_confirm('No filter applied. Are you sure you want to apply switch for ALL URLs in current mission ?', default=False):
                    logger.info('Canceled')
                    return
            req.switch_https()         
        elif args.addcred:
            if not req.filter_applied:
                if not Output.prompt_confirm('No filter applied. Are you sure you want to add same creds for ALL services in current mission ?', default=False):
                    logger.info('Canceled')
                    return
            req.add_cred(args.addcred[0], args.addcred[1], None) 
        elif args.addcred_http:
            if not req.are_only_http_services_selected():
                logger.warning('Some non-HTTP services are selected. Use --addcred instead for non-HTTP services')
                return
            if not self.settings.services.is_valid_authentication_type(args.addcred_http[2]):
                logger.warning('Invalid HTTP authentication type')
                logger.info('List of supported authentication types: ')
                for auth_type in self.settings.services.get_authentication_types('http'):
                    logger.info('- {type}'.format(type=auth_type))
                return
            if not req.filter_applied:
                if not Output.prompt_confirm('No filter applied. Are you sure you want to add same creds for ALL HTTP services in current mission ?', default=False):
                    logger.info('Canceled')
                    return
            req.add_cred(args.addcred_http[0], args.addcred_http[1], args.addcred_http[2]) 
        elif args.adduser:
            if not req.filter_applied:
                if not Output.prompt_confirm('No filter applied. Are you sure you want to add same username for ALL services in current mission ?', default=False):
                    logger.info('Canceled')
                    return
            req.add_cred(args.adduser[0], None, None)
        elif args.adduser_http:
            if not req.are_only_http_services_selected():
                logger.warning('Some non-HTTP services are selected. Use --adduser instead for non-HTTP services')
                return
            if not self.settings.services.is_valid_authentication_type(args.adduser_http[1]):
                logger.warning('Invalid HTTP authentication type')
                logger.info('List of supported authentication types: ')
                for auth_type in self.settings.services.get_authentication_types('http'):
                    logger.info('- {type}'.format(type=auth_type))
                return
            if not req.filter_applied:
                if not Output.prompt_confirm('No filter applied. Are you sure you want to add same username for ALL HTTP services in current mission ?', default=False):
                    logger.info('Canceled')
                    return
            req.add_cred(args.adduser_http[0], None, args.adduser_http[1]) 
        else:
            req.show()                      

        print()