def testRdfFormatterHandlesKeyValuePair(self): """rdfvalue.KeyValue items need special handling to expand k and v.""" key = rdf_protodict.DataBlob().SetValue("skynet") value = rdf_protodict.DataBlob().SetValue([1997]) rdf = rdf_protodict.KeyValue(k=key, v=value) template = "{k}: {v}" hinter = hints.Hinter(template=template) expected = "skynet: 1997" result = hinter.Render(rdf) self.assertEqual(expected, result)
def Run(self, unused_arg): """This kills us with no cleanups.""" logging.debug("Disabling service") win32serviceutil.ChangeServiceConfig( None, config.CONFIG["Nanny.service_name"], startType=win32service.SERVICE_DISABLED) svc_config = QueryService(config.CONFIG["Nanny.service_name"]) if svc_config[1] == win32service.SERVICE_DISABLED: logging.info("Disabled service successfully") self.SendReply(rdf_protodict.DataBlob(string="Service disabled.")) else: self.SendReply( rdf_protodict.DataBlob(string="Service failed to disable."))
def Run(self, args): """Reads a buffer on the client and sends it to the server.""" # Make sure we limit the size of our output if args.length > constants.CLIENT_MAX_BUFFER_SIZE: raise RuntimeError("Can not read buffers this large.") data = vfs.ReadVFS(args.pathspec, args.offset, args.length, progress_callback=self.Progress) result = rdf_protodict.DataBlob( data=zlib.compress(data), compression=rdf_protodict.DataBlob.CompressionType.ZCOMPRESSION) digest = hashlib.sha256(data).digest() # Ensure that the buffer is counted against this response. Check network # send limit. self.ChargeBytesToSession(len(data)) # Now return the data to the server into the special TransferStore well # known flow. self.grr_worker.SendReply( result, session_id=rdfvalue.SessionID(flow_name="TransferStore")) # Now report the hash of this blob to our flow as well as the offset and # length. self.SendReply( rdf_client.BufferReference(offset=args.offset, length=len(data), data=digest))
def _MakeRegStat(self, path, value, registry_type): options = rdf_paths.PathSpec.Options.CASE_LITERAL pathspec = rdf_paths.PathSpec( path=path, path_options=options, pathtype=rdf_paths.PathSpec.PathType.REGISTRY) if registry_type == rdf_client.StatEntry.RegistryType.REG_MULTI_SZ: reg_data = rdf_protodict.DataBlob(list=rdf_protodict.BlobArray( content=rdf_protodict.DataBlob(string=value))) else: reg_data = rdf_protodict.DataBlob().SetValue(value) return rdf_client.StatEntry(pathspec=pathspec, registry_data=reg_data, registry_type=registry_type)
def _StoreDataAndHash(self, data: AnyStr, offset: int) -> None: """Uploads data as blob and replies hash to flow. Args: data: Bytes to be stored as a blob. offset: Offset where the data was read from. """ data_blob = rdf_protodict.DataBlob( data=zlib.compress(data), compression=rdf_protodict.DataBlob.CompressionType.ZCOMPRESSION) # Ensure that the buffer is counted against this response. Check network # send limit. self.ChargeBytesToSession(len(data)) # Now return the data to the server into the special TransferStore well # known flow. self.grr_worker.SendReply( data_blob, session_id=rdfvalue.SessionID(flow_name="TransferStore")) # Now report the hash of this blob to our flow as well as the offset and # length. digest = hashlib.sha256(data).digest() buffer_reference = rdf_client.BufferReference(offset=offset, length=len(data), data=digest) self._partial_file_hash.update(data) partial_file_hash = self._partial_file_hash.digest() self.SendReply( rdf_read_low_level.ReadLowLevelResult( blob=buffer_reference, accumulated_hash=partial_file_hash))
def testForemanMessageHandler(self): with test_lib.ConfigOverrider({ "Database.useForReads": True, "Database.useForReads.message_handlers": True }): with mock.patch.object(foreman.Foreman, "AssignTasksToClient") as instr: worker_obj = worker_lib.GRRWorker(token=self.token) # Send a message to the Foreman. session_id = administrative.Foreman.well_known_session_id client_id = rdf_client.ClientURN("C.1100110011001100") self.SendResponse( session_id, rdf_protodict.DataBlob(), client_id=client_id, well_known=True) done = threading.Event() def handle(l): worker_obj._ProcessMessageHandlerRequests(l) done.set() data_store.REL_DB.RegisterMessageHandler( handle, worker_obj.well_known_flow_lease_time, limit=1000) self.assertTrue(done.wait(10)) # Make sure there are no leftover requests. self.assertEqual(data_store.REL_DB.ReadMessageHandlerRequests(), []) instr.assert_called_once_with(client_id)
def Start(self): for i in range(10): self.CallClient( client_test_lib.Test, rdf_protodict.DataBlob(string="test%s" % i), data=str(i), next_state="Incoming")
def Run(self, unused_arg): """This kills us with no cleanups.""" logging.debug("Disabling service") msg = "Service disabled." if hasattr(sys, "frozen"): grr_binary = os.path.abspath(sys.executable) elif __file__: grr_binary = os.path.abspath(__file__) try: os.remove(grr_binary) except OSError: msg = "Could not remove binary." try: os.remove(config.CONFIG["Client.plist_path"]) except OSError: if "Could not" in msg: msg += " Could not remove plist file." else: msg = "Could not remove plist file." # Get the directory we are running in from pyinstaller. This is either the # GRR directory which we should delete (onedir mode) or a generated temp # directory which we can delete without problems in onefile mode. directory = getattr(sys, "_MEIPASS", None) if directory: shutil.rmtree(directory, ignore_errors=True) self.SendReply(rdf_protodict.DataBlob(string=msg))
def testForemanMessageHandler(self): with mock.patch.object(foreman.Foreman, "AssignTasksToClient") as instr: # Send a message to the Foreman. client_id = "C.1100110011001100" data_store.REL_DB.WriteMessageHandlerRequests([ rdf_objects.MessageHandlerRequest( client_id=client_id, handler_name="ForemanHandler", request_id=12345, request=rdf_protodict.DataBlob()) ]) done = threading.Event() def handle(l): worker_lib.ProcessMessageHandlerRequests(l) done.set() data_store.REL_DB.RegisterMessageHandler( handle, worker_lib.GRRWorker.message_handler_lease_time, limit=1000) try: self.assertTrue(done.wait(10)) # Make sure there are no leftover requests. self.assertEqual( data_store.REL_DB.ReadMessageHandlerRequests(), []) instr.assert_called_once_with(client_id) finally: data_store.REL_DB.UnregisterMessageHandler(timeout=60)
def testBlobHandlerMessagesAreHandledOnTheFrontend(self): client_id = "C.1234567890123456" data_store.REL_DB.WriteClientMetadata(client_id, fleetspeak_enabled=False) # Check that the worker queue is empty. self.assertEmpty(data_store.REL_DB.ReadMessageHandlerRequests()) data = b"foo" data_blob = rdf_protodict.DataBlob( data=zlib.compress(data), compression=rdf_protodict.DataBlob.CompressionType.ZCOMPRESSION) messages = [ rdf_flows.GrrMessage( source=client_id, session_id=str(rdfvalue.SessionID(flow_name="TransferStore")), payload=data_blob, auth_state=rdf_flows.GrrMessage.AuthorizationState.AUTHENTICATED, ) ] ReceiveMessages(client_id, messages) # Check that the worker queue is still empty. self.assertEmpty(data_store.REL_DB.ReadMessageHandlerRequests()) # Check that the blob was written to the blob store. self.assertTrue( data_store.BLOBS.CheckBlobExists(rdf_objects.BlobID.FromBlobData(data)))
def testNannyMessageHandlerForUnknownClient(self): client_id = self.SetupClient(0) nanny_message = "Oh no!" email_dict = {} def SendEmail(address, sender, title, message, **_): email_dict.update( dict(address=address, sender=sender, title=title, message=message)) with utils.Stubber(email_alerts.EMAIL_ALERTER, "SendEmail", SendEmail): flow_test_lib.MockClient(client_id, None)._PushHandlerMessage( rdf_flows.GrrMessage( source=client_id, session_id=rdfvalue.SessionID(flow_name="NannyMessage"), payload=rdf_protodict.DataBlob(string=nanny_message), request_id=0, auth_state="AUTHENTICATED", response_id=123)) # We expect the email to be sent. self.assertEqual(email_dict.get("address"), config.CONFIG["Monitoring.alert_email"]) # Make sure the message is included in the email message. self.assertIn(nanny_message, email_dict["message"]) self.assertIn(client_id, email_dict["title"])
def _Stat(self, name, value, value_type, mtime=None): response = rdf_client_fs.StatEntry() response_pathspec = self.pathspec.Copy() # No matter how we got here, there is no need to do case folding from now on # since this is the exact filename casing. response_pathspec.path_options = rdf_paths.PathSpec.Options.CASE_LITERAL response_pathspec.last.path = utils.JoinPath( response_pathspec.last.path, name) response.pathspec = response_pathspec if self.IsDirectory(): response.st_mode = stat.S_IFDIR else: response.st_mode = stat.S_IFREG if mtime: response.st_mtime = mtime if value is None: response.st_size = 0 elif isinstance(value, bytes): response.st_size = len(value) else: response.st_size = len(str(value).encode("utf-8")) if value_type is not None: response.registry_type = self.registry_map.get(value_type, 0) response.registry_data = rdf_protodict.DataBlob().SetValue(value) return response
def testClientAlertHandler(self): client_id = self.SetupClient(0).Basename() client_message = "Oh no!" email_dict = {} def SendEmail(address, sender, title, message, **_): email_dict.update( dict(address=address, sender=sender, title=title, message=message)) with test_lib.ConfigOverrider({ "Database.useForReads": True, "Database.useForReads.message_handlers": True }): with utils.Stubber(email_alerts.EMAIL_ALERTER, "SendEmail", SendEmail): flow_test_lib.MockClient(client_id, None)._PushHandlerMessage( rdf_flows.GrrMessage( source=client_id, session_id=rdfvalue.SessionID(flow_name="ClientAlert"), payload=rdf_protodict.DataBlob(string=client_message), request_id=0, auth_state="AUTHENTICATED", response_id=123)) self._CheckAlertEmail(client_id, client_message, email_dict)
def testNannyMessageHandlerForUnknownClient(self): client_id = "C.1000000000000000" nanny_message = "Oh no!" email_dict = {} def SendEmail(address, sender, title, message, **_): email_dict.update( dict(address=address, sender=sender, title=title, message=message)) with test_lib.ConfigOverrider({ "Database.useForReads": True, "Database.useForReads.message_handlers": True }): with utils.Stubber(email_alerts.EMAIL_ALERTER, "SendEmail", SendEmail): flow_test_lib.MockClient(client_id, None)._PushHandlerMessage( rdf_flows.GrrMessage( source=client_id, session_id=rdfvalue.SessionID(flow_name="NannyMessage"), payload=rdf_protodict.DataBlob(string=nanny_message), request_id=0, auth_state="AUTHENTICATED", response_id=123)) # We expect the email to be sent. self.assertEqual( email_dict.get("address"), config.CONFIG["Monitoring.alert_email"]) # Make sure the message is included in the email message. self.assertIn(nanny_message, email_dict["message"]) if data_store.RelationalDBReadEnabled(): self.assertIn(client_id, email_dict["title"]) else: self.assertIn(client_id.Basename(), email_dict["title"])
def Run(self): """The main run method of the client. This method does not normally return. Only if there have been more than connection_error_limit failures, the method returns and allows the client to exit. """ while True: if self.http_manager.ErrorLimitReached(): return # Check if there is a message from the nanny to be sent. self.client_worker.SendNannyMessage() now = time.time() # Check with the foreman if we need to if (now > self.last_foreman_check + config.CONFIG["Client.foreman_check_frequency"]): # We must not queue messages from the comms thread with blocking=True # or we might deadlock. If the output queue is full, we can't accept # more work from the foreman anyways so it's ok to drop the message. try: self.client_worker.SendReply( rdf_protodict.DataBlob(), session_id=rdfvalue.FlowSessionID(flow_name="Foreman"), require_fastpoll=False, blocking=False) self.last_foreman_check = now except queue.Full: pass try: self.RunOnce() except Exception: # pylint: disable=broad-except # Catch everything, yes, this is terrible but necessary logging.warning("Uncaught exception caught: %s", traceback.format_exc()) if flags.FLAGS.pdb_post_mortem: pdb.post_mortem() # We suicide if our memory is exceeded, and there is no more work to do # right now. Our death should not result in loss of messages since we are # not holding any requests in our input queues. if (self.client_worker.MemoryExceeded() and not self.client_worker.IsActive() and self.client_worker.InQueueSize() == 0 and self.client_worker.OutQueueSize() == 0): logging.warning("Memory exceeded - exiting.") self.client_worker.SendClientAlert("Memory limit exceeded, exiting.") # Make sure this will return True so we don't get more work. # pylint: disable=g-bad-name self.client_worker.MemoryExceeded = lambda: True # pylint: enable=g-bad-name # Now send back the client message. self.RunOnce() # And done for now. sys.exit(-1) self.timer.Wait() self.client_worker.Heartbeat()
def _ForemanOp(self): """Sends Foreman checks periodically.""" period = config.CONFIG["Client.foreman_check_frequency"] self._threads["Worker"].SendReply( rdf_protodict.DataBlob(), session_id=rdfvalue.FlowSessionID(flow_name="Foreman"), require_fastpoll=False) time.sleep(period)
def testProvidesSingleValue(self): """Test a single provides value is updated from registry data.""" provides = ["code_page"] self.request = self.InitializeRequest(provides=provides) self.response = rdf_client_fs.StatEntry( registry_data=rdf_protodict.DataBlob(string="value1")) knowledge_base = self.GetUpdatedKnowledgeBase() self.assertEqual(knowledge_base.code_page, "value1")
def MakeRegistryStatEntry(self, path, value): options = rdf_paths.PathSpec.Options.CASE_LITERAL pathspec = rdf_paths.PathSpec( path=path, path_options=options, pathtype=rdf_paths.PathSpec.PathType.REGISTRY) return rdf_client_fs.StatEntry( pathspec=pathspec, registry_data=rdf_protodict.DataBlob().SetValue(value), registry_type=rdf_client_fs.StatEntry.RegistryType.REG_SZ)
def SendNannyMessage(self): # We might be monitored by Fleetspeak. if not self.nanny_controller: return msg = self.nanny_controller.GetNannyMessage() if msg: self.SendReply( rdf_protodict.DataBlob(string=msg), session_id=rdfvalue.FlowSessionID(flow_name="NannyMessage"), require_fastpoll=False) self.nanny_controller.ClearNannyMessage()
def testEqualTimestampNotifications(self): frontend_server = frontend_lib.FrontEndServer( certificate=config.CONFIG["Frontend.certificate"], private_key=config.CONFIG["PrivateKeys.server_key"], message_expiry_time=100, threadpool_prefix="notification-test") # This schedules 10 requests. session_id = flow.StartFlow( client_id=self.client_id, flow_name="WorkerSendingTestFlow", token=self.token) # We pretend that the client processed all the 10 requests at once and # sends the replies in a single http poll. messages = [ rdf_flows.GrrMessage( request_id=i, response_id=1, session_id=session_id, payload=rdf_protodict.DataBlob(string="test%s" % i), auth_state="AUTHENTICATED", generate_task_id=True) for i in range(1, 11) ] status = rdf_flows.GrrStatus(status=rdf_flows.GrrStatus.ReturnedStatus.OK) statuses = [ rdf_flows.GrrMessage( request_id=i, response_id=2, session_id=session_id, payload=status, type=rdf_flows.GrrMessage.Type.STATUS, auth_state="AUTHENTICATED", generate_task_id=True) for i in range(1, 11) ] frontend_server.ReceiveMessages(self.client_id, messages + statuses) with queue_manager.QueueManager(token=self.token) as q: all_notifications = q.GetNotificationsByPriorityForAllShards( rdfvalue.RDFURN("aff4:/F")) medium_priority = rdf_flows.GrrNotification.Priority.MEDIUM_PRIORITY medium_notifications = all_notifications[medium_priority] my_notifications = [ n for n in medium_notifications if n.session_id == session_id ] # There must not be more than one notification. self.assertEqual(len(my_notifications), 1) notification = my_notifications[0] self.assertEqual(notification.first_queued, notification.timestamp) self.assertEqual(notification.last_status, 10)
def setUp(self): super().setUp() self.registry_stat = rdf_client_fs.StatEntry( registry_type=rdf_client_fs.StatEntry.RegistryType.REG_SZ, pathspec=rdf_paths.PathSpec( path="/HKEY_USERS/S-1-5-20/Software/Microsoft/Windows/" "CurrentVersion/Run/Sidebar", pathtype=rdf_paths.PathSpec.PathType.REGISTRY), registry_data=rdf_protodict.DataBlob( string="C:\\Windows\\Sidebar.exe")) self.file_stat = rdf_client_fs.StatEntry(pathspec=rdf_paths.PathSpec( path="/tmp/bar.exe", pathtype=rdf_paths.PathSpec.PathType.OS))
def Run(self, args: rdf_timeline.TimelineArgs) -> None: """Executes the client action.""" result = rdf_timeline.TimelineResult() entries = Walk(args.root) for entry_batch in rdf_timeline.TimelineEntry.SerializeStream(entries): entry_batch_blob = rdf_protodict.DataBlob(data=entry_batch) self.SendReply(entry_batch_blob, session_id=self._TRANSFER_STORE_ID) entry_batch_blob_id = hashlib.sha256(entry_batch).digest() result.entry_batch_blob_ids.append(entry_batch_blob_id) self.Progress() self.SendReply(result)
def SendMessages(self, response_ids, session_id, authenticated=True): """Send messages to the flow.""" for response_id in response_ids: message = rdf_flows.GrrMessage( request_id=1, response_id=response_id, session_id=session_id) blob = rdf_protodict.DataBlob() blob.SetValue(response_id) message.payload = blob if authenticated: auth_state = rdf_flows.GrrMessage.AuthorizationState.AUTHENTICATED message.auth_state = auth_state self.SendMessage(message)
def ProcessFileStats(self, responses): """Extract DataBlob from Stat response.""" if not responses.success: return system_root_paths = ["Windows", "WinNT", "WINNT35", "WTSRV", "WINDOWS"] for response in responses: if response.pathspec.path[4:] in system_root_paths: systemdrive = response.pathspec.path[1:3] systemroot = "%s\\%s" % (systemdrive, response.pathspec.path[4:]) # Put the data back into the original format expected for the artifact data = rdf_protodict.DataBlob().SetValue(systemroot) self.SendReply(rdf_client_fs.StatEntry(registry_data=data)) self.state.success = True break
def Run(self, args: rdf_timeline.TimelineArgs) -> None: """Executes the client action.""" entries = iterator.Counted(Walk(args.root)) for entry_batch in rdf_timeline.TimelineEntry.SerializeStream(entries): entry_batch_blob = rdf_protodict.DataBlob(data=entry_batch) self.SendReply(entry_batch_blob, session_id=self._TRANSFER_STORE_ID) entry_batch_blob_id = hashlib.sha256(entry_batch).digest() result = rdf_timeline.TimelineResult() result.entry_batch_blob_ids.append(entry_batch_blob_id) result.entry_count = entries.count self.SendReply(result) # Each result should contain information only about the number of entries # in the current batch, so after the results are sent we simply reset the # counter. entries.Reset()
def testParse(self): parser = windows_persistence.WindowsPersistenceMechanismsParser() path = (r"HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion" r"\Run\test") pathspec = rdf_paths.PathSpec( path=path, pathtype=rdf_paths.PathSpec.PathType.REGISTRY) reg_data = "C:\\blah\\some.exe /v" reg_type = rdf_client_fs.StatEntry.RegistryType.REG_SZ stat = rdf_client_fs.StatEntry( pathspec=pathspec, registry_type=reg_type, registry_data=rdf_protodict.DataBlob(string=reg_data)) persistence = [stat] image_paths = [ "system32\\drivers\\ACPI.sys", "%systemroot%\\system32\\svchost.exe -k netsvcs", "\\SystemRoot\\system32\\drivers\\acpipmi.sys" ] reg_key = "HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/services/AcpiPmi" for path in image_paths: serv_info = rdf_client.WindowsServiceInformation( name="blah", display_name="GRRservice", image_path=path, registry_key=reg_key) persistence.append(serv_info) knowledge_base = rdf_client.KnowledgeBase() knowledge_base.environ_systemroot = "C:\\Windows" expected = [ "C:\\blah\\some.exe", "C:\\Windows\\system32\\drivers\\ACPI.sys", "C:\\Windows\\system32\\svchost.exe", "C:\\Windows\\system32\\drivers\\acpipmi.sys" ] for index, item in enumerate(persistence): results = list( parser.Parse(item, knowledge_base, rdf_paths.PathSpec.PathType.OS)) self.assertEqual(results[0].pathspec.path, expected[index]) self.assertEqual(len(results), 1)
def SendResponse(self, session_id, data, client_id=None, well_known=False, request_id=None): if not isinstance(data, rdfvalue.RDFValue): data = rdf_protodict.DataBlob(string=data) if well_known: request_id, response_id = 0, 12345 else: request_id, response_id = request_id or 1, 1 with queue_manager.QueueManager(token=self.token) as flow_manager: flow_manager.QueueResponse( rdf_flows.GrrMessage( source=client_id, session_id=session_id, payload=data, request_id=request_id, auth_state="AUTHENTICATED", response_id=response_id)) if not well_known: # For normal flows we have to send a status as well. flow_manager.QueueResponse( rdf_flows.GrrMessage( source=client_id, session_id=session_id, payload=rdf_flows.GrrStatus( status=rdf_flows.GrrStatus.ReturnedStatus.OK), request_id=request_id, response_id=response_id + 1, auth_state="AUTHENTICATED", type=rdf_flows.GrrMessage.Type.STATUS)) flow_manager.QueueNotification( session_id=session_id, last_status=request_id) timestamp = flow_manager.frozen_timestamp return timestamp
def testNannyMessageHandler(self): client_id = self.SetupClient(0) nanny_message = "Oh no!" email_dict = {} def SendEmail(address, sender, title, message, **_): email_dict.update( dict(address=address, sender=sender, title=title, message=message)) with utils.Stubber(email_alerts.EMAIL_ALERTER, "SendEmail", SendEmail): flow_test_lib.MockClient(client_id, None)._PushHandlerMessage( rdf_flows.GrrMessage( source=client_id, session_id=rdfvalue.SessionID(flow_name="NannyMessage"), payload=rdf_protodict.DataBlob(string=nanny_message), request_id=0, auth_state="AUTHENTICATED", response_id=123)) self._CheckNannyEmail(client_id, nanny_message, email_dict)
def testStatEntryToExportedRegistryKeyConverter(self): stat = rdf_client_fs.StatEntry( st_mode=32768, st_size=51, st_mtime=1247546054, registry_type=rdf_client_fs.StatEntry.RegistryType.REG_EXPAND_SZ, pathspec=rdf_paths.PathSpec( path="/HKEY_USERS/S-1-5-20/Software/Microsoft/Windows/" "CurrentVersion/Run/Sidebar", pathtype=rdf_paths.PathSpec.PathType.REGISTRY), registry_data=rdf_protodict.DataBlob(string="Sidebar.exe")) converter = file.StatEntryToExportedRegistryKeyConverter() results = list(converter.Convert(self.metadata, stat)) self.assertLen(results, 1) self.assertEqual( results[0].urn, "aff4:/%s/registry/HKEY_USERS/S-1-5-20/Software/" "Microsoft/Windows/CurrentVersion/Run/Sidebar" % self.client_id) self.assertEqual(results[0].last_modified, rdfvalue.RDFDatetimeSeconds(1247546054)) self.assertEqual(results[0].type, rdf_client_fs.StatEntry.RegistryType.REG_EXPAND_SZ) self.assertEqual(results[0].data, b"Sidebar.exe")
def GetHostnameFromClient(args): del args # Unused. yield rdf_protodict.DataBlob(string=socket.gethostname())