def ls(self, path, max_depth = 1): """Lists contents of a given directory. Args: path: A path to the directory to list the contents of. max_depth: Max depth of subdirectories to explore. If max_depth is >1, then the results will also include the contents of subdirectories (and sub-subdirectories and so on). Returns: A sequence of stat entries. """ if max_depth > 1: args = flows_pb2.RecursiveListDirectoryArgs() args.pathspec.path = path args.pathspec.pathtype = self._path_type args.max_depth = max_depth try: ls = self._client.CreateFlow(name='RecursiveListDirectory', args=args) except api_errors.AccessForbiddenError as e: raise errors.ApprovalMissingError(self.id, e) else: args = flows_pb2.ListDirectoryArgs() args.pathspec.path = path args.pathspec.pathtype = self._path_type try: ls = self._client.CreateFlow(name='ListDirectory', args=args) except api_errors.AccessForbiddenError as e: raise errors.ApprovalMissingError(self.id, e) _timeout.await_flow(ls) return representer.StatEntryList([_.payload for _ in ls.ListResults()])
def ls(self, path: Text, max_depth: int = 1) -> List[jobs_pb2.StatEntry]: """Lists contents of a given VFS directory. Args: path: A path to the directory to list the contents of. max_depth: Max depth of subdirectories to explore. If max_depth is >1, then the results will also include the contents of subdirectories (and sub-subdirectories and so on). Returns: A sequence of stat entries. """ if max_depth < 1: return representer.StatEntryList([]) try: f = self._get_file(path).Get() except api_errors.AccessForbiddenError as e: raise errors.ApprovalMissingError(self._client.client_id, e) if not f.is_directory: raise errors.NotDirectoryError(self._client.client_id, path) try: stat_entries = [_.data.stat for _ in f.ListFiles()] except api_errors.AccessForbiddenError as e: raise errors.ApprovalMissingError(self._client.client_id, e) inner_entries = [] for entry in stat_entries: try: inner_entries += self.ls(entry.pathspec.path, max_depth - 1) except errors.NotDirectoryError: inner_entries += [] return representer.StatEntryList(stat_entries + inner_entries)
def grep(self, path, pattern): """Greps for given content on the specified path. Args: path: A path to a file to be searched. pattern: A regular expression on search for. Returns: A list of buffer references to the matched content. """ args = flows_pb2.FileFinderArgs() args.paths.append(path) args.pathtype = self._path_type cond = args.conditions.add() cond.condition_type = \ flows_pb2.FileFinderCondition.Type.CONTENTS_REGEX_MATCH cond.contents_regex_match.mode = \ flows_pb2.FileFinderContentsRegexMatchCondition.ALL_HITS cond.contents_regex_match.regex = pattern args.action.action_type = flows_pb2.FileFinderAction.Action.STAT try: ff = self._client.CreateFlow(name='FileFinder', args=args) except api_errors.AccessForbiddenError as e: raise errors.ApprovalMissingError(self.id, e) _timeout.await_flow(ff) return representer.BufferReferenceList( [list(_.payload.matches)[0] for _ in ff.ListResults()])
def fgrep(self, path: Text, literal: bytes) -> Sequence[jobs_pb2.BufferReference]: """Greps for given content on the specified path. Args: path: A path to a file to be searched. literal: A literal expression on search for. Returns: A list of buffer references to the matched content. """ args = flows_pb2.FileFinderArgs() args.paths.append(path) args.pathtype = self._path_type cond = args.conditions.add() cond.condition_type = \ flows_pb2.FileFinderCondition.Type.CONTENTS_LITERAL_MATCH cond.contents_literal_match.mode = \ flows_pb2.FileFinderContentsLiteralMatchCondition.Mode.ALL_HITS cond.contents_literal_match.literal = literal args.action.action_type = flows_pb2.FileFinderAction.Action.STAT try: ff = self._client.CreateFlow(name='FileFinder', args=args) except api_errors.AccessForbiddenError as e: raise errors.ApprovalMissingError(self.id, e) _timeout.await_flow(ff) results = [_first_match(result.payload) for result in ff.ListResults()] return representer.BufferReferenceList(results)
def yara(self, signature, pids=None, regex=None): """Scans processes using provided YARA rule. Args: signature: YARA rule to run. pids: List of pids of processes to scan. regex: A regex to match against the process name. Returns: A list of YARA matches. """ if pids is None: pids = [] args = flows_pb2.YaraProcessScanRequest() args.yara_signature = signature args.ignore_grr_process = False if regex is not None: args.process_regex = regex args.pids.extend(pids) try: yara = self._client.CreateFlow(name='YaraProcessScan', args=args) except api_errors.AccessForbiddenError as e: raise errors.ApprovalMissingError(self.id, e) _timeout.await_flow(yara) return [_.payload for _ in yara.ListResults()]
def osquery( self, query: Text, timeout: int = 30000, ignore_stderr_errors: bool = False) -> osquery_pb2.OsqueryTable: """Runs given query on the client. Args: query: An SQL query to run against osquery on the client. timeout: Query timeout in millis. ignore_stderr_errors: If true, will not break in case of stderr errors. Returns: An osquery table corresponding to the result of running the query. """ args = osquery_pb2.OsqueryFlowArgs() args.query = query args.timeout_millis = timeout args.ignore_stderr_errors = ignore_stderr_errors try: oq = self._client.CreateFlow(name='OsqueryFlow', args=args) except api_errors.AccessForbiddenError as e: raise errors.ApprovalMissingError(self.id, e) _timeout.await_flow(oq) result = list(oq.ListResults())[0].payload if not isinstance(result, osquery_pb2.OsqueryResult): raise TypeError(f'Unexpected flow result type: {type(result)}') return result.table
def collect( self, artifact: Text, ) -> Sequence[Union[message.Message, api_utils.UnknownProtobuf]]: """Collects specified artifact. Args: artifact: A name of the artifact to collect. Returns: A list of results that artifact collection yielded. """ args = flows_pb2.ArtifactCollectorFlowArgs() args.artifact_list.append(artifact) args.apply_parsers = True try: ac = self._client.CreateFlow(name='ArtifactCollectorFlow', args=args) except api_errors.AccessForbiddenError as e: raise errors.ApprovalMissingError(self.id, e) _timeout.await_flow(ac) return [_.payload for _ in ac.ListResults()]
def ps(self): """Returns a list of processes running on the client.""" args = flows_pb2.ListProcessesArgs() try: ps = self._client.CreateFlow(name='ListProcesses', args=args) except api_errors.AccessForbiddenError as e: raise errors.ApprovalMissingError(self.id, e) _timeout.await_flow(ps) return representer.ProcessList([_.payload for _ in ps.ListResults()])
def interrogate(self): """Grabs fresh metadata about the client. Returns: A client summary. """ try: interrogate = self._client.CreateFlow(name='Interrogate') except api_errors.AccessForbiddenError as e: raise errors.ApprovalMissingError(self.id, e) _timeout.await_flow(interrogate) self._summary = list(interrogate.ListResults())[0].payload return self._summary
def yara( self, signature: Text, pids: Optional[Sequence[int]] = None, regex: Optional[Text] = None, ) -> Sequence[flows_pb2.YaraProcessScanMatch]: """Scans processes using provided YARA rule. Args: signature: YARA rule to run. pids: List of pids of processes to scan. regex: A regex to match against the process name. Returns: A list of YARA matches. """ if pids is None: pids = [] args = flows_pb2.YaraProcessScanRequest() args.yara_signature = signature args.ignore_grr_process = False if regex is not None: args.process_regex = regex args.pids.extend(pids) try: yara = self._client.CreateFlow(name='YaraProcessScan', args=args) except api_errors.AccessForbiddenError as e: raise errors.ApprovalMissingError(self.id, e) _timeout.await_flow(yara) def yara_result( result: message.Message) -> flows_pb2.YaraProcessScanMatch: if not isinstance(result, flows_pb2.YaraProcessScanMatch): raise TypeError( f'Unexpected flow result type: {type(result)!r}') return result return [yara_result(result.payload) for result in yara.ListResults()]
def _collect_file(self, path: Text) -> None: """Save file from client to VFS. Args: path: A path to the file to collect. Returns: Nothing. """ args = flows_pb2.GetFileArgs() args.pathspec.path = path args.pathspec.pathtype = self._path_type try: gf = self._client.CreateFlow(name='GetFile', args=args) except api_errors.AccessForbiddenError as e: raise errors.ApprovalMissingError(self.id, e) _timeout.await_flow(gf)
def interrogate(self) -> jobs_pb2.ClientSummary: """Grabs fresh metadata about the client. Returns: A client summary. """ try: interrogate = self._client.CreateFlow(name='Interrogate') except api_errors.AccessForbiddenError as e: raise errors.ApprovalMissingError(self.id, e) _timeout.await_flow(interrogate) result = list(interrogate.ListResults())[0].payload if not isinstance(result, jobs_pb2.ClientSummary): raise TypeError(f'Unexpected flow result type: {type(result)!r}') self._summary = result return self._summary
def ps(self) -> Sequence[sysinfo_pb2.Process]: """Returns a list of processes running on the client.""" args = flows_pb2.ListProcessesArgs() try: ps = self._client.CreateFlow(name='ListProcesses', args=args) except api_errors.AccessForbiddenError as e: raise errors.ApprovalMissingError(self.id, e) _timeout.await_flow(ps) def process(result: message.Message) -> sysinfo_pb2.Process: if not isinstance(result, sysinfo_pb2.Process): raise TypeError(f'Unexpected flow result type: {type(result)!r}') return result results = [process(response.payload) for response in ps.ListResults()] return representer.ProcessList(results)
def open(self, path: Text) -> VfsFile: """Opens a file object corresponding to the given path in the VFS. The returned file object is read-only. Args: path: A path to the file to open. Returns: A file-like object (implementing standard IO interface). """ f = self._get_file(path) try: self._client.VerifyAccess() except api_errors.AccessForbiddenError as e: raise errors.ApprovalMissingError(self._client.client_id, e) return VfsFile(f.GetBlobWithOffset)
def glob(self, path): """Globs for files on the given client. Args: path: A glob expression (that may include `*` and `**`). Returns: A sequence of stat entries to the found files. """ args = flows_pb2.GlobArgs() args.paths.append(path) args.pathtype = self._path_type try: glob = self._client.CreateFlow(name='Glob', args=args) except api_errors.AccessForbiddenError as e: raise errors.ApprovalMissingError(self.id, e) _timeout.await_flow(glob) return representer.StatEntryList([_.payload for _ in glob.ListResults()])
def refresh(self, path: Text, max_depth: int = 1) -> None: """Syncs the collected VFS with current filesystem of the client. Args: path: A path to the directory to sync. max_depth: Max depth of subdirectories to sync. If max_depth is >1, then subdirectories (and sub-subdirectories and so on) are going to be synced as well. Returns: Nothing. """ f = self._get_file(path) try: if max_depth > 1: f.RefreshRecursively(max_depth).WaitUntilDone() else: f.Refresh().WaitUntilDone() except api_errors.AccessForbiddenError as e: raise errors.ApprovalMissingError(self._client.client_id, e)
def wget(self, path: Text) -> Text: """Returns a link to the file specified. Args: path: A path to the file. Returns: A link to the file. """ if not FLAGS.grr_admin_ui_url: raise ValueError('GRR Admin UI URL has not been specified') try: f = self._get_file(path).Get() except api_errors.AccessForbiddenError as e: raise errors.ApprovalMissingError(self._client.client_id, e) if f.is_directory: raise ValueError('`{}` is a directory'.format(path)) link = '{}/api/clients/{}/vfs-blob/{}' return link.format(FLAGS.grr_admin_ui_url, self._client.client_id, get_vfs_path(path, self._path_type))
def osquery(self, query, timeout=30000, ignore_stderr_errors=False): """Runs given query on the client. Args: query: An SQL query to run against osquery on the client. timeout: Query timeout in millis. ignore_stderr_errors: If true, will not break in case of stderr errors. Returns: An osquery table corresponding to the result of running the query. """ args = osquery_pb2.OsqueryArgs() args.query = query args.timeout_millis = timeout args.ignore_stderr_errors = ignore_stderr_errors try: oq = self._client.CreateFlow(name='OsqueryFlow', args=args) except api_errors.AccessForbiddenError as e: raise errors.ApprovalMissingError(self.id, e) _timeout.await_flow(oq) return list(oq.ListResults())[0].payload.table