Ejemplo n.º 1
0
class LP ( cmd.Cmd ):

    #===========================================================================
    #   HOUSEKEEPING
    #===========================================================================
    prompt = '<NEED_LOGIN> %> '

    def __init__( self ):
        cmd.Cmd.__init__( self )
        self.be = None
        self.user = None
        self.hbsKey = None
        self.aid = None
        self.investigationId = None
        self.tags = Symbols()
        readline.set_completer_delims(":;'\"? \t")

    def updatePrompt( self ):
        self.prompt = '%s%s / %s %s%%> ' % ( ( '' if self.hbsKey is None else '* ' ),
                                           ( '' if self.user is None else self.user ),
                                           ( '' if self.aid is None else self.aid ),
                                           ( '' if ( self.investigationId is None or self.investigationId == '' ) else ' : %s ' % self.investigationId ) )

    def getParser( self, desc, isHbsTask = False ):
        parser = argparse.ArgumentParser( prog = desc )

        if isHbsTask:
            parser.add_argument( '-!',
                                  type = AgentId,
                                  required = False,
                                  default = AgentId( self.aid ),
                                  help = 'agent id to change context to ONLY for the duration of this command.',
                                  dest = 'toAgent' )

            parser.add_argument( '-x',
                                  type = int,
                                  required = False,
                                  default = ( 60 * 60 * 1 ),
                                  help = 'set this command\'s specific expiry time in seconds.',
                                  dest = 'expiry' )

            parser.add_argument( '-@',
                                  type = str,
                                  required = False,
                                  default = self.investigationId,
                                  help = 'the investigation id to attach to the command, results and side-effects.',
                                  dest = 'investigationId' )

        return parser

    def parse( self, parser, line ):
        try:
            return parser.parse_args( shlex.split( line ) )
        except SystemExit:
            return None

    def do_exit( self, s ):
        return True

    def do_quit( self, s ):
        return True

    def emptyline( self ):
        pass

    def completedefault( self, text, line, begidx, endidx ):
        def get_possible_filename_completions(text):
            head, tail = os.path.split(text.strip())
            if head == "": #no head
                head = "."
            files = os.listdir(head)
            return [ os.path.join( head, f ) for f in files if f.startswith(tail) ]

        if begidx != 0:
            return get_possible_filename_completions( text )
        else:
            return [ x[ 3 : ] for x in dir( self ) if x.startswith( 'do_' ) ]

    def execAndPrintResponse( self, command, arguments, isHbsTask = False ):
        if isHbsTask:
            tmp = arguments
            arguments = argparse.Namespace()

            if not tmp.toAgent.isValid or self.hbsKey is None:
                print( 'Agent id and hbs key must be set in context.' )
                return

            if not hasattr( tmp, 'key' ) or tmp.key is None:
                setattr( tmp, 'key', self.hbsKey )

            if ( tmp.investigationId is not None ) and '' != tmp.investigationId:
                setattr( arguments, 'investigationId', tmp.investigationId )

            setattr( arguments, 'toAgent', tmp.toAgent )
            setattr( arguments, 'task', tmp.task )
            setattr( arguments, 'key', tmp.key )
            setattr( arguments, 'id', tmp.id )
            setattr( arguments, 'expiry', int( tmp.expiry + time.time() ) )

            del( tmp )

        for k, a in vars( arguments ).iteritems():
            if type( a ) is AgentId:
                if not a.isValid:
                    print( 'Invalid agent id.' )
                    return
                else:
                    setattr( arguments, k, str( a ) )

        results = command( **vars( arguments ) )

        if results.isSuccess:
            print( "<<<SUCCESS>>>" )
        elif results.isTimedOut:
            print( "<<<TIMEOUT>>>" )
        else:
            if 0 == len( results.error ):
                print( "<<<FAILURE>>>" )
            else:
                print( "<<<FAILURE: %s>>>" % results.error )
        pprint.pprint( results.data, indent = 2, width = 80 )

        return results.data

    def getTags( self, tagsPath ):
        raw_tags = json.loads( open( tagsPath, 'r' ).read() )
        tags = Symbols()
        for group in raw_tags[ 'groups' ]:
            g = Symbols()
            for definition in group[ 'definitions' ]:
                setattr( g, definition[ 'name' ], str( definition[ 'value' ] ) )
            setattr( tags, group[ 'groupName' ], g )

        return tags

    #===========================================================================
    #   SESSION SETTING COMMANDS
    #===========================================================================
    @report_errors
    def do_login( self, s ):
        '''Login to the BE using credentials stored in a config file.'''

        parser = self.getParser( 'login' )
        parser.add_argument( 'configFile',
                             type = argparse.FileType( 'r' ),
                             help = 'config file specifying the endpoint and token to use' )
        parser.add_argument( '-k', '--key',
                             required = False,
                             default = None,
                             #type = argparse.FileType( 'r' ),
                             type = str,
                             help = 'key to use to sign hbs tasks',
                             dest = 'key' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            try:
                config = json.loads( arguments.configFile.read() )
            except:
                print( "Invalid config file format (JSON): %s" % traceback.format_exc() )
                return

            if 'beach_config' not in config or 'token' not in config:
                print( "Missing endpoint or token in config." )
                return

            _ = os.getcwd()
            os.chdir( os.path.dirname(  __file__ ) )
            self.be = BEAdmin( config[ 'beach_config' ], config[ 'token' ] )
            os.chdir( _ )

            remoteTime = self.be.testConnection()

            if remoteTime.isTimedOut:
                print( "Endpoint did not respond." )
                return

            if 'pong' not in remoteTime.data:
                print( "Endpoint responded with invalid data." )
                return

            if arguments.key is not None:
                if os.path.isfile( arguments.key ):
                    try:
                        password = getpass.getpass()
                        print( "...decrypting key..." )
                        # There are weird problems with pexpect and newlines and binary, so
                        # we have to brute force it a bit
                        for i in range( 0, 30 ):
                            proc = pexpect.spawn( 'openssl aes-256-cbc -d -in %s' % arguments.key )
                            proc.expect( [ 'enter aes-256-cbc decryption password: *' ] )
                            proc.sendline( password )
                            proc.expect( "\r\n" )
                            proc.expect( ".*" )
                            self.hbsKey = proc.match.group( 0 ).replace( "\r\n", "\n" )
                            try:
                                testSign = Signing( self.hbsKey )
                                testSig = testSign.sign( 'a' )
                                if testSig is not None:
                                    break
                            except:
                                self.hbsKey = None

                        if self.hbsKey is not None:
                            print( "success, authenticated!" )
                        else:
                            print( "error loading key, bad key format or password?" )
                    except:
                        self.hbsKey = None
                        print( "error getting cloud key: %s" % traceback.format_exc() )
                    if self.hbsKey is not None and 'bad decrypt' in self.hbsKey:
                        print( "Invalid password" )
                        self.hbsKey = None
                else:
                    print( "Invalid key file: %s." % arguments.key )
                    self.hbsKey = None
            else:
                self.hbsKey = None

            remoteTime = remoteTime.data.get( 'pong', 0 )
            print( "Successfully logged in." )
            print( "Remote endpoint time: %s." % remoteTime )

            self.user = config[ 'token' ].split( '/' )[ 0 ]

            self.updatePrompt()

    @report_errors
    def do_chid( self, s ):
        '''Change execution context to a specific agent id, used only for HBS tasking.'''

        parser = self.getParser( 'chid' )
        parser.add_argument( 'aid',
                             type = AgentId,
                             help = 'agent id to change context to' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            aid = arguments.aid
            if aid.isValid:
                self.aid = aid
                self.updatePrompt()
            else:
                print( 'Agent Id is not valid.' )

    @report_errors
    def do_setInvestigationId( self, s ):
        '''Change execution context to relate to a specific investigationId, used only for HBS tasking.'''

        parser = self.getParser( 'setInvestigationId' )
        parser.add_argument( 'investigationId',
                             type = str,
                             help = 'investigation id to change context to' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.investigationId = arguments.investigationId
            self.updatePrompt()



    #===========================================================================
    #   HCP COMMANDS
    #===========================================================================
    @report_errors
    def do_hcp_getAgentStates( self, s ):
        '''Get the general state of agents.'''

        parser = self.getParser( 'getAgentStates' )
        parser.add_argument( '-a', '--aid',
                              type = AgentId,
                              required = False,
                              help = 'agent id to retrieve the info of',
                              dest = 'aid' )
        parser.add_argument( '-n', '--hostname',
                              type = str,
                              required = False,
                              help = 'hostname of the agent to retrieve the info of',
                              dest = 'hostname' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_getAgentStates, arguments )

    @report_errors
    def do_hcp_setPeriod( self, s ):
        '''Set the period agents beacon back.'''

        parser = self.getParser( 'setPeriod' )
        parser.add_argument( 'period',
                             type = int,
                             help = 'new period to schedule hcp beacons over' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_setPeriod, arguments )

    @report_errors
    def do_hcp_getPeriod( self, s ):
        '''Get the current agent beacon period.'''

        parser = self.getParser( 'getPeriod' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_getPeriod, arguments )

    @report_errors
    def do_hcp_addEnrollmentRule( self, s ):
        '''Add a new enrollment rule for new agents.'''

        parser = self.getParser( 'addEnrollmentRule' )
        parser.add_argument( '-m', '--mask',
                             type = AgentId,
                             required = True,
                             help = 'agent id mask this rule applies to',
                             dest = 'mask' )
        parser.add_argument( '-i', '--internalip',
                             type = str,
                             required = False,
                             default = '255.255.255.255',
                             help = 'internal ip mask the rule applies to (255 wildcard)',
                             dest = 'internalIp' )
        parser.add_argument( '-e', '--externalip',
                             type = str,
                             required = False,
                             default = '255.255.255.255',
                             help = 'external ip mask the rule applies to (255 wildcard)',
                             dest = 'externalIp' )
        parser.add_argument( '-s', '--newsubnet',
                             type = hexArg,
                             required = True,
                             help = 'new subnet to give to agents matching this rule (hex)',
                             dest = 'newSubnet' )
        parser.add_argument( '-o', '--neworg',
                             type = hexArg,
                             required = True,
                             help = 'new org to give to agents matching this rule (hex)',
                             dest = 'newOrg' )
        parser.add_argument( '-n', '--hostname',
                             type = str,
                             required = False,
                             default = '',
                             help = 'hostname of the host',
                             dest = 'hostname' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_addEnrollmentRule, arguments )

    @report_errors
    def do_hcp_delEnrollmentRule( self, s ):
        '''Remove an enrollment rule for new agents.'''

        parser = self.getParser( 'addEnrollmentRule' )
        parser.add_argument( '-m', '--mask',
                             type = AgentId,
                             required = True,
                             help = 'agent id mask this rule applies to',
                             dest = 'mask' )
        parser.add_argument( '-i', '--internalip',
                             type = str,
                             required = False,
                             default = '255.255.255.255',
                             help = 'internal ip mask the rule applies to (255 wildcard)',
                             dest = 'internalIp' )
        parser.add_argument( '-e', '--externalip',
                             type = str,
                             required = False,
                             default = '255.255.255.255',
                             help = 'external ip mask the rule applies to (255 wildcard)',
                             dest = 'externalIp' )
        parser.add_argument( '-s', '--newsubnet',
                             type = hexArg,
                             required = True,
                             help = 'new subnet to give to agents matching this rule (hex)',
                             dest = 'newSubnet' )
        parser.add_argument( '-o', '--neworg',
                             type = hexArg,
                             required = True,
                             help = 'new org to give to agents matching this rule (hex)',
                             dest = 'newOrg' )
        parser.add_argument( '-n', '--hostname',
                             type = str,
                             required = False,
                             default = '',
                             help = 'hostname of the host',
                             dest = 'hostname' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_delEnrollmentRule, arguments )

    @report_errors
    def do_hcp_getEnrollmentRules( self, s ):
        '''Get the list of enrollment rules for new agents.'''

        parser = self.getParser( 'getPeriod' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_getEnrollmentRules, arguments )

    @report_errors
    def do_hcp_addTasking( self, s ):
        '''Task a module to a list of agents.'''

        parser = self.getParser( 'addTasking' )
        parser.add_argument( '-m', '--mask',
                             type = AgentId,
                             required = True,
                             help = 'agent id mask of the rule',
                             dest = 'mask' )
        parser.add_argument( '-i', '--moduleid',
                             type = int,
                             required = True,
                             help = 'module id to task',
                             dest = 'moduleId' )
        parser.add_argument( '-s', '--hash',
                             type = str,
                             required = True,
                             help = 'hash of the module to task',
                             dest = 'hashStr' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_addTasking, arguments )

    @report_errors
    def do_hcp_delTasking( self, s ):
        '''Remove a module from tasking to agents.'''

        parser = self.getParser( 'delTasking' )
        parser.add_argument( '-m', '--mask',
                             type = AgentId,
                             required = True,
                             help = 'agent id mask of the rule',
                             dest = 'mask' )
        parser.add_argument( '-i', '--moduleid',
                             type = int,
                             required = True,
                             help = 'module id tasked',
                             dest = 'moduleId' )
        parser.add_argument( '-s', '--hash',
                             type = str,
                             required = True,
                             help = 'hash of the module to untask',
                             dest = 'hashStr' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_delTasking, arguments )

    @report_errors
    def do_hcp_getTaskings( self, s ):
        '''Get the list of modules tasked to agents.'''

        parser = self.getParser( 'getTaskings' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_getTaskings, arguments )

    @report_errors
    def do_hcp_addModule( self, s ):
        '''Add a taskable module.'''

        parser = self.getParser( 'addModule' )
        parser.add_argument( '-i', '--moduleid',
                             type = int,
                             required = True,
                             help = 'module id',
                             dest = 'moduleId' )
        parser.add_argument( '-b', '--binary',
                             type = argparse.FileType( 'r' ),
                             required = True,
                             help = 'path to file containing module',
                             dest = 'binary' )
        parser.add_argument( '-s', '--signature',
                             type = argparse.FileType( 'r' ),
                             required = True,
                             help = 'path to file containing signature of the module',
                             dest = 'signature' )
        parser.add_argument( '-d', '--description',
                             type = str,
                             required = True,
                             help = 'description of the module',
                             dest = 'description' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            arguments.binary = arguments.binary.read()
            arguments.signature = arguments.signature.read()
            self.execAndPrintResponse( self.be.hcp_addModule, arguments )

    @report_errors
    def do_hcp_delModule( self, s ):
        '''Remove a taskable module.'''

        parser = self.getParser( 'delTasking' )
        parser.add_argument( '-i', '--moduleid',
                             type = int,
                             required = True,
                             help = 'module id',
                             dest = 'moduleId' )
        parser.add_argument( '-s', '--hash',
                             type = str,
                             required = True,
                             help = 'hash of the module',
                             dest = 'hashStr' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_delModule, arguments )

    @report_errors
    def do_hcp_getModules( self, s ):
        '''Get the list of modules available for tasking.'''

        parser = self.getParser( 'getModules' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_getModules, arguments )

    @report_errors
    def do_hcp_relocAgent( self, s ):
        '''Relocate an agent to a new org and network.'''

        parser = self.getParser( 'relocAgent' )
        parser.add_argument( '-a', '--agentid',
                             type = AgentId,
                             required = True,
                             help = 'agent id to relocate',
                             dest = 'agentid' )
        parser.add_argument( '-s', '--newsubnet',
                             type = hexArg,
                             required = True,
                             help = 'new subnet to give to agents matching this rule (hex)',
                             dest = 'newSubnet' )
        parser.add_argument( '-o', '--neworg',
                             type = hexArg,
                             required = True,
                             help = 'new org to give to agents matching this rule (hex)',
                             dest = 'newOrg' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_relocAgent, arguments )

    @report_errors
    def do_hcp_getRelocations( self, s ):
        '''Get the list of agent reolcations.'''

        parser = self.getParser( 'getRelocations' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_getRelocations, arguments )





    #===========================================================================
    #   HBS COMMANDS
    #===========================================================================
    def do_hbs_setPeriod( self, s ):
        '''Set the period agents beacon back.'''

        parser = self.getParser( 'setPeriod' )
        parser.add_argument( 'period',
                             type = int,
                             help = 'new period to schedule hcp beacons over' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hbs_setPeriod, arguments )

    @report_errors
    def do_hbs_getPeriod( self, s ):
        '''Get the current agent beacon period.'''

        parser = self.getParser( 'getPeriod' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hbs_getPeriod, arguments )

    @report_errors
    def do_hbs_addProfile( self, s ):
        '''Add an execution profile.'''

        parser = self.getParser( 'addProfile' )
        parser.add_argument( '-m', '--mask',
                             type = AgentId,
                             required = True,
                             help = 'agent id mask the profile applies to',
                             dest = 'mask' )
        parser.add_argument( '-f', '--configfile',
                             type = argparse.FileType( 'r' ),
                             required = True,
                             help = 'path to the file containing the config',
                             dest = 'config' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            arguments.config = arguments.config.read()
            self.execAndPrintResponse( self.be.hbs_addProfile, arguments )

    @report_errors
    def do_hbs_delProfile( self, s ):
        '''Remove an execution profile.'''

        parser = self.getParser( 'delProfile' )
        parser.add_argument( '-m', '--mask',
                             type = AgentId,
                             required = True,
                             help = 'agent id mask the profile applies to',
                             dest = 'mask' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hbs_delProfile, arguments )

    @report_errors
    def do_hbs_getProfiles( self, s ):
        '''Get the list of profiles.'''

        parser = self.getParser( 'getProfiles' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hbs_getProfiles, arguments )

    #===========================================================================
    #   HBS TASKINGS
    #===========================================================================
    def _executeHbsTasking( self, notifId, payload, arguments ):
        setattr( arguments, 'id', notifId )
        setattr( arguments, 'task', payload )

        # Multiplex to all agents matching
        getAgentsArg = argparse.Namespace()
        setattr( getAgentsArg, 'aid', arguments.toAgent )
        agents = self.execAndPrintResponse( self.be.hcp_getAgentStates, getAgentsArg )
        if agents is not None:
            if 'agents' in agents:
                for aid in agents[ 'agents' ].keys():
                    print( "Tasking agent %s" % aid )
                    arguments.toAgent = AgentId( aid )
                    self.execAndPrintResponse( self.be.hbs_taskAgent, arguments, True )
            else:
                print( "No matching agents found." )
        else:
            print( "Failed to get agent list from endpoint." )

    @report_errors
    def do_file_get( self, s ):
        '''Retrieve a file from the host.'''

        parser = self.getParser( 'file_get', True )
        parser.add_argument( 'file',
                             type = unicode,
                             help = 'file path to file to get' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.FILE_GET_REQ,
                                     rSequence().addStringW( self.tags.base.FILE_PATH, arguments.file ),
                                     arguments )

    @report_errors
    def do_file_info( self, s ):
        '''Retrieve information on a file from the host.'''

        parser = self.getParser( 'file_info', True )
        parser.add_argument( 'file',
                             type = unicode,
                             help = 'file path to file to get info on' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.FILE_INFO_REQ,
                                     rSequence().addStringW( self.tags.base.FILE_PATH, arguments.file ),
                                     arguments )

    @report_errors
    def do_dir_list( self, s ):
        '''Get the directory listing.'''

        parser = self.getParser( 'dir_list', True )
        parser.add_argument( 'rootDir',
                             type = unicode,
                             help = 'the root directory where to begin the listing from' )
        parser.add_argument( 'fileExp',
                             type = unicode,
                             help = 'a file name expression supporting basic wildcards like * and ?' )
        parser.add_argument( '-d', '--depth',
                             dest = 'depth',
                             required = False,
                             default = 0,
                             help = 'optional maximum depth of the listing, defaults to a single level' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.hbs.NOTIFICATION_DIR_LIST_REQ,
                                     rSequence().addStringW( self.tags.base.FILE_PATH, arguments.fileExp )
                                                .addStringW( self.tags.base.DIRECTORY_PATH, arguments.rootDir )
                                                .addInt32( self.tags.base.DIRECTORY_LIST_DEPTH, arguments.depth ),
                                     arguments )

    @report_errors
    def do_file_del( self, s ):
        '''Delete a file from the host.'''

        parser = self.getParser( 'file_del', True )
        parser.add_argument( 'file',
                             type = unicode,
                             help = 'file path to delete' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.FILE_DEL_REQ,
                                     rSequence().addStringW( self.tags.base.FILE_PATH, arguments.file ),
                                     arguments )

    @report_errors
    def do_file_mov( self, s ):
        '''Move a file on the host.'''

        parser = self.getParser( 'file_mov', True )
        parser.add_argument( 'srcFile',
                             type = unicode,
                             help = 'source file path' )
        parser.add_argument( 'dstFile',
                             type = unicode,
                             help = 'destination file path' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.FILE_MOV_REQ,
                                     rSequence().addStringW( self.tags.base.FILE_PATH, arguments.srcFile )
                                                .addStringW( self.tags.base.FILE_NAME, arguments.dstFile ),
                                     arguments )

    @report_errors
    def do_file_hash( self, s ):
        '''Hash a file from the host.'''

        parser = self.getParser( 'file_hash', True )
        parser.add_argument( 'file',
                             type = unicode,
                             help = 'file path to hash' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.FILE_HASH_REQ,
                                     rSequence().addStringW( self.tags.base.FILE_PATH, arguments.file ),
                                     arguments )

    @report_errors
    def do_mem_map( self, s ):
        '''Get the memory mapping of a specific process.'''

        parser = self.getParser( 'mem_map', True )
        parser.add_argument( 'pid',
                             type = int,
                             help = 'pid of the process to get the map from' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.MEM_MAP_REQ,
                                     rSequence().addInt32( self.tags.base.PROCESS_ID, arguments.pid ),
                                     arguments )

    @report_errors
    def do_mem_read( self, s ):
        '''Read the memory of a process at a specific address.'''

        parser = self.getParser( 'mem_read', True )
        parser.add_argument( 'pid',
                             type = int,
                             help = 'pid of the process to get the map from' )
        parser.add_argument( 'baseAddr',
                             type = hexArg,
                             help = 'base address to read from, in HEX FORMAT' )
        parser.add_argument( 'memSize',
                             type = hexArg,
                             help = 'number of bytes to read, in HEX FORMAT' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.MEM_READ_REQ,
                                     rSequence().addInt32( self.tags.base.PROCESS_ID, arguments.pid )
                                                .addInt64( self.tags.base.BASE_ADDRESS, arguments.baseAddr )
                                                .addInt32( self.tags.base.MEMORY_SIZE, arguments.memSize ),
                                     arguments )

    @report_errors
    def do_mem_handles( self, s ):
        '''Get the handles openned by a specific process.'''

        parser = self.getParser( 'mem_handles', True )
        parser.add_argument( 'pid',
                             type = int,
                             help = 'pid of the process to get the handles from, 0 for all processes' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.MEM_HANDLES_REQ,
                                     rSequence().addInt32( self.tags.base.PROCESS_ID, arguments.pid ),
                                     arguments )

    @report_errors
    def do_mem_strings( self, s ):
        '''Get the strings from a specific process.'''

        parser = self.getParser( 'mem_strings', True )
        parser.add_argument( 'pid',
                             type = int,
                             help = 'pid of the process to get the strings from' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.MEM_STRINGS_REQ,
                                     rSequence().addInt32( self.tags.base.PROCESS_ID, arguments.pid ),
                                     arguments )

    @report_errors
    def do_os_services( self, s ):
        '''Get the services registered on the host.'''

        parser = self.getParser( 'getServices', True )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.OS_SERVICES_REQ,
                                     rSequence(),
                                     arguments )

    @report_errors
    def do_os_drivers( self, s ):
        '''Get the drivers registered on the host.'''

        parser = self.getParser( 'os_drivers', True )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.OS_DRIVERS_REQ,
                                     rSequence(),
                                     arguments )

    @report_errors
    def do_os_kill_process( self, s ):
        '''Kill a process on the host.'''

        parser = self.getParser( 'os_kill_process', True )
        parser.add_argument( 'pid',
                             type = int,
                             help = 'pid of the process to kill' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.OS_KILL_PROCESS_REQ,
                                     rSequence().addInt32( self.tags.base.PROCESS_ID, arguments.pid ),
                                     arguments )

    @report_errors
    def do_os_processes( self, s ):
        '''Generate a new process snapshot.'''

        parser = self.getParser( 'os_processes', True )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.OS_PROCESSES_REQ,
                                     rSequence(),
                                     arguments )

    @report_errors
    def do_os_autoruns( self, s ):
        '''Generate a new autoruns snapshot.'''

        parser = self.getParser( 'os_autoruns', True )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.OS_AUTORUNS_REQ,
                                     rSequence(),
                                     arguments )

    @report_errors
    def do_mem_find_string( self, s ):
        '''Find the specific strings in a specific process.'''

        parser = self.getParser( 'mem_find_string', True )
        parser.add_argument( 'pid',
                             type = int,
                             help = 'pid of the process to search in' )
        parser.add_argument( '-s', '--strings',
                             type = unicode,
                             required = True,
                             nargs = '*',
                             dest = 'strings',
                             help = 'list of strings to look for' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            seq = rSequence().addInt32( self.tags.base.PROCESS_ID, arguments.pid )
            l = rList()
            for s in arguments.strings:
                l.addStringW( self.tags.base.STRING, s )
            seq.addList( self.tags.base.STRINGSW, l )
            self._executeHbsTasking( self.tags.notification.MEM_FIND_STRING_REQ,
                                     seq,
                                     arguments )

    @report_errors
    def do_mem_find_handle( self, s ):
        '''Find the handles in any process that contain a specific substring.'''

        parser = self.getParser( 'mem_find_handle', True )
        parser.add_argument( 'needle',
                             type = unicode,
                             help = 'substring of the handle names to get' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.MEM_FIND_HANDLE_REQ,
                                     rSequence().addStringW( self.tags.base.HANDLE_NAME, arguments.needle ),
                                     arguments )

    @report_errors
    def do_run_script( self, s ):
        '''Runs a list of commands from a file but with additional context passed in the command line.'''

        parser = self.getParser( 'run_script' )
        parser.add_argument( '-f', '--configfile',
                             type = argparse.FileType( 'r' ),
                             required = True,
                             help = 'path to the file containing the script to run',
                             dest = 'script' )
        parser.add_argument( '-n', '--nocontext',
                             dest = 'nocontext',
                             action = 'store_true',
                             default = False,
                             required = False,
                             help = 'if present will NOT overwrite the context in the script with the one passed in the run_script command line' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            arguments.script = [ x for x in arguments.script.read().split( '\n' ) if ( x.strip() != '' and not x.startswith( '#' ) ) ]

            print( "Executing script containing %d commands." % ( len( arguments.script ), ) )

            if arguments.nocontext:
                curContext = ''
            else:
                curContext = ' -! %s -@ %s -x %s ' % ( arguments.toAgent, arguments.investigationId, arguments.expiry )

            self.cmdqueue.extend( [ x + curContext for x in arguments.script ] )

    @report_errors
    def do_history_dump( self, s ):
        '''Dump the full recent history of events on the sensor.'''

        parser = self.getParser( 'history_dump', True )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.HISTORY_DUMP_REQ,
                                     rSequence(),
                                     arguments )
Ejemplo n.º 2
0
    def do_login( self, s ):
        '''Login to the BE using credentials stored in a config file.'''

        parser = self.getParser( 'login' )
        parser.add_argument( 'configFile',
                             type = argparse.FileType( 'r' ),
                             help = 'config file specifying the endpoint and token to use' )
        parser.add_argument( '-k', '--key',
                             required = False,
                             default = None,
                             #type = argparse.FileType( 'r' ),
                             type = str,
                             help = 'key to use to sign hbs tasks',
                             dest = 'key' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            try:
                config = json.loads( arguments.configFile.read() )
            except:
                print( "Invalid config file format (JSON): %s" % traceback.format_exc() )
                return

            if 'beach_config' not in config or 'token' not in config:
                print( "Missing endpoint or token in config." )
                return

            _ = os.getcwd()
            os.chdir( os.path.dirname(  __file__ ) )
            self.be = BEAdmin( config[ 'beach_config' ], config[ 'token' ] )
            os.chdir( _ )

            remoteTime = self.be.testConnection()

            if remoteTime.isTimedOut:
                print( "Endpoint did not respond." )
                return

            if 'pong' not in remoteTime.data:
                print( "Endpoint responded with invalid data." )
                return

            if arguments.key is not None:
                if os.path.isfile( arguments.key ):
                    try:
                        password = getpass.getpass()
                        print( "...decrypting key..." )
                        # There are weird problems with pexpect and newlines and binary, so
                        # we have to brute force it a bit
                        for i in range( 0, 30 ):
                            proc = pexpect.spawn( 'openssl aes-256-cbc -d -in %s' % arguments.key )
                            proc.expect( [ 'enter aes-256-cbc decryption password: *' ] )
                            proc.sendline( password )
                            proc.expect( "\r\n" )
                            proc.expect( ".*" )
                            self.hbsKey = proc.match.group( 0 ).replace( "\r\n", "\n" )
                            try:
                                testSign = Signing( self.hbsKey )
                                testSig = testSign.sign( 'a' )
                                if testSig is not None:
                                    break
                            except:
                                self.hbsKey = None

                        if self.hbsKey is not None:
                            print( "success, authenticated!" )
                        else:
                            print( "error loading key, bad key format or password?" )
                    except:
                        self.hbsKey = None
                        print( "error getting cloud key: %s" % traceback.format_exc() )
                    if self.hbsKey is not None and 'bad decrypt' in self.hbsKey:
                        print( "Invalid password" )
                        self.hbsKey = None
                else:
                    print( "Invalid key file: %s." % arguments.key )
                    self.hbsKey = None
            else:
                self.hbsKey = None

            remoteTime = remoteTime.data.get( 'pong', 0 )
            print( "Successfully logged in." )
            print( "Remote endpoint time: %s." % remoteTime )

            self.user = config[ 'token' ].split( '/' )[ 0 ]

            self.updatePrompt()
Ejemplo n.º 3
0
class HcpCli ( cmd.Cmd ):

    #===========================================================================
    #   HOUSEKEEPING
    #===========================================================================
    prompt = '<NEED_LOGIN> %> '

    def __init__( self, beachConfig = None, token = None, hbsKey = None, logFile = None ):
        self.logFile = logFile
        if self.logFile is not None:
            self.logFile = open( self.logFile, 'w', 0 )

        cmd.Cmd.__init__( self, stdout = ( self.logFile if self.logFile is not None else sys.stdout ) )
        self.be = None
        self.user = None
        self.hbsKey = None
        self.aid = None
        self.investigationId = None
        self.tags = Symbols()
        readline.set_completer_delims(":;'\"? \t")

        if beachConfig is not None:
            self.connectWithConfig( beachConfig, token )

        if hbsKey is not None:
            self.loadKey( hbsKey )

    def outputString( self, s ):
        s = str( s )
        if self.logFile is None:
            print( s )
        else:
            self.logFile.write( s )
            self.logFile.write( "\n" )
            self.logFile.flush()

    def connectWithConfig( self, beachConfig, token ):
        self.be = BEAdmin( beachConfig, token )
        self.outputString( "Interface to cloud set." )

    def loadKey( self, hbsKey ):
        self.hbsKey = hbsKey
        self.outputString( "HBS key set." )

    def updatePrompt( self ):
        self.prompt = '%s%s / %s %s%%> ' % ( ( '' if self.hbsKey is None else '* ' ),
                                           ( '' if self.user is None else self.user ),
                                           ( '' if self.aid is None else self.aid ),
                                           ( '' if ( self.investigationId is None or self.investigationId == '' ) else ' : %s ' % self.investigationId ) )

    def getParser( self, desc, isHbsTask = False ):
        parser = argparse.ArgumentParser( prog = desc )

        if isHbsTask:
            parser.add_argument( '-!',
                                  type = AgentId,
                                  required = False,
                                  default = AgentId( self.aid ),
                                  help = 'agent id to change context to ONLY for the duration of this command.',
                                  dest = 'toAgent' )

            parser.add_argument( '-x',
                                  type = int,
                                  required = False,
                                  default = ( 60 * 60 * 1 ),
                                  help = 'set this command\'s specific expiry time in seconds.',
                                  dest = 'expiry' )

            parser.add_argument( '-@',
                                  type = str,
                                  required = False,
                                  default = self.investigationId,
                                  help = 'the investigation id to attach to the command, results and side-effects.',
                                  dest = 'investigationId' )

        return parser

    def parse( self, parser, line ):
        try:
            return parser.parse_args( shlex.split( line ) )
        except SystemExit:
            return None

    def do_exit( self, s ):
        return True

    def do_quit( self, s ):
        return True

    def emptyline( self ):
        pass

    def completedefault( self, text, line, begidx, endidx ):
        def get_possible_filename_completions(text):
            head, tail = os.path.split(text.strip())
            if head == "": #no head
                head = "."
            files = os.listdir(head)
            return [ os.path.join( head, f ) for f in files if f.startswith(tail) ]

        if begidx != 0:
            return get_possible_filename_completions( text )
        else:
            return [ x[ 3 : ] for x in dir( self ) if x.startswith( 'do_' ) ]

    def execAndPrintResponse( self, command, arguments, isHbsTask = False ):
        if isHbsTask:
            tmp = arguments
            arguments = argparse.Namespace()

            if not tmp.toAgent.isValid or self.hbsKey is None:
                self.outputString( 'Agent id and hbs key must be set in context.' )
                return

            if not hasattr( tmp, 'key' ) or tmp.key is None:
                setattr( tmp, 'key', self.hbsKey )

            if ( tmp.investigationId is not None ) and '' != tmp.investigationId:
                setattr( arguments, 'investigationId', tmp.investigationId )

            setattr( arguments, 'toAgent', tmp.toAgent )
            setattr( arguments, 'task', tmp.task )
            setattr( arguments, 'key', tmp.key )
            setattr( arguments, 'id', tmp.id )
            setattr( arguments, 'expiry', int( tmp.expiry + time.time() ) )

            del( tmp )

        for k, a in vars( arguments ).iteritems():
            if type( a ) is AgentId:
                if not a.isValid:
                    self.outputString( 'Invalid agent id: %s.' % str(a) )
                    return
                else:
                    setattr( arguments, k, str( a ) )

        results = command( **vars( arguments ) )

        if results.isSuccess:
            self.outputString( "<<<SUCCESS>>>" )
        elif results.isTimedOut:
            self.outputString( "<<<TIMEOUT>>>" )
        else:
            if 0 == len( results.error ):
                self.outputString( "<<<FAILURE>>>" )
            else:
                self.outputString( "<<<FAILURE: %s>>>" % results.error )
        pprint.pprint( results.data, indent = 2, width = 80, stream = self.logFile )

        return results.data

    def getTags( self, tagsPath ):
        raw_tags = json.loads( open( tagsPath, 'r' ).read() )
        tags = Symbols()
        for group in raw_tags[ 'groups' ]:
            g = Symbols()
            for definition in group[ 'definitions' ]:
                setattr( g, definition[ 'name' ], str( definition[ 'value' ] ) )
            setattr( tags, group[ 'groupName' ], g )

        return tags

    #===========================================================================
    #   SESSION SETTING COMMANDS
    #===========================================================================
    @report_errors
    def do_login( self, s ):
        '''Login to the BE using credentials stored in a config file.'''

        parser = self.getParser( 'login' )
        parser.add_argument( 'configFile',
                             type = argparse.FileType( 'r' ),
                             help = 'config file specifying the endpoint and token to use' )
        parser.add_argument( '-k', '--key',
                             required = False,
                             default = None,
                             #type = argparse.FileType( 'r' ),
                             type = str,
                             help = 'key to use to sign hbs tasks',
                             dest = 'key' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            try:
                config = json.loads( arguments.configFile.read() )
            except:
                self.outputString( "Invalid config file format (JSON): %s" % traceback.format_exc() )
                return

            if 'beach_config' not in config or 'token' not in config:
                self.outputString( "Missing endpoint or token in config." )
                return

            _ = os.getcwd()
            os.chdir( os.path.dirname(  __file__ ) )
            self.connectWithConfig( config[ 'beach_config' ], config[ 'token' ] )
            os.chdir( _ )

            remoteTime = self.be.testConnection()

            if remoteTime.isTimedOut:
                self.outputString( "Endpoint did not respond." )
                return

            if 'pong' not in remoteTime.data:
                self.outputString( "Endpoint responded with invalid data." )
                return

            if arguments.key is not None:
                if os.path.isfile( arguments.key ):
                    try:
                        password = getpass.getpass()
                        self.outputString( "...decrypting key..." )
                        # There are weird problems with pexpect and newlines and binary, so
                        # we have to brute force it a bit
                        for i in range( 0, 30 ):
                            proc = pexpect.spawn( 'openssl aes-256-cbc -d -in %s' % arguments.key )
                            proc.expect( [ 'enter aes-256-cbc decryption password: *' ] )
                            proc.sendline( password )
                            proc.expect( "\r\n" )
                            proc.expect( ".*" )
                            self.loadKey( proc.match.group( 0 ).replace( "\r\n", "\n" ) )
                            try:
                                testSign = Signing( self.hbsKey )
                                testSig = testSign.sign( 'a' )
                                if testSig is not None:
                                    break
                            except:
                                self.hbsKey = None

                        if self.hbsKey is not None:
                            self.outputString( "success, authenticated!" )
                        else:
                            self.outputString( "error loading key, bad key format or password?" )
                    except:
                        self.hbsKey = None
                        self.outputString( "error getting cloud key: %s" % traceback.format_exc() )
                    if self.hbsKey is not None and 'bad decrypt' in self.hbsKey:
                        self.outputString( "Invalid password" )
                        self.hbsKey = None
                else:
                    self.outputString( "Invalid key file: %s." % arguments.key )
                    self.hbsKey = None
            else:
                self.hbsKey = None

            remoteTime = remoteTime.data.get( 'pong', 0 )
            self.outputString( "Successfully logged in." )
            self.outputString( "Remote endpoint time: %s." % remoteTime )

            self.user = config[ 'token' ].split( '/' )[ 0 ]

            self.updatePrompt()

    @report_errors
    def do_chid( self, s ):
        '''Change execution context to a specific agent id, used only for HBS tasking.'''

        parser = self.getParser( 'chid' )
        parser.add_argument( 'aid',
                             type = AgentId,
                             help = 'agent id to change context to' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            aid = arguments.aid
            if aid.isValid:
                self.aid = aid
                self.updatePrompt()
            else:
                self.outputString( 'Agent Id is not valid.' )

    @report_errors
    def do_setInvestigationId( self, s ):
        '''Change execution context to relate to a specific investigationId, used only for HBS tasking.'''

        parser = self.getParser( 'setInvestigationId' )
        parser.add_argument( 'investigationId',
                             type = str,
                             help = 'investigation id to change context to' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.investigationId = arguments.investigationId
            self.updatePrompt()



    #===========================================================================
    #   HCP COMMANDS
    #===========================================================================
    @report_errors
    def do_hcp_getAgentStates( self, s ):
        '''Get the general state of agents.'''

        parser = self.getParser( 'getAgentStates' )
        parser.add_argument( '-a', '--aid',
                              type = AgentId,
                              required = False,
                              help = 'agent id to retrieve the info of',
                              dest = 'aid' )
        parser.add_argument( '-n', '--hostname',
                              type = str,
                              required = False,
                              help = 'hostname of the agent to retrieve the info of',
                              dest = 'hostname' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_getAgentStates, arguments )

    @report_errors
    def do_hcp_setPeriod( self, s ):
        '''Set the period agents beacon back.'''

        parser = self.getParser( 'setPeriod' )
        parser.add_argument( 'period',
                             type = int,
                             help = 'new period to schedule hcp beacons over' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_setPeriod, arguments )

    @report_errors
    def do_hcp_getPeriod( self, s ):
        '''Get the current agent beacon period.'''

        parser = self.getParser( 'getPeriod' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_getPeriod, arguments )

    @report_errors
    def do_hcp_addEnrollmentRule( self, s ):
        '''Add a new enrollment rule for new agents.'''

        parser = self.getParser( 'addEnrollmentRule' )
        parser.add_argument( '-m', '--mask',
                             type = AgentId,
                             required = True,
                             help = 'agent id mask this rule applies to',
                             dest = 'mask' )
        parser.add_argument( '-i', '--internalip',
                             type = str,
                             required = False,
                             default = '255.255.255.255',
                             help = 'internal ip mask the rule applies to (255 wildcard)',
                             dest = 'internalIp' )
        parser.add_argument( '-e', '--externalip',
                             type = str,
                             required = False,
                             default = '255.255.255.255',
                             help = 'external ip mask the rule applies to (255 wildcard)',
                             dest = 'externalIp' )
        parser.add_argument( '-s', '--newsubnet',
                             type = hexArg,
                             required = True,
                             help = 'new subnet to give to agents matching this rule (hex)',
                             dest = 'newSubnet' )
        parser.add_argument( '-o', '--neworg',
                             type = hexArg,
                             required = True,
                             help = 'new org to give to agents matching this rule (hex)',
                             dest = 'newOrg' )
        parser.add_argument( '-n', '--hostname',
                             type = str,
                             required = False,
                             default = '',
                             help = 'hostname of the host',
                             dest = 'hostname' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_addEnrollmentRule, arguments )

    @report_errors
    def do_hcp_delEnrollmentRule( self, s ):
        '''Remove an enrollment rule for new agents.'''

        parser = self.getParser( 'addEnrollmentRule' )
        parser.add_argument( '-m', '--mask',
                             type = AgentId,
                             required = True,
                             help = 'agent id mask this rule applies to',
                             dest = 'mask' )
        parser.add_argument( '-i', '--internalip',
                             type = str,
                             required = False,
                             default = '255.255.255.255',
                             help = 'internal ip mask the rule applies to (255 wildcard)',
                             dest = 'internalIp' )
        parser.add_argument( '-e', '--externalip',
                             type = str,
                             required = False,
                             default = '255.255.255.255',
                             help = 'external ip mask the rule applies to (255 wildcard)',
                             dest = 'externalIp' )
        parser.add_argument( '-s', '--newsubnet',
                             type = hexArg,
                             required = True,
                             help = 'new subnet to give to agents matching this rule (hex)',
                             dest = 'newSubnet' )
        parser.add_argument( '-o', '--neworg',
                             type = hexArg,
                             required = True,
                             help = 'new org to give to agents matching this rule (hex)',
                             dest = 'newOrg' )
        parser.add_argument( '-n', '--hostname',
                             type = str,
                             required = False,
                             default = '',
                             help = 'hostname of the host',
                             dest = 'hostname' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_delEnrollmentRule, arguments )

    @report_errors
    def do_hcp_getEnrollmentRules( self, s ):
        '''Get the list of enrollment rules for new agents.'''

        parser = self.getParser( 'getPeriod' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_getEnrollmentRules, arguments )

    @report_errors
    def do_hcp_addTasking( self, s ):
        '''Task a module to a list of agents.'''

        parser = self.getParser( 'addTasking' )
        parser.add_argument( '-m', '--mask',
                             type = AgentId,
                             required = True,
                             help = 'agent id mask of the rule',
                             dest = 'mask' )
        parser.add_argument( '-i', '--moduleid',
                             type = int,
                             required = True,
                             help = 'module id to task',
                             dest = 'moduleId' )
        parser.add_argument( '-s', '--hash',
                             type = str,
                             required = True,
                             help = 'hash of the module to task',
                             dest = 'hashStr' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_addTasking, arguments )

    @report_errors
    def do_hcp_delTasking( self, s ):
        '''Remove a module from tasking to agents.'''

        parser = self.getParser( 'delTasking' )
        parser.add_argument( '-m', '--mask',
                             type = AgentId,
                             required = True,
                             help = 'agent id mask of the rule',
                             dest = 'mask' )
        parser.add_argument( '-i', '--moduleid',
                             type = int,
                             required = True,
                             help = 'module id tasked',
                             dest = 'moduleId' )
        parser.add_argument( '-s', '--hash',
                             type = str,
                             required = True,
                             help = 'hash of the module to untask',
                             dest = 'hashStr' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_delTasking, arguments )

    @report_errors
    def do_hcp_getTaskings( self, s ):
        '''Get the list of modules tasked to agents.'''

        parser = self.getParser( 'getTaskings' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_getTaskings, arguments )

    @report_errors
    def do_hcp_addModule( self, s ):
        '''Add a taskable module.'''

        parser = self.getParser( 'addModule' )
        parser.add_argument( '-i', '--moduleid',
                             type = int,
                             required = True,
                             help = 'module id',
                             dest = 'moduleId' )
        parser.add_argument( '-b', '--binary',
                             type = argparse.FileType( 'r' ),
                             required = True,
                             help = 'path to file containing module',
                             dest = 'binary' )
        parser.add_argument( '-s', '--signature',
                             type = argparse.FileType( 'r' ),
                             required = True,
                             help = 'path to file containing signature of the module',
                             dest = 'signature' )
        parser.add_argument( '-d', '--description',
                             type = str,
                             required = True,
                             help = 'description of the module',
                             dest = 'description' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            arguments.binary = arguments.binary.read()
            arguments.signature = arguments.signature.read()
            self.execAndPrintResponse( self.be.hcp_addModule, arguments )

    @report_errors
    def do_hcp_delModule( self, s ):
        '''Remove a taskable module.'''

        parser = self.getParser( 'delTasking' )
        parser.add_argument( '-i', '--moduleid',
                             type = int,
                             required = True,
                             help = 'module id',
                             dest = 'moduleId' )
        parser.add_argument( '-s', '--hash',
                             type = str,
                             required = True,
                             help = 'hash of the module',
                             dest = 'hashStr' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_delModule, arguments )

    @report_errors
    def do_hcp_getModules( self, s ):
        '''Get the list of modules available for tasking.'''

        parser = self.getParser( 'getModules' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_getModules, arguments )

    @report_errors
    def do_hcp_relocAgent( self, s ):
        '''Relocate an agent to a new org and network.'''

        parser = self.getParser( 'relocAgent' )
        parser.add_argument( '-a', '--agentid',
                             type = AgentId,
                             required = True,
                             help = 'agent id to relocate',
                             dest = 'agentid' )
        parser.add_argument( '-s', '--newsubnet',
                             type = hexArg,
                             required = True,
                             help = 'new subnet to give to agents matching this rule (hex)',
                             dest = 'newSubnet' )
        parser.add_argument( '-o', '--neworg',
                             type = hexArg,
                             required = True,
                             help = 'new org to give to agents matching this rule (hex)',
                             dest = 'newOrg' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_relocAgent, arguments )

    @report_errors
    def do_hcp_getRelocations( self, s ):
        '''Get the list of agent reolcations.'''

        parser = self.getParser( 'getRelocations' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hcp_getRelocations, arguments )





    #===========================================================================
    #   HBS COMMANDS
    #===========================================================================
    def do_hbs_setPeriod( self, s ):
        '''Set the period agents beacon back.'''

        parser = self.getParser( 'setPeriod' )
        parser.add_argument( 'period',
                             type = int,
                             help = 'new period to schedule hcp beacons over' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hbs_setPeriod, arguments )

    @report_errors
    def do_hbs_getPeriod( self, s ):
        '''Get the current agent beacon period.'''

        parser = self.getParser( 'getPeriod' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hbs_getPeriod, arguments )

    @report_errors
    def do_hbs_addProfile( self, s ):
        '''Add an execution profile.'''

        parser = self.getParser( 'addProfile' )
        parser.add_argument( '-m', '--mask',
                             type = AgentId,
                             required = True,
                             help = 'agent id mask the profile applies to',
                             dest = 'mask' )
        parser.add_argument( '-f', '--configfile',
                             type = argparse.FileType( 'r' ),
                             required = True,
                             help = 'path to the file containing the config',
                             dest = 'config' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            arguments.config = arguments.config.read()
            self.execAndPrintResponse( self.be.hbs_addProfile, arguments )

    @report_errors
    def do_hbs_delProfile( self, s ):
        '''Remove an execution profile.'''

        parser = self.getParser( 'delProfile' )
        parser.add_argument( '-m', '--mask',
                             type = AgentId,
                             required = True,
                             help = 'agent id mask the profile applies to',
                             dest = 'mask' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hbs_delProfile, arguments )

    @report_errors
    def do_hbs_getProfiles( self, s ):
        '''Get the list of profiles.'''

        parser = self.getParser( 'getProfiles' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            self.execAndPrintResponse( self.be.hbs_getProfiles, arguments )

    #===========================================================================
    #   HBS TASKINGS
    #===========================================================================
    def _executeHbsTasking( self, notifId, payload, arguments ):
        setattr( arguments, 'id', notifId )
        setattr( arguments, 'task', payload )

        # Multiplex to all agents matching
        getAgentsArg = argparse.Namespace()
        setattr( getAgentsArg, 'aid', arguments.toAgent )
        agents = self.execAndPrintResponse( self.be.hcp_getAgentStates, getAgentsArg )
        if agents is not None:
            if 'agents' in agents:
                for aid in agents[ 'agents' ].keys():
                    self.outputString( "Tasking agent %s: %s" % ( aid, str( arguments ) ) )
                    arguments.toAgent = AgentId( aid )
                    self.execAndPrintResponse( self.be.hbs_taskAgent, arguments, True )
            else:
                self.outputString( "No matching agents found." )
        else:
            self.outputString( "Failed to get agent list from endpoint." )

    @report_errors
    def do_file_get( self, s ):
        '''Retrieve a file from the host.'''

        parser = self.getParser( 'file_get', True )
        parser.add_argument( 'file',
                             type = unicode,
                             help = 'file path to file to get' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.FILE_GET_REQ,
                                     rSequence().addStringW( self.tags.base.FILE_PATH, arguments.file ),
                                     arguments )

    @report_errors
    def do_file_info( self, s ):
        '''Retrieve information on a file from the host.'''

        parser = self.getParser( 'file_info', True )
        parser.add_argument( 'file',
                             type = unicode,
                             help = 'file path to file to get info on' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.FILE_INFO_REQ,
                                     rSequence().addStringW( self.tags.base.FILE_PATH, arguments.file ),
                                     arguments )

    @report_errors
    def do_dir_list( self, s ):
        '''Get the directory listing.'''

        parser = self.getParser( 'dir_list', True )
        parser.add_argument( 'rootDir',
                             type = unicode,
                             help = 'the root directory where to begin the listing from' )
        parser.add_argument( 'fileExp',
                             type = unicode,
                             help = 'a file name expression supporting basic wildcards like * and ?' )
        parser.add_argument( '-d', '--depth',
                             dest = 'depth',
                             required = False,
                             default = 0,
                             help = 'optional maximum depth of the listing, defaults to a single level' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.hbs.NOTIFICATION_DIR_LIST_REQ,
                                     rSequence().addStringW( self.tags.base.FILE_PATH, arguments.fileExp )
                                                .addStringW( self.tags.base.DIRECTORY_PATH, arguments.rootDir )
                                                .addInt32( self.tags.base.DIRECTORY_LIST_DEPTH, arguments.depth ),
                                     arguments )

    @report_errors
    def do_file_del( self, s ):
        '''Delete a file from the host.'''

        parser = self.getParser( 'file_del', True )
        parser.add_argument( 'file',
                             type = unicode,
                             help = 'file path to delete' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.FILE_DEL_REQ,
                                     rSequence().addStringW( self.tags.base.FILE_PATH, arguments.file ),
                                     arguments )

    @report_errors
    def do_file_mov( self, s ):
        '''Move a file on the host.'''

        parser = self.getParser( 'file_mov', True )
        parser.add_argument( 'srcFile',
                             type = unicode,
                             help = 'source file path' )
        parser.add_argument( 'dstFile',
                             type = unicode,
                             help = 'destination file path' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.FILE_MOV_REQ,
                                     rSequence().addStringW( self.tags.base.FILE_PATH, arguments.srcFile )
                                                .addStringW( self.tags.base.FILE_NAME, arguments.dstFile ),
                                     arguments )

    @report_errors
    def do_file_hash( self, s ):
        '''Hash a file from the host.'''

        parser = self.getParser( 'file_hash', True )
        parser.add_argument( 'file',
                             type = unicode,
                             help = 'file path to hash' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.FILE_HASH_REQ,
                                     rSequence().addStringW( self.tags.base.FILE_PATH, arguments.file ),
                                     arguments )

    @report_errors
    def do_mem_map( self, s ):
        '''Get the memory mapping of a specific process.'''

        parser = self.getParser( 'mem_map', True )
        parser.add_argument( 'pid',
                             type = int,
                             help = 'pid of the process to get the map from' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.MEM_MAP_REQ,
                                     rSequence().addInt32( self.tags.base.PROCESS_ID, arguments.pid ),
                                     arguments )

    @report_errors
    def do_mem_read( self, s ):
        '''Read the memory of a process at a specific address.'''

        parser = self.getParser( 'mem_read', True )
        parser.add_argument( 'pid',
                             type = int,
                             help = 'pid of the process to get the map from' )
        parser.add_argument( 'baseAddr',
                             type = hexArg,
                             help = 'base address to read from, in HEX FORMAT' )
        parser.add_argument( 'memSize',
                             type = hexArg,
                             help = 'number of bytes to read, in HEX FORMAT' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.MEM_READ_REQ,
                                     rSequence().addInt32( self.tags.base.PROCESS_ID, arguments.pid )
                                                .addInt64( self.tags.base.BASE_ADDRESS, arguments.baseAddr )
                                                .addInt32( self.tags.base.MEMORY_SIZE, arguments.memSize ),
                                     arguments )

    @report_errors
    def do_mem_handles( self, s ):
        '''Get the handles openned by a specific process.'''

        parser = self.getParser( 'mem_handles', True )
        parser.add_argument( 'pid',
                             type = int,
                             help = 'pid of the process to get the handles from, 0 for all processes' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.MEM_HANDLES_REQ,
                                     rSequence().addInt32( self.tags.base.PROCESS_ID, arguments.pid ),
                                     arguments )

    @report_errors
    def do_mem_strings( self, s ):
        '''Get the strings from a specific process.'''

        parser = self.getParser( 'mem_strings', True )
        parser.add_argument( 'pid',
                             type = int,
                             help = 'pid of the process to get the strings from' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.MEM_STRINGS_REQ,
                                     rSequence().addInt32( self.tags.base.PROCESS_ID, arguments.pid ),
                                     arguments )

    @report_errors
    def do_os_services( self, s ):
        '''Get the services registered on the host.'''

        parser = self.getParser( 'getServices', True )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.OS_SERVICES_REQ,
                                     rSequence(),
                                     arguments )

    @report_errors
    def do_os_drivers( self, s ):
        '''Get the drivers registered on the host.'''

        parser = self.getParser( 'os_drivers', True )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.OS_DRIVERS_REQ,
                                     rSequence(),
                                     arguments )

    @report_errors
    def do_os_kill_process( self, s ):
        '''Kill a process on the host.'''

        parser = self.getParser( 'os_kill_process', True )
        parser.add_argument( 'pid',
                             type = int,
                             help = 'pid of the process to kill' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.OS_KILL_PROCESS_REQ,
                                     rSequence().addInt32( self.tags.base.PROCESS_ID, arguments.pid ),
                                     arguments )

    @report_errors
    def do_os_processes( self, s ):
        '''Generate a new process snapshot.'''

        parser = self.getParser( 'os_processes', True )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.OS_PROCESSES_REQ,
                                     rSequence(),
                                     arguments )

    @report_errors
    def do_os_autoruns( self, s ):
        '''Generate a new autoruns snapshot.'''

        parser = self.getParser( 'os_autoruns', True )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.OS_AUTORUNS_REQ,
                                     rSequence(),
                                     arguments )

    @report_errors
    def do_mem_find_string( self, s ):
        '''Find the specific strings in a specific process.'''

        parser = self.getParser( 'mem_find_string', True )
        parser.add_argument( 'pid',
                             type = int,
                             help = 'pid of the process to search in' )
        parser.add_argument( '-s', '--strings',
                             type = unicode,
                             required = True,
                             nargs = '*',
                             dest = 'strings',
                             help = 'list of strings to look for' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            seq = rSequence().addInt32( self.tags.base.PROCESS_ID, arguments.pid )
            l = rList()
            for s in arguments.strings:
                l.addStringW( self.tags.base.STRING, s )
            seq.addList( self.tags.base.STRINGSW, l )
            self._executeHbsTasking( self.tags.notification.MEM_FIND_STRING_REQ,
                                     seq,
                                     arguments )

    @report_errors
    def do_mem_find_handle( self, s ):
        '''Find the handles in any process that contain a specific substring.'''

        parser = self.getParser( 'mem_find_handle', True )
        parser.add_argument( 'needle',
                             type = unicode,
                             help = 'substring of the handle names to get' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.MEM_FIND_HANDLE_REQ,
                                     rSequence().addStringW( self.tags.base.HANDLE_NAME, arguments.needle ),
                                     arguments )

    @report_errors
    def do_hidden_module_scan( self, s ):
        '''Scan one or more processes for hidden modules.'''

        parser = self.getParser( 'hidden_module_scan', True )
        parser.add_argument( 'pid',
                             type = int,
                             help = 'pid of the process to scan, or "-1" for ALL processes' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.HIDDEN_MODULE_REQ,
                                     rSequence().addInt32( self.tags.base.PROCESS_ID, arguments.pid ),
                                     arguments )

    @report_errors
    def do_exec_oob_scan( self, s ):
        '''Scan one or more processes for out of bounds execution (thread out of known modules).'''

        parser = self.getParser( 'exec_oob_scan', True )
        parser.add_argument( 'pid',
                             type = int,
                             help = 'pid of the process to scan, or "-1" for ALL processes' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.EXEC_OOB_REQ,
                                     rSequence().addInt32( self.tags.base.PROCESS_ID, arguments.pid ),
                                     arguments )

    @report_errors
    def do_remain_live( self, s ):
        '''Request the sensor remain in constant contact for the next X seconds.'''

        parser = self.getParser( 'remain_live', True )
        parser.add_argument( 'seconds',
                             type = int,
                             help = 'number of seconds from now to remain live' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.REMAIN_LIVE_REQ,
                                     rSequence().addTimestamp( self.tags.base.EXPIRY,
                                                               int( time.time() + arguments.seconds ) ),
                                     arguments )

    @report_errors
    def do_exfil_add( self, s ):
        '''Tell the sensor to start exfiling specific event.'''

        parser = self.getParser( 'exfil_add', True )
        parser.add_argument( 'event',
                             type = eventArg,
                             help = 'name of event to start exfiling' )
        parser.add_argument( '-e', '--expire',
                             type = int,
                             required = False,
                             dest = 'expire',
                             help = 'number of seconds before stopping exfil of event' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            data = ( rSequence().addInt32( self.tags.hbs.NOTIFICATION_ID,
                                           arguments.event )
                                .addTimestamp( self.tags.base.EXPIRY,
                                               int( time.time() + arguments.expire ) ) )
            self._executeHbsTasking( self.tags.notification.ADD_EXFIL_EVENT_REQ,
                                     data,
                                     arguments )

    @report_errors
    def do_exfil_del( self, s ):
        '''Tell the sensor to stop exfiling specific event.'''

        parser = self.getParser( 'exfil_del', True )
        parser.add_argument( 'event',
                             type = eventArg,
                             help = 'name of event to stop exfiling' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.DEL_EXFIL_EVENT_REQ,
                                     rSequence().addInt32( self.tags.hbs.NOTIFICATION_ID,
                                                               arguments.event ),
                                     arguments )

    @report_errors
    def do_exfil_get( self, s ):
        '''Show which custom events are exfiled by sensor (other than through the global profile).'''

        parser = self.getParser( 'exfil_get', True )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.GET_EXFIL_EVENT_REQ,
                                     rSequence(),
                                     arguments )

    @report_errors
    def do_critical_add( self, s ):
        '''Tell the sensor to add an event to the list of critical events to beacon home.'''

        parser = self.getParser( 'critical_add', True )
        parser.add_argument( 'event',
                             type = eventArg,
                             help = 'name of event to start treating as critical' )
        parser.add_argument( '-e', '--expire',
                             type = int,
                             required = False,
                             dest = 'expire',
                             help = 'number of seconds before removing event from critical' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            data = ( rSequence().addInt32( self.tags.hbs.NOTIFICATION_ID,
                                           arguments.event )
                                .addTimestamp( self.tags.base.EXPIRY,
                                               int( time.time() + arguments.expire ) ) )
            self._executeHbsTasking( self.tags.notification.ADD_CRITICAL_EVENT_REQ,
                                     data,
                                     arguments )

    @report_errors
    def do_critical_del( self, s ):
        '''Tell the sensor to remove an event from the list of critical events.'''

        parser = self.getParser( 'critical_del', True )
        parser.add_argument( 'event',
                             type = eventArg,
                             help = 'name of event to stop treating as critical' )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.DEL_CRITICAL_EVENT_REQ,
                                     rSequence().addInt32( self.tags.hbs.NOTIFICATION_ID,
                                                               arguments.event ),
                                     arguments )

    @report_errors
    def do_critical_get( self, s ):
        '''Show which custom events are critical (other than through the global profile).'''

        parser = self.getParser( 'critical_get', True )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.GET_CRITICAL_EVENT_REQ,
                                     rSequence(),
                                     arguments )


    @report_errors
    def do_run_script( self, s ):
        '''Runs a list of commands from a file but with additional context passed in the command line.'''

        parser = self.getParser( 'run_script' )
        parser.add_argument( '-f', '--configfile',
                             type = argparse.FileType( 'r' ),
                             required = True,
                             help = 'path to the file containing the script to run',
                             dest = 'script' )
        parser.add_argument( '-n', '--nocontext',
                             dest = 'nocontext',
                             action = 'store_true',
                             default = False,
                             required = False,
                             help = 'if present will NOT overwrite the context in the script with the one passed in the run_script command line' )
        arguments = self.parse( parser, s )

        if arguments is not None:
            arguments.script = [ x for x in arguments.script.read().split( '\n' ) if ( x.strip() != '' and not x.startswith( '#' ) ) ]

            self.outputString( "Executing script containing %d commands." % ( len( arguments.script ), ) )

            if arguments.nocontext:
                curContext = ''
            else:
                curContext = ' -! %s -@ %s -x %s ' % ( arguments.toAgent, arguments.investigationId, arguments.expiry )

            self.cmdqueue.extend( [ x + curContext for x in arguments.script ] )

    @report_errors
    def do_history_dump( self, s ):
        '''Dump the full recent history of events on the sensor.'''

        parser = self.getParser( 'history_dump', True )
        arguments = self.parse( parser, s )
        if arguments is not None:
            self._executeHbsTasking( self.tags.notification.HISTORY_DUMP_REQ,
                                     rSequence(),
                                     arguments )
Ejemplo n.º 4
0
 def connectWithConfig( self, beachConfig, token ):
     self.be = BEAdmin( beachConfig, token )
     self.outputString( "Interface to cloud set." )
Ejemplo n.º 5
0
class LP(cmd.Cmd):

    # ===========================================================================
    #   HOUSEKEEPING
    # ===========================================================================
    prompt = "<NEED_LOGIN> %> "

    def __init__(self):
        cmd.Cmd.__init__(self)
        self.be = None
        self.user = None
        self.hbsKey = None
        self.aid = None
        self.investigationId = None
        self.tags = Symbols()
        readline.set_completer_delims(":;'\"? \t")

    def updatePrompt(self):
        self.prompt = "%s%s / %s %s%%> " % (
            ("" if self.hbsKey is None else "* "),
            ("" if self.user is None else self.user),
            ("" if self.aid is None else self.aid),
            ("" if (self.investigationId is None or self.investigationId == "") else " : %s " % self.investigationId),
        )

    def getParser(self, desc, isHbsTask=False):
        parser = argparse.ArgumentParser(prog=desc)

        if isHbsTask:
            parser.add_argument(
                "-!",
                type=AgentId,
                required=False,
                default=AgentId(self.aid),
                help="agent id to change context to ONLY for the duration of this command.",
                dest="toAgent",
            )

            parser.add_argument(
                "-x",
                type=int,
                required=False,
                default=(60 * 60 * 1),
                help="set this command's specific expiry time in seconds.",
                dest="expiry",
            )

            parser.add_argument(
                "-@",
                type=str,
                required=False,
                default=self.investigationId,
                help="the investigation id to attach to the command, results and side-effects.",
                dest="investigationId",
            )

        return parser

    def parse(self, parser, line):
        try:
            return parser.parse_args(shlex.split(line))
        except SystemExit:
            return None

    def do_exit(self, s):
        return True

    def do_quit(self, s):
        return True

    def emptyline(self):
        pass

    def completedefault(self, text, line, begidx, endidx):
        def get_possible_filename_completions(text):
            head, tail = os.path.split(text.strip())
            if head == "":  # no head
                head = "."
            files = os.listdir(head)
            return [os.path.join(head, f) for f in files if f.startswith(tail)]

        if begidx != 0:
            return get_possible_filename_completions(text)
        else:
            return [x[3:] for x in dir(self) if x.startswith("do_")]

    def execAndPrintResponse(self, command, arguments, isHbsTask=False):
        if isHbsTask:
            tmp = arguments
            arguments = argparse.Namespace()

            if not tmp.toAgent.isValid or self.hbsKey is None:
                print("Agent id and hbs key must be set in context.")
                return

            if not hasattr(tmp, "key") or tmp.key is None:
                setattr(tmp, "key", self.hbsKey)

            if (tmp.investigationId is not None) and "" != tmp.investigationId:
                setattr(arguments, "investigationId", tmp.investigationId)

            setattr(arguments, "toAgent", tmp.toAgent)
            setattr(arguments, "task", tmp.task)
            setattr(arguments, "key", tmp.key)
            setattr(arguments, "id", tmp.id)
            setattr(arguments, "expiry", int(tmp.expiry + time.time()))

            del (tmp)

        for k, a in vars(arguments).iteritems():
            if type(a) is AgentId:
                if not a.isValid:
                    print("Invalid agent id.")
                    return
                else:
                    setattr(arguments, k, str(a))

        results = command(**vars(arguments))

        if results.isSuccess:
            print("<<<SUCCESS>>>")
        elif results.isTimedOut:
            print("<<<TIMEOUT>>>")
        else:
            if 0 == len(results.error):
                print("<<<FAILURE>>>")
            else:
                print("<<<FAILURE: %s>>>" % results.error)
        pprint.pprint(results.data, indent=2, width=80)

        return results.data

    def getTags(self, tagsPath):
        raw_tags = json.loads(open(tagsPath, "r").read())
        tags = Symbols()
        for group in raw_tags["groups"]:
            g = Symbols()
            for definition in group["definitions"]:
                setattr(g, definition["name"], str(definition["value"]))
            setattr(tags, group["groupName"], g)

        return tags

    # ===========================================================================
    #   SESSION SETTING COMMANDS
    # ===========================================================================
    @report_errors
    def do_login(self, s):
        """Login to the BE using credentials stored in a config file."""

        parser = self.getParser("login")
        parser.add_argument(
            "configFile", type=argparse.FileType("r"), help="config file specifying the endpoint and token to use"
        )
        parser.add_argument(
            "-k",
            "--key",
            required=False,
            default=None,
            # type = argparse.FileType( 'r' ),
            type=str,
            help="key to use to sign hbs tasks",
            dest="key",
        )
        arguments = self.parse(parser, s)

        if arguments is not None:
            try:
                config = json.loads(arguments.configFile.read())
            except:
                print("Invalid config file format (JSON): %s" % traceback.format_exc())
                return

            if "beach_config" not in config or "token" not in config:
                print("Missing endpoint or token in config.")
                return

            _ = os.getcwd()
            os.chdir(os.path.dirname(__file__))
            self.be = BEAdmin(config["beach_config"], config["token"])
            os.chdir(_)

            remoteTime = self.be.testConnection()

            if remoteTime.isTimedOut:
                print("Endpoint did not respond.")
                return

            if "pong" not in remoteTime.data:
                print("Endpoint responded with invalid data.")
                return

            if arguments.key is not None:
                if os.path.isfile(arguments.key):
                    try:
                        password = getpass.getpass()
                        print("...decrypting key...")
                        # There are weird problems with pexpect and newlines and binary, so
                        # we have to brute force it a bit
                        for i in range(0, 30):
                            proc = pexpect.spawn("openssl aes-256-cbc -d -in %s" % arguments.key)
                            proc.expect(["enter aes-256-cbc decryption password: *"])
                            proc.sendline(password)
                            proc.expect("\r\n")
                            proc.expect(".*")
                            self.hbsKey = proc.match.group(0).replace("\r\n", "\n")
                            try:
                                testSign = Signing(self.hbsKey)
                                testSig = testSign.sign("a")
                                if testSig is not None:
                                    break
                            except:
                                self.hbsKey = None

                        if self.hbsKey is not None:
                            print("success, authenticated!")
                        else:
                            print("error loading key, bad key format or password?")
                    except:
                        self.hbsKey = None
                        print("error getting cloud key: %s" % traceback.format_exc())
                    if self.hbsKey is not None and "bad decrypt" in self.hbsKey:
                        print("Invalid password")
                        self.hbsKey = None
                else:
                    print("Invalid key file: %s." % arguments.key)
                    self.hbsKey = None
            else:
                self.hbsKey = None

            remoteTime = remoteTime.data.get("pong", 0)
            print("Successfully logged in.")
            print("Remote endpoint time: %s." % remoteTime)

            self.user = config["token"].split("/")[0]

            self.updatePrompt()

    @report_errors
    def do_chid(self, s):
        """Change execution context to a specific agent id, used only for HBS tasking."""

        parser = self.getParser("chid")
        parser.add_argument("aid", type=AgentId, help="agent id to change context to")
        arguments = self.parse(parser, s)

        if arguments is not None:
            aid = arguments.aid
            if aid.isValid:
                self.aid = aid
                self.updatePrompt()
            else:
                print("Agent Id is not valid.")

    @report_errors
    def do_setInvestigationId(self, s):
        """Change execution context to relate to a specific investigationId, used only for HBS tasking."""

        parser = self.getParser("setInvestigationId")
        parser.add_argument("investigationId", type=str, help="investigation id to change context to")
        arguments = self.parse(parser, s)

        if arguments is not None:
            self.investigationId = arguments.investigationId
            self.updatePrompt()

    # ===========================================================================
    #   HCP COMMANDS
    # ===========================================================================
    @report_errors
    def do_hcp_getAgentStates(self, s):
        """Get the general state of agents."""

        parser = self.getParser("getAgentStates")
        parser.add_argument(
            "-a", "--aid", type=AgentId, required=False, help="agent id to retrieve the info of", dest="aid"
        )
        parser.add_argument(
            "-n",
            "--hostname",
            type=str,
            required=False,
            help="hostname of the agent to retrieve the info of",
            dest="hostname",
        )
        arguments = self.parse(parser, s)

        if arguments is not None:
            self.execAndPrintResponse(self.be.hcp_getAgentStates, arguments)

    @report_errors
    def do_hcp_setPeriod(self, s):
        """Set the period agents beacon back."""

        parser = self.getParser("setPeriod")
        parser.add_argument("period", type=int, help="new period to schedule hcp beacons over")
        arguments = self.parse(parser, s)

        if arguments is not None:
            self.execAndPrintResponse(self.be.hcp_setPeriod, arguments)

    @report_errors
    def do_hcp_getPeriod(self, s):
        """Get the current agent beacon period."""

        parser = self.getParser("getPeriod")
        arguments = self.parse(parser, s)

        if arguments is not None:
            self.execAndPrintResponse(self.be.hcp_getPeriod, arguments)

    @report_errors
    def do_hcp_addEnrollmentRule(self, s):
        """Add a new enrollment rule for new agents."""

        parser = self.getParser("addEnrollmentRule")
        parser.add_argument(
            "-m", "--mask", type=AgentId, required=True, help="agent id mask this rule applies to", dest="mask"
        )
        parser.add_argument(
            "-i",
            "--internalip",
            type=str,
            required=False,
            default="255.255.255.255",
            help="internal ip mask the rule applies to (255 wildcard)",
            dest="internalIp",
        )
        parser.add_argument(
            "-e",
            "--externalip",
            type=str,
            required=False,
            default="255.255.255.255",
            help="external ip mask the rule applies to (255 wildcard)",
            dest="externalIp",
        )
        parser.add_argument(
            "-s",
            "--newsubnet",
            type=hexArg,
            required=True,
            help="new subnet to give to agents matching this rule (hex)",
            dest="newSubnet",
        )
        parser.add_argument(
            "-o",
            "--neworg",
            type=hexArg,
            required=True,
            help="new org to give to agents matching this rule (hex)",
            dest="newOrg",
        )
        parser.add_argument(
            "-n", "--hostname", type=str, required=False, default="", help="hostname of the host", dest="hostname"
        )
        arguments = self.parse(parser, s)

        if arguments is not None:
            self.execAndPrintResponse(self.be.hcp_addEnrollmentRule, arguments)

    @report_errors
    def do_hcp_delEnrollmentRule(self, s):
        """Remove an enrollment rule for new agents."""

        parser = self.getParser("addEnrollmentRule")
        parser.add_argument(
            "-m", "--mask", type=AgentId, required=True, help="agent id mask this rule applies to", dest="mask"
        )
        parser.add_argument(
            "-i",
            "--internalip",
            type=str,
            required=False,
            default="255.255.255.255",
            help="internal ip mask the rule applies to (255 wildcard)",
            dest="internalIp",
        )
        parser.add_argument(
            "-e",
            "--externalip",
            type=str,
            required=False,
            default="255.255.255.255",
            help="external ip mask the rule applies to (255 wildcard)",
            dest="externalIp",
        )
        parser.add_argument(
            "-s",
            "--newsubnet",
            type=hexArg,
            required=True,
            help="new subnet to give to agents matching this rule (hex)",
            dest="newSubnet",
        )
        parser.add_argument(
            "-o",
            "--neworg",
            type=hexArg,
            required=True,
            help="new org to give to agents matching this rule (hex)",
            dest="newOrg",
        )
        parser.add_argument(
            "-n", "--hostname", type=str, required=False, default="", help="hostname of the host", dest="hostname"
        )
        arguments = self.parse(parser, s)

        if arguments is not None:
            self.execAndPrintResponse(self.be.hcp_delEnrollmentRule, arguments)

    @report_errors
    def do_hcp_getEnrollmentRules(self, s):
        """Get the list of enrollment rules for new agents."""

        parser = self.getParser("getPeriod")
        arguments = self.parse(parser, s)

        if arguments is not None:
            self.execAndPrintResponse(self.be.hcp_getEnrollmentRules, arguments)

    @report_errors
    def do_hcp_addTasking(self, s):
        """Task a module to a list of agents."""

        parser = self.getParser("addTasking")
        parser.add_argument("-m", "--mask", type=AgentId, required=True, help="agent id mask of the rule", dest="mask")
        parser.add_argument("-i", "--moduleid", type=int, required=True, help="module id to task", dest="moduleId")
        parser.add_argument("-s", "--hash", type=str, required=True, help="hash of the module to task", dest="hashStr")
        arguments = self.parse(parser, s)

        if arguments is not None:
            self.execAndPrintResponse(self.be.hcp_addTasking, arguments)

    @report_errors
    def do_hcp_delTasking(self, s):
        """Remove a module from tasking to agents."""

        parser = self.getParser("delTasking")
        parser.add_argument("-m", "--mask", type=AgentId, required=True, help="agent id mask of the rule", dest="mask")
        parser.add_argument("-i", "--moduleid", type=int, required=True, help="module id tasked", dest="moduleId")
        parser.add_argument(
            "-s", "--hash", type=str, required=True, help="hash of the module to untask", dest="hashStr"
        )
        arguments = self.parse(parser, s)

        if arguments is not None:
            self.execAndPrintResponse(self.be.hcp_delTasking, arguments)

    @report_errors
    def do_hcp_getTaskings(self, s):
        """Get the list of modules tasked to agents."""

        parser = self.getParser("getTaskings")
        arguments = self.parse(parser, s)

        if arguments is not None:
            self.execAndPrintResponse(self.be.hcp_getTaskings, arguments)

    @report_errors
    def do_hcp_addModule(self, s):
        """Add a taskable module."""

        parser = self.getParser("addModule")
        parser.add_argument("-i", "--moduleid", type=int, required=True, help="module id", dest="moduleId")
        parser.add_argument(
            "-b",
            "--binary",
            type=argparse.FileType("r"),
            required=True,
            help="path to file containing module",
            dest="binary",
        )
        parser.add_argument(
            "-s",
            "--signature",
            type=argparse.FileType("r"),
            required=True,
            help="path to file containing signature of the module",
            dest="signature",
        )
        parser.add_argument(
            "-d", "--description", type=str, required=True, help="description of the module", dest="description"
        )
        arguments = self.parse(parser, s)

        if arguments is not None:
            arguments.binary = arguments.binary.read()
            arguments.signature = arguments.signature.read()
            self.execAndPrintResponse(self.be.hcp_addModule, arguments)

    @report_errors
    def do_hcp_delModule(self, s):
        """Remove a taskable module."""

        parser = self.getParser("delTasking")
        parser.add_argument("-i", "--moduleid", type=int, required=True, help="module id", dest="moduleId")
        parser.add_argument("-s", "--hash", type=str, required=True, help="hash of the module", dest="hashStr")
        arguments = self.parse(parser, s)

        if arguments is not None:
            self.execAndPrintResponse(self.be.hcp_delModule, arguments)

    @report_errors
    def do_hcp_getModules(self, s):
        """Get the list of modules available for tasking."""

        parser = self.getParser("getModules")
        arguments = self.parse(parser, s)

        if arguments is not None:
            self.execAndPrintResponse(self.be.hcp_getModules, arguments)

    @report_errors
    def do_hcp_relocAgent(self, s):
        """Relocate an agent to a new org and network."""

        parser = self.getParser("relocAgent")
        parser.add_argument("-a", "--agentid", type=AgentId, required=True, help="agent id to relocate", dest="agentid")
        parser.add_argument(
            "-s",
            "--newsubnet",
            type=hexArg,
            required=True,
            help="new subnet to give to agents matching this rule (hex)",
            dest="newSubnet",
        )
        parser.add_argument(
            "-o",
            "--neworg",
            type=hexArg,
            required=True,
            help="new org to give to agents matching this rule (hex)",
            dest="newOrg",
        )
        arguments = self.parse(parser, s)

        if arguments is not None:
            self.execAndPrintResponse(self.be.hcp_relocAgent, arguments)

    @report_errors
    def do_hcp_getRelocations(self, s):
        """Get the list of agent reolcations."""

        parser = self.getParser("getRelocations")
        arguments = self.parse(parser, s)

        if arguments is not None:
            self.execAndPrintResponse(self.be.hcp_getRelocations, arguments)

    # ===========================================================================
    #   HBS COMMANDS
    # ===========================================================================
    def do_hbs_setPeriod(self, s):
        """Set the period agents beacon back."""

        parser = self.getParser("setPeriod")
        parser.add_argument("period", type=int, help="new period to schedule hcp beacons over")
        arguments = self.parse(parser, s)

        if arguments is not None:
            self.execAndPrintResponse(self.be.hbs_setPeriod, arguments)

    @report_errors
    def do_hbs_getPeriod(self, s):
        """Get the current agent beacon period."""

        parser = self.getParser("getPeriod")
        arguments = self.parse(parser, s)

        if arguments is not None:
            self.execAndPrintResponse(self.be.hbs_getPeriod, arguments)

    @report_errors
    def do_hbs_addProfile(self, s):
        """Add an execution profile."""

        parser = self.getParser("addProfile")
        parser.add_argument(
            "-m", "--mask", type=AgentId, required=True, help="agent id mask the profile applies to", dest="mask"
        )
        parser.add_argument(
            "-f",
            "--configfile",
            type=argparse.FileType("r"),
            required=True,
            help="path to the file containing the config",
            dest="config",
        )
        arguments = self.parse(parser, s)

        if arguments is not None:
            arguments.config = arguments.config.read()
            self.execAndPrintResponse(self.be.hbs_addProfile, arguments)

    @report_errors
    def do_hbs_delProfile(self, s):
        """Remove an execution profile."""

        parser = self.getParser("delProfile")
        parser.add_argument(
            "-m", "--mask", type=AgentId, required=True, help="agent id mask the profile applies to", dest="mask"
        )
        arguments = self.parse(parser, s)

        if arguments is not None:
            self.execAndPrintResponse(self.be.hbs_delProfile, arguments)

    @report_errors
    def do_hbs_getProfiles(self, s):
        """Get the list of profiles."""

        parser = self.getParser("getProfiles")
        arguments = self.parse(parser, s)

        if arguments is not None:
            self.execAndPrintResponse(self.be.hbs_getProfiles, arguments)

    # ===========================================================================
    #   HBS TASKINGS
    # ===========================================================================
    def _executeHbsTasking(self, notifId, payload, arguments):
        setattr(arguments, "id", notifId)
        setattr(arguments, "task", payload)

        # Multiplex to all agents matching
        getAgentsArg = argparse.Namespace()
        setattr(getAgentsArg, "aid", arguments.toAgent)
        agents = self.execAndPrintResponse(self.be.hcp_getAgentStates, getAgentsArg)
        if agents is not None:
            if "agents" in agents:
                for aid in agents["agents"].keys():
                    print("Tasking agent %s" % aid)
                    arguments.toAgent = AgentId(aid)
                    self.execAndPrintResponse(self.be.hbs_taskAgent, arguments, True)
            else:
                print("No matching agents found.")
        else:
            print("Failed to get agent list from endpoint.")

    @report_errors
    def do_file_get(self, s):
        """Retrieve a file from the host."""

        parser = self.getParser("file_get", True)
        parser.add_argument("file", type=unicode, help="file path to file to get")
        arguments = self.parse(parser, s)
        if arguments is not None:
            self._executeHbsTasking(
                self.tags.notification.FILE_GET_REQ,
                rSequence().addStringW(self.tags.base.FILE_PATH, arguments.file),
                arguments,
            )

    @report_errors
    def do_file_info(self, s):
        """Retrieve information on a file from the host."""

        parser = self.getParser("file_info", True)
        parser.add_argument("file", type=unicode, help="file path to file to get info on")
        arguments = self.parse(parser, s)
        if arguments is not None:
            self._executeHbsTasking(
                self.tags.notification.FILE_INFO_REQ,
                rSequence().addStringW(self.tags.base.FILE_PATH, arguments.file),
                arguments,
            )

    @report_errors
    def do_dir_list(self, s):
        """Get the directory listing."""

        parser = self.getParser("dir_list", True)
        parser.add_argument("rootDir", type=unicode, help="the root directory where to begin the listing from")
        parser.add_argument(
            "fileExp", type=unicode, help="a file name expression supporting basic wildcards like * and ?"
        )
        parser.add_argument(
            "-d",
            "--depth",
            dest="depth",
            required=False,
            default=0,
            help="optional maximum depth of the listing, defaults to a single level",
        )
        arguments = self.parse(parser, s)
        if arguments is not None:
            self._executeHbsTasking(
                self.tags.hbs.NOTIFICATION_DIR_LIST_REQ,
                rSequence()
                .addStringW(self.tags.base.FILE_PATH, arguments.fileExp)
                .addStringW(self.tags.base.DIRECTORY_PATH, arguments.rootDir)
                .addInt32(self.tags.base.DIRECTORY_LIST_DEPTH, arguments.depth),
                arguments,
            )

    @report_errors
    def do_file_del(self, s):
        """Delete a file from the host."""

        parser = self.getParser("file_del", True)
        parser.add_argument("file", type=unicode, help="file path to delete")
        arguments = self.parse(parser, s)
        if arguments is not None:
            self._executeHbsTasking(
                self.tags.notification.FILE_DEL_REQ,
                rSequence().addStringW(self.tags.base.FILE_PATH, arguments.file),
                arguments,
            )

    @report_errors
    def do_file_mov(self, s):
        """Move a file on the host."""

        parser = self.getParser("file_mov", True)
        parser.add_argument("srcFile", type=unicode, help="source file path")
        parser.add_argument("dstFile", type=unicode, help="destination file path")
        arguments = self.parse(parser, s)
        if arguments is not None:
            self._executeHbsTasking(
                self.tags.notification.FILE_MOV_REQ,
                rSequence()
                .addStringW(self.tags.base.FILE_PATH, arguments.srcFile)
                .addStringW(self.tags.base.FILE_NAME, arguments.dstFile),
                arguments,
            )

    @report_errors
    def do_file_hash(self, s):
        """Hash a file from the host."""

        parser = self.getParser("file_hash", True)
        parser.add_argument("file", type=unicode, help="file path to hash")
        arguments = self.parse(parser, s)
        if arguments is not None:
            self._executeHbsTasking(
                self.tags.notification.FILE_HASH_REQ,
                rSequence().addStringW(self.tags.base.FILE_PATH, arguments.file),
                arguments,
            )

    @report_errors
    def do_mem_map(self, s):
        """Get the memory mapping of a specific process."""

        parser = self.getParser("mem_map", True)
        parser.add_argument("pid", type=int, help="pid of the process to get the map from")
        arguments = self.parse(parser, s)
        if arguments is not None:
            self._executeHbsTasking(
                self.tags.notification.MEM_MAP_REQ,
                rSequence().addInt32(self.tags.base.PROCESS_ID, arguments.pid),
                arguments,
            )

    @report_errors
    def do_mem_read(self, s):
        """Read the memory of a process at a specific address."""

        parser = self.getParser("mem_read", True)
        parser.add_argument("pid", type=int, help="pid of the process to get the map from")
        parser.add_argument("baseAddr", type=hexArg, help="base address to read from, in HEX FORMAT")
        parser.add_argument("memSize", type=hexArg, help="number of bytes to read, in HEX FORMAT")
        arguments = self.parse(parser, s)
        if arguments is not None:
            self._executeHbsTasking(
                self.tags.notification.MEM_READ_REQ,
                rSequence()
                .addInt32(self.tags.base.PROCESS_ID, arguments.pid)
                .addInt64(self.tags.base.BASE_ADDRESS, arguments.baseAddr)
                .addInt32(self.tags.base.MEMORY_SIZE, arguments.memSize),
                arguments,
            )

    @report_errors
    def do_mem_handles(self, s):
        """Get the handles openned by a specific process."""

        parser = self.getParser("mem_handles", True)
        parser.add_argument("pid", type=int, help="pid of the process to get the handles from, 0 for all processes")
        arguments = self.parse(parser, s)
        if arguments is not None:
            self._executeHbsTasking(
                self.tags.notification.MEM_HANDLES_REQ,
                rSequence().addInt32(self.tags.base.PROCESS_ID, arguments.pid),
                arguments,
            )

    @report_errors
    def do_mem_strings(self, s):
        """Get the strings from a specific process."""

        parser = self.getParser("mem_strings", True)
        parser.add_argument("pid", type=int, help="pid of the process to get the strings from")
        arguments = self.parse(parser, s)
        if arguments is not None:
            self._executeHbsTasking(
                self.tags.notification.MEM_STRINGS_REQ,
                rSequence().addInt32(self.tags.base.PROCESS_ID, arguments.pid),
                arguments,
            )

    @report_errors
    def do_os_services(self, s):
        """Get the services registered on the host."""

        parser = self.getParser("getServices", True)
        arguments = self.parse(parser, s)
        if arguments is not None:
            self._executeHbsTasking(self.tags.notification.OS_SERVICES_REQ, rSequence(), arguments)

    @report_errors
    def do_os_drivers(self, s):
        """Get the drivers registered on the host."""

        parser = self.getParser("os_drivers", True)
        arguments = self.parse(parser, s)
        if arguments is not None:
            self._executeHbsTasking(self.tags.notification.OS_DRIVERS_REQ, rSequence(), arguments)

    @report_errors
    def do_os_kill_process(self, s):
        """Kill a process on the host."""

        parser = self.getParser("os_kill_process", True)
        parser.add_argument("pid", type=int, help="pid of the process to kill")
        arguments = self.parse(parser, s)
        if arguments is not None:
            self._executeHbsTasking(
                self.tags.notification.OS_KILL_PROCESS_REQ,
                rSequence().addInt32(self.tags.base.PROCESS_ID, arguments.pid),
                arguments,
            )

    @report_errors
    def do_os_processes(self, s):
        """Generate a new process snapshot."""

        parser = self.getParser("os_processes", True)
        arguments = self.parse(parser, s)
        if arguments is not None:
            self._executeHbsTasking(self.tags.notification.OS_PROCESSES_REQ, rSequence(), arguments)

    @report_errors
    def do_os_autoruns(self, s):
        """Generate a new autoruns snapshot."""

        parser = self.getParser("os_autoruns", True)
        arguments = self.parse(parser, s)
        if arguments is not None:
            self._executeHbsTasking(self.tags.notification.OS_AUTORUNS_REQ, rSequence(), arguments)

    @report_errors
    def do_mem_find_string(self, s):
        """Find the specific strings in a specific process."""

        parser = self.getParser("mem_find_string", True)
        parser.add_argument("pid", type=int, help="pid of the process to search in")
        parser.add_argument(
            "-s",
            "--strings",
            type=unicode,
            required=True,
            nargs="*",
            dest="strings",
            help="list of strings to look for",
        )
        arguments = self.parse(parser, s)
        if arguments is not None:
            seq = rSequence().addInt32(self.tags.base.PROCESS_ID, arguments.pid)
            l = rList()
            for s in arguments.strings:
                l.addStringW(self.tags.base.STRING, s)
            seq.addList(self.tags.base.STRINGSW, l)
            self._executeHbsTasking(self.tags.notification.MEM_FIND_STRING_REQ, seq, arguments)

    @report_errors
    def do_mem_find_handle(self, s):
        """Find the handles in any process that contain a specific substring."""

        parser = self.getParser("mem_find_handle", True)
        parser.add_argument("needle", type=unicode, help="substring of the handle names to get")
        arguments = self.parse(parser, s)
        if arguments is not None:
            self._executeHbsTasking(
                self.tags.notification.MEM_FIND_HANDLE_REQ,
                rSequence().addStringW(self.tags.base.HANDLE_NAME, arguments.needle),
                arguments,
            )

    @report_errors
    def do_run_script(self, s):
        """Runs a list of commands from a file but with additional context passed in the command line."""

        parser = self.getParser("run_script")
        parser.add_argument(
            "-f",
            "--configfile",
            type=argparse.FileType("r"),
            required=True,
            help="path to the file containing the script to run",
            dest="script",
        )
        parser.add_argument(
            "-n",
            "--nocontext",
            dest="nocontext",
            action="store_true",
            default=False,
            required=False,
            help="if present will NOT overwrite the context in the script with the one passed in the run_script command line",
        )
        arguments = self.parse(parser, s)

        if arguments is not None:
            arguments.script = [
                x for x in arguments.script.read().split("\n") if (x.strip() != "" and not x.startswith("#"))
            ]

            print("Executing script containing %d commands." % (len(arguments.script),))

            if arguments.nocontext:
                curContext = ""
            else:
                curContext = " -! %s -@ %s -x %s " % (arguments.toAgent, arguments.investigationId, arguments.expiry)

            self.cmdqueue.extend([x + curContext for x in arguments.script])

    @report_errors
    def do_history_dump(self, s):
        """Dump the full recent history of events on the sensor."""

        parser = self.getParser("history_dump", True)
        arguments = self.parse(parser, s)
        if arguments is not None:
            self._executeHbsTasking(self.tags.notification.HISTORY_DUMP_REQ, rSequence(), arguments)
Ejemplo n.º 6
0
    def do_login(self, s):
        """Login to the BE using credentials stored in a config file."""

        parser = self.getParser("login")
        parser.add_argument(
            "configFile", type=argparse.FileType("r"), help="config file specifying the endpoint and token to use"
        )
        parser.add_argument(
            "-k",
            "--key",
            required=False,
            default=None,
            # type = argparse.FileType( 'r' ),
            type=str,
            help="key to use to sign hbs tasks",
            dest="key",
        )
        arguments = self.parse(parser, s)

        if arguments is not None:
            try:
                config = json.loads(arguments.configFile.read())
            except:
                print("Invalid config file format (JSON): %s" % traceback.format_exc())
                return

            if "beach_config" not in config or "token" not in config:
                print("Missing endpoint or token in config.")
                return

            _ = os.getcwd()
            os.chdir(os.path.dirname(__file__))
            self.be = BEAdmin(config["beach_config"], config["token"])
            os.chdir(_)

            remoteTime = self.be.testConnection()

            if remoteTime.isTimedOut:
                print("Endpoint did not respond.")
                return

            if "pong" not in remoteTime.data:
                print("Endpoint responded with invalid data.")
                return

            if arguments.key is not None:
                if os.path.isfile(arguments.key):
                    try:
                        password = getpass.getpass()
                        print("...decrypting key...")
                        # There are weird problems with pexpect and newlines and binary, so
                        # we have to brute force it a bit
                        for i in range(0, 30):
                            proc = pexpect.spawn("openssl aes-256-cbc -d -in %s" % arguments.key)
                            proc.expect(["enter aes-256-cbc decryption password: *"])
                            proc.sendline(password)
                            proc.expect("\r\n")
                            proc.expect(".*")
                            self.hbsKey = proc.match.group(0).replace("\r\n", "\n")
                            try:
                                testSign = Signing(self.hbsKey)
                                testSig = testSign.sign("a")
                                if testSig is not None:
                                    break
                            except:
                                self.hbsKey = None

                        if self.hbsKey is not None:
                            print("success, authenticated!")
                        else:
                            print("error loading key, bad key format or password?")
                    except:
                        self.hbsKey = None
                        print("error getting cloud key: %s" % traceback.format_exc())
                    if self.hbsKey is not None and "bad decrypt" in self.hbsKey:
                        print("Invalid password")
                        self.hbsKey = None
                else:
                    print("Invalid key file: %s." % arguments.key)
                    self.hbsKey = None
            else:
                self.hbsKey = None

            remoteTime = remoteTime.data.get("pong", 0)
            print("Successfully logged in.")
            print("Remote endpoint time: %s." % remoteTime)

            self.user = config["token"].split("/")[0]

            self.updatePrompt()