From f1a828016921b4d5852148cdb2b8147ceafd7f69 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Wed, 30 Jul 2025 21:18:09 +1200 Subject: [PATCH] librpc:bcrypt_rsakey_blob: exponent and modulus lengths can't be zero Apart from it making no sense, without these ranges we end up allocating a NULL buffer and aborting. We also put a maximum size on the RSA key, in case we could get tricked into a DoS by pulling a large buffer and trying crypto maths on it. 6 0x572ebce2749a in talloc_abort samba/lib/talloc/talloc.c:506:3 7 0x572ebce271d4 in talloc_chunk_from_ptr samba/lib/talloc/talloc.c:0 8 0x572ebce271d4 in __talloc_with_prefix samba/lib/talloc/talloc.c:762:12 9 0x572ebce235f9 in __talloc samba/lib/talloc/talloc.c:825:9 10 0x572ebce235f9 in _talloc_named_const samba/lib/talloc/talloc.c:982:8 11 0x572ebce235f9 in _talloc_memdup samba/lib/talloc/talloc.c:2441:9 12 0x572ebc8f6a4f in data_blob_talloc_named samba/lib/util/data_blob.c:56:25 13 0x572ebc7d23bd in pull_BCRYPT_RSAPUBLIC_BLOB samba/librpc/ndr/ndr_keycredlink.c:878:17 14 0x572ebc7d23bd in ndr_pull_KeyMaterialInternal samba/librpc/ndr/ndr_keycredlink.c:959:10 15 0x572ebc788e90 in LLVMFuzzerTestOneInput samba/bin/default/lib/fuzzing/fuzz_ndr_keycredlink_TYPE_STRUCT.c:282:13 REF: https://issues.oss-fuzz.com/issues/435039896 Signed-off-by: Douglas Bagnall Reviewed-by: Gary Lockyer Autobuild-User(master): Douglas Bagnall Autobuild-Date(master): Thu Jul 31 05:45:07 UTC 2025 on atb-devel-224 --- librpc/idl/bcrypt_rsakey_blob.idl | 15 +++++-- python/samba/tests/bcrypt_rsakey_blob.py | 51 +++++++++++------------- 2 files changed, 35 insertions(+), 31 deletions(-) diff --git a/librpc/idl/bcrypt_rsakey_blob.idl b/librpc/idl/bcrypt_rsakey_blob.idl index fd5f6134e34..6bd071cf4d4 100644 --- a/librpc/idl/bcrypt_rsakey_blob.idl +++ b/librpc/idl/bcrypt_rsakey_blob.idl @@ -21,13 +21,22 @@ interface bcrypt_rsakey_blob /* Currently only handle RSA Public Key blobs */ [value(0x31415352), range(0x31415352, 0x31415352)] uint32 magic; /* RSA1 */ - uint32 bit_length; + /* + * In key_credential_links we expect bit_length to be + * 2048, but we accept a wider range in part because + * testing is much easier with small numbers. + */ + [range(1,65536)]uint32 bit_length; /* * As of Windows 10 version 1903, public exponents larger * than (2^64 - 1) are no longer supported. */ - [range(0x0,0x8)] uint32 public_exponent_len; - uint32 modulus_len; + [range(0x1,0x8)] uint32 public_exponent_len; + /* + * modulus_len is the key size in bytes, more or less + * bit_length / 8. + */ + [range(0x1, 0x2001)] uint32 modulus_len; /* * We're only supporting public keys, so the private * key prime lengths should be zero diff --git a/python/samba/tests/bcrypt_rsakey_blob.py b/python/samba/tests/bcrypt_rsakey_blob.py index ea984445d24..59e446deec2 100755 --- a/python/samba/tests/bcrypt_rsakey_blob.py +++ b/python/samba/tests/bcrypt_rsakey_blob.py @@ -35,7 +35,8 @@ class BcryptRsaKeyBlobTests(TestCase): def test_unpack_empty_key_blob(self): """ Ensure that a minimal header only BCRYPT_RSAPUBLIC_BLOB - can be unpacked, then packed into identical bytes + can't be unpacked, because it would imply zero length modulus + and exponent numbers, which is meaningless. """ empty_key_blob = bytes.fromhex( "52 53 41 31" # Magic value RSA1 @@ -45,20 +46,11 @@ class BcryptRsaKeyBlobTests(TestCase): "00 00 00 00" # prime one length" "00 00 00 00" # prime two length" ) - blob = ndr_unpack( - bcrypt_rsakey_blob.BCRYPT_RSAPUBLIC_BLOB, empty_key_blob) - - self.assertEqual(blob.magic, 0x31415352) - self.assertEqual(blob.bit_length, 0) - self.assertEqual(blob.public_exponent_len, 0) - self.assertEqual(blob.modulus_len, 0) - self.assertEqual(blob.prime1_len_unused, 0) - self.assertEqual(blob.prime2_len_unused, 0) - self.assertEqual(len(blob.public_exponent), 0) - self.assertEqual(len(blob.modulus), 0) - - packed = ndr_pack(blob) - self.assertEqual(empty_key_blob, packed) + with self.assertRaises(RuntimeError) as e: + ndr_unpack(bcrypt_rsakey_blob.BCRYPT_RSAPUBLIC_BLOB, + empty_key_blob) + self.assertEqual(e.exception.args[0], 13) + self.assertEqual(e.exception.args[1], "Range Error") def test_unpack_invalid_magic(self): """ @@ -67,11 +59,12 @@ class BcryptRsaKeyBlobTests(TestCase): """ invalid_magic_key_blob = bytes.fromhex( "52 53 41 30" # Magic value RSA0 - "00 00 00 00" # bit length - "00 00 00 00" # public exponent length - "00 00 00 00" # modulus length + "04 00 00 00" # bit length + "01 00 00 00" # public exponent length + "01 00 00 00" # modulus length "00 00 00 00" # prime one length "00 00 00 00" # prime two length" + "01 02" # exponent and modulus, one byte each ) with self.assertRaises(RuntimeError) as e: ndr_unpack(bcrypt_rsakey_blob.BCRYPT_RSAPUBLIC_BLOB, @@ -87,11 +80,12 @@ class BcryptRsaKeyBlobTests(TestCase): """ extra_data_key_blob = bytes.fromhex( "52 53 41 31" # Magic value RSA1 - "00 00 00 00" # bit length - "00 00 00 00" # public exponent length - "00 00 00 00" # modulus length + "04 00 00 00" # bit length + "01 00 00 00" # public exponent length + "01 00 00 00" # modulus length "00 00 00 00" # prime one length "00 00 00 00" # prime two length + "01 02" # exponent and modulus, one byte each "01" # a trailing byte of data ) with self.assertRaises(RuntimeError) as e: @@ -127,9 +121,9 @@ class BcryptRsaKeyBlobTests(TestCase): """ invalid_magic_key_blob = bytes.fromhex( "52 53 41 31" # Magic value RSA1 - "00 00 00 00" # bit length + "08 00 00 00" # bit length "09 00 00 00" # public exponent length, 9 bytes - "00 00 00 00" # modulus length + "01 00 00 00" # modulus length "00 00 00 00" # prime one length "00 00 00 00" # prime two length" ) @@ -147,11 +141,12 @@ class BcryptRsaKeyBlobTests(TestCase): """ invalid_prime1_key_blob = bytes.fromhex( "52 53 41 31" # Magic value RSA1 - "00 00 00 00" # bit length - "00 00 00 00" # public exponent length, 9 bytes - "00 00 00 00" # modulus length + "04 00 00 00" # bit length + "01 00 00 00" # public exponent length + "01 00 00 00" # modulus length "01 00 00 00" # prime one length "00 00 00 00" # prime two length" + "01 02" # exponent and modulus, one byte each ) with self.assertRaises(RuntimeError) as e: ndr_unpack(bcrypt_rsakey_blob.BCRYPT_RSAPUBLIC_BLOB, @@ -168,8 +163,8 @@ class BcryptRsaKeyBlobTests(TestCase): invalid_prime2_key_blob = bytes.fromhex( "52 53 41 31" # Magic value RSA1 "00 00 00 00" # bit length - "00 00 00 00" # public exponent length, 9 bytes - "00 00 00 00" # modulus length + "01 00 00 00" # public exponent length + "01 00 00 00" # modulus length "00 00 00 00" # prime one length "01 00 00 00" # prime two length" ) -- 2.47.2