def testDeleteSignedBinary(self): binary1_urn = rdfvalue.RDFURN("aff4:/config/executables/foo1") binary2_urn = rdfvalue.RDFURN("aff4:/config/executables/foo2") signed_binary_utils.WriteSignedBinaryBlobs( binary1_urn, [rdf_crypto.SignedBlob().Sign(b"\x00", self._private_key)]) signed_binary_utils.WriteSignedBinaryBlobs( binary2_urn, [rdf_crypto.SignedBlob().Sign(b"\x11", self._private_key)]) self.assertCountEqual(signed_binary_utils.FetchURNsForAllSignedBinaries(), [binary1_urn, binary2_urn]) signed_binary_utils.DeleteSignedBinary(binary1_urn) self.assertCountEqual(signed_binary_utils.FetchURNsForAllSignedBinaries(), [binary2_urn])
def testWriteSignedBinaryBlobs(self): test_urn = rdfvalue.RDFURN("aff4:/config/executables/foo") test_blobs = [ rdf_crypto.SignedBlob().Sign(b"\x00\x11\x22", self._private_key), rdf_crypto.SignedBlob().Sign(b"\x33\x44\x55", self._private_key), rdf_crypto.SignedBlob().Sign(b"\x66\x77\x88", self._private_key), rdf_crypto.SignedBlob().Sign(b"\x99", self._private_key) ] signed_binary_utils.WriteSignedBinaryBlobs(test_urn, test_blobs) blobs_iter, timestamp = signed_binary_utils.FetchBlobsForSignedBinaryByURN( test_urn) self.assertGreater(timestamp.AsMicrosecondsSinceEpoch(), 0) self.assertCountEqual(list(blobs_iter), test_blobs)
def testExecutePythonEnvironment(self): """Test the basic ExecutePython action.""" python_code = """ import io import uu def decode(encoded): # Use the import (uu) inside a function. This will fail if the environment # for exec is not set up properly. i = io.BytesIO(s) o = io.BytesIO() uu.decode(i, o) return o.getvalue() s = "626567696e20363636202d0a2c3226354c3b265c4035565d523b2630410a200a656e640a" s = s.decode("hex") magic_return_str = decode(s) """ signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(python_code.encode("utf-8"), self.signing_key) request = rdf_client_action.ExecutePythonRequest(python_code=signed_blob) result = self.RunAction(standard.ExecutePython, request)[0] self.assertTrue(result.time_used > 0) self.assertEqual(result.return_val, "Hello World!")
def testExecuteModifiedPython(self): """Test that rejects invalid ExecutePython action.""" utils.TEST_VAL = "original" python_code = "utils.TEST_VAL = 'modified'" signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(python_code.encode("utf-8"), self.signing_key) # Modify the data so the signature does not match. signed_blob.data = b"utils.TEST_VAL = 'notmodified'" request = rdf_client_action.ExecutePythonRequest( python_code=signed_blob) # Should raise since the code has been modified. self.assertRaises(rdf_crypto.VerificationError, self.RunAction, standard.ExecutePython, request) # Lets also adjust the hash. signed_blob.digest = hashlib.sha256(signed_blob.data).digest() request = rdf_client_action.ExecutePythonRequest( python_code=signed_blob) self.assertRaises(rdf_crypto.VerificationError, self.RunAction, standard.ExecutePython, request) # Make sure the code never ran. self.assertEqual(utils.TEST_VAL, "original")
def testExecutePythonEnvironment(self): """Test the basic ExecutePython action.""" python_code = """ import io import uu import binascii def decode(encoded): # Use the import (uu) inside a function. This will fail if the environment # for exec is not set up properly. i = io.BytesIO(s) o = io.BytesIO() uu.decode(i, o) return o.getvalue() s = "626567696e20363636202d0a2c3226354c3b265c4035565d523b2630410a200a656e640a" s = binascii.unhexlify(s.encode("ascii")) magic_return_str = decode(s) """ signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(python_code.encode("utf-8"), self.signing_key) request = rdf_client_action.ExecutePythonRequest( python_code=signed_blob) result = self.RunAction(standard.ExecutePython, request)[0] if platform.system( ) != "Windows": # Windows time resolution is too coarse. self.assertGreater(result.time_used, 0) self.assertEqual(result.return_val, "Hello World!")
def WriteSignedBinary(binary_urn: rdfvalue.RDFURN, binary_content: bytes, private_key: rdf_crypto.RSAPrivateKey, public_key: Optional[rdf_crypto.RSAPublicKey], chunk_size: int = 1024): """Signs a binary and saves it to the datastore. If a signed binary with the given URN already exists, its contents will get overwritten. Args: binary_urn: URN that should serve as a unique identifier for the binary. binary_content: Contents of the binary, as raw bytes. private_key: Key that should be used for signing the binary contents. public_key: Key that should be used to verify the signature generated using the private key. chunk_size: Size, in bytes, of the individual blobs that the binary contents will be split to before saving to the datastore. """ blob_references = rdf_objects.BlobReferences() for chunk_offset in range(0, len(binary_content), chunk_size): chunk = binary_content[chunk_offset:chunk_offset + chunk_size] blob_rdf = rdf_crypto.SignedBlob() blob_rdf.Sign(chunk, private_key, verify_key=public_key) blob_id = data_store.BLOBS.WriteBlobWithUnknownHash( blob_rdf.SerializeToBytes()) blob_references.items.Append( rdf_objects.BlobReference(offset=chunk_offset, size=len(chunk), blob_id=blob_id)) data_store.REL_DB.WriteSignedBinaryReferences( SignedBinaryIDFromURN(binary_urn), blob_references)
def testExecuteBrokenPython(self): """Test broken code raises back to the original flow.""" python_code = "raise ValueError" signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(python_code.encode("utf-8"), self.signing_key) request = rdf_client_action.ExecutePythonRequest(python_code=signed_blob) self.assertRaises(ValueError, self.RunAction, standard.ExecutePython, request)
def testFetchSizeOfSignedBinary(self): binary1_urn = rdfvalue.RDFURN("aff4:/config/executables/foo1") binary2_urn = rdfvalue.RDFURN("aff4:/config/executables/foo2") binary1_data = b"\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99" binary2_blobs = [ rdf_crypto.SignedBlob().Sign(b"\x00\x11\x22", self._private_key), rdf_crypto.SignedBlob().Sign(b"\x33\x44", self._private_key) ] signed_binary_utils.WriteSignedBinary(binary1_urn, binary1_data, private_key=self._private_key, public_key=self._public_key, chunk_size=3) signed_binary_utils.WriteSignedBinaryBlobs(binary2_urn, binary2_blobs) binary1_size = signed_binary_utils.FetchSizeOfSignedBinary(binary1_urn) binary2_size = signed_binary_utils.FetchSizeOfSignedBinary(binary2_urn) self.assertEqual(binary1_size, 10) self.assertEqual(binary2_size, 5)
def testReturnVals(self): """Test return values.""" python_code = "magic_return_str = 'return string'" signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(python_code.encode("utf-8"), self.signing_key) request = rdf_client_action.ExecutePythonRequest(python_code=signed_blob) result = self.RunAction(standard.ExecutePython, request)[0] self.assertEqual(result.return_val, "return string")
def testWriteSignedBinary(self): binary_data = b"\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99" # 10 bytes. test_urn = rdfvalue.RDFURN("aff4:/config/executables/foo") signed_binary_utils.WriteSignedBinary(test_urn, binary_data, private_key=self._private_key, public_key=self._public_key, chunk_size=3) blobs_iter, timestamp = signed_binary_utils.FetchBlobsForSignedBinaryByURN( test_urn) self.assertGreater(timestamp.AsMicrosecondsSinceEpoch(), 0) self.assertIsInstance(blobs_iter, collections.Iterator) # We expect blobs to have at most 3 contiguous bytes of data. expected_blobs = [ rdf_crypto.SignedBlob().Sign(b"\x00\x11\x22", self._private_key), rdf_crypto.SignedBlob().Sign(b"\x33\x44\x55", self._private_key), rdf_crypto.SignedBlob().Sign(b"\x66\x77\x88", self._private_key), rdf_crypto.SignedBlob().Sign(b"\x99", self._private_key) ] self.assertCountEqual(list(blobs_iter), expected_blobs)
def testWrongKey(self): """Test return values.""" python_code = "print 'test'" # Generate a test valid RSA key that isn't the real one. signing_key = rdf_crypto.RSAPrivateKey.GenerateKey(2048, 65537) signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(python_code.encode("utf-8"), signing_key) request = rdf_client_action.ExecutePythonRequest(python_code=signed_blob) self.assertRaises(rdf_crypto.VerificationError, self.RunAction, standard.ExecutePython, request)
def testReadIndividualBlobsFromSignedBinary(self): test_urn = rdfvalue.RDFURN("aff4:/config/executables/foo") test_blobs = [ rdf_crypto.SignedBlob().Sign(b"\x00\x11\x22", self._private_key), rdf_crypto.SignedBlob().Sign(b"\x33\x44\x55", self._private_key), rdf_crypto.SignedBlob().Sign(b"\x66\x77\x88", self._private_key), rdf_crypto.SignedBlob().Sign(b"\x99", self._private_key) ] signed_binary_utils.WriteSignedBinaryBlobs(test_urn, test_blobs) with self.assertRaises(ValueError): signed_binary_utils.FetchBlobForSignedBinaryByURN(test_urn, -1) for i, test_blob in enumerate(test_blobs): blob = signed_binary_utils.FetchBlobForSignedBinaryByURN(test_urn, i) self.assertEqual(blob.data, test_blob.data) with self.assertRaises(signed_binary_utils.BlobIndexOutOfBoundsError): signed_binary_utils.FetchBlobForSignedBinaryByURN(test_urn, len(test_blobs))
def testExecutePython(self): """Test the basic ExecutePython action.""" utils.TEST_VAL = "original" python_code = "utils.TEST_VAL = 'modified'" signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(python_code.encode("utf-8"), self.signing_key) request = rdf_client_action.ExecutePythonRequest(python_code=signed_blob) result = self.RunAction(standard.ExecutePython, request)[0] self.assertTrue(result.time_used > 0) self.assertEqual(result.return_val, "") self.assertEqual(utils.TEST_VAL, "modified")
def testExecuteBinary(self): """Test the basic ExecuteBinaryCommand action.""" signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(open("/bin/ls", "rb").read(), self.signing_key) request = rdf_client_action.ExecuteBinaryRequest( executable=signed_blob, args=[__file__], write_path="ablob") result = self.RunAction(standard.ExecuteBinaryCommand, request)[0] self.assertTrue(result.time_used > 0) self.assertTrue(__file__ in result.stdout)
def testExecutePython(self): """Test the basic ExecutePython action.""" utils.TEST_VAL = "original" python_code = "utils.TEST_VAL = 'modified'" signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(python_code.encode("utf-8"), self.signing_key) request = rdf_client_action.ExecutePythonRequest(python_code=signed_blob) result = self.RunAction(standard.ExecutePython, request)[0] if platform.system() != "Windows": # Windows time resolution is too coarse. self.assertGreater(result.time_used, 0) self.assertEqual(result.return_val, "") self.assertEqual(utils.TEST_VAL, "modified")
def testExecuteBinary(self): """Test the basic ExecuteBinaryCommand action.""" signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(open("/bin/ls", "rb").read(), self.signing_key) filepath = __file__.encode(sys.getfilesystemencoding()) request = rdf_client_action.ExecuteBinaryRequest( executable=signed_blob, args=[filepath], write_path="ablob") result = self.RunAction(standard.ExecuteBinaryCommand, request)[0] self.assertGreater(result.time_used, 0) self.assertIn(filepath, result.stdout)
def testArgs(self): """Test passing arguments.""" utils.TEST_VAL = "original" python_code = """ magic_return_str = py_args['test'] utils.TEST_VAL = py_args[43] """ signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(python_code.encode("utf-8"), self.signing_key) pdict = rdf_protodict.Dict({"test": "dict_arg", 43: "dict_arg2"}) request = rdf_client_action.ExecutePythonRequest( python_code=signed_blob, py_args=pdict) result = self.RunAction(standard.ExecutePython, request)[0] self.assertEqual(result.return_val, "dict_arg") self.assertEqual(utils.TEST_VAL, "dict_arg2")
def testStdoutHooking(self): python_code = """ def f(n): print("F called: %s" % n) print("Calling f.") f(1) print("Done.") """ signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(python_code.encode("utf-8"), self.signing_key) request = rdf_client_action.ExecutePythonRequest(python_code=signed_blob) result = self.RunAction(standard.ExecutePython, request)[0] self.assertTrue(result.time_used > 0) self.assertEqual(result.return_val, "Calling f.\nF called: 1\nDone.\n")
def testProgress(self): python_code = """ def f(): # This should also work inside a function. Progress() f() Progress() print("Done.") """ signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(python_code.encode("utf-8"), self.signing_key) request = rdf_client_action.ExecutePythonRequest(python_code=signed_blob) result = self.RunAction(standard.ExecutePython, request)[0] self.assertTrue(result.time_used > 0) self.assertEqual(result.return_val, "Done.\n")
def testStdoutHooking(self): python_code = """ def f(n): print("F called: %s" % n) print("Calling f.") f(1) print("Done.") """ signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(python_code.encode("utf-8"), self.signing_key) request = rdf_client_action.ExecutePythonRequest(python_code=signed_blob) result = self.RunAction(standard.ExecutePython, request)[0] if platform.system() != "Windows": # Windows time resolution is too coarse. self.assertGreater(result.time_used, 0) self.assertEqual(result.return_val, "Calling f.\nF called: 1\nDone.\n")
def testExecuteBinary(self): """Test the basic ExecuteBinaryCommand action.""" if platform.system() == "Windows": cmd, args = r"C:\Windows\System32\cmd.exe", ["/C", "echo", "foobar"] else: cmd, args = "/bin/echo", ["foobar"] signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(open(cmd, "rb").read(), self.signing_key) request = rdf_client_action.ExecuteBinaryRequest( executable=signed_blob, args=args, write_path="ablob") result = self.RunAction(standard.ExecuteBinaryCommand, request)[0] if platform.system() != "Windows": # Windows time resolution is too coarse. self.assertGreater(result.time_used, 0) self.assertEqual("foobar{}".format(os.linesep).encode("utf-8"), result.stdout)
def WriteSignedBinary(binary_urn, binary_content, private_key, public_key, chunk_size=1024, token=None): """Signs a binary and saves it to the datastore. If a signed binary with the given URN already exists, its contents will get overwritten. Args: binary_urn: URN that should serve as a unique identifier for the binary. binary_content: Contents of the binary, as raw bytes. private_key: Key that should be used for signing the binary contents. public_key: Key that should be used to verify the signature generated using the private key. chunk_size: Size, in bytes, of the individual blobs that the binary contents will be split to before saving to the datastore. token: ACL token to use with the legacy (non-relational) datastore. """ if _ShouldUseLegacyDatastore(): collects.GRRSignedBlob.NewFromContent(binary_content, binary_urn, chunk_size=chunk_size, token=token, private_key=private_key, public_key=public_key) if data_store.RelationalDBWriteEnabled(): blob_references = rdf_objects.BlobReferences() for chunk_offset in range(0, len(binary_content), chunk_size): chunk = binary_content[chunk_offset:chunk_offset + chunk_size] blob_rdf = rdf_crypto.SignedBlob() blob_rdf.Sign(chunk, private_key, verify_key=public_key) blob_id = data_store.BLOBS.WriteBlobWithUnknownHash( blob_rdf.SerializeToString()) blob_references.items.Append( rdf_objects.BlobReference(offset=chunk_offset, size=len(chunk), blob_id=blob_id)) data_store.REL_DB.WriteSignedBinaryReferences( _SignedBinaryIDFromURN(binary_urn), blob_references)
def testProgress(self): python_code = """ def f(): # This should also work inside a function. Progress() f() Progress() print("Done.") """ signed_blob = rdf_crypto.SignedBlob() signed_blob.Sign(python_code.encode("utf-8"), self.signing_key) request = rdf_client_action.ExecutePythonRequest(python_code=signed_blob) result = self.RunAction(standard.ExecutePython, request)[0] if platform.system() != "Windows": # Windows time resolution is too coarse. self.assertGreater(result.time_used, 0) self.assertEqual(result.return_val, "Done.\n")
def NewFromContent(cls, content, urn, chunk_size=1024, token=None, private_key=None, public_key=None): """Alternate constructor for GRRSignedBlob. Creates a GRRSignedBlob from a content string by chunking it and signing each chunk. Args: content: The data to stored in the GRRSignedBlob. urn: The AFF4 URN to create. chunk_size: Data will be chunked into this size (each chunk is individually signed. token: The ACL Token. private_key: An rdf_crypto.RSAPrivateKey() instance. public_key: An rdf_crypto.RSAPublicKey() instance. Returns: the URN of the new object written. """ aff4.FACTORY.Delete(urn, token=token) with data_store.DB.GetMutationPool() as pool: with aff4.FACTORY.Create(urn, cls, mode="w", mutation_pool=pool, token=token) as fd: for start_of_chunk in range(0, len(content), chunk_size): chunk = content[start_of_chunk:start_of_chunk + chunk_size] blob_rdf = rdf_crypto.SignedBlob() blob_rdf.Sign(chunk, private_key, public_key) fd.Add(blob_rdf, mutation_pool=pool) return urn