larley.dev home page
================================================================================
 ClearKey RSA Encryption (12/09/2024)
--------------------------------------------------------------------------------------

Someone asked me whether I could take a look at some weird ClearKey values they found.

It turns out that they're encrypted with a 2048-bit RSA key, so I was able to
defeat this encryption with a straight forward Man-in-the-Middle attack.

A server POST request looks like this:
{
    "kids": [
        "XSttj42FOQ-YkhBqYex1sA",
        "AAAAAAAAAAAAAAAAAAAAAA",
        "eyJ2IjoiMiIsImZpZCI6Ig",
        "aF85MTB2cnRtMDA1MDMiLA",
        "InN2aWQiOiJkaWdpdGFsIg",
        "LCJwbCI6ImV5SndhV1FpTw",
        "aUpvWHpreE1IWnlkRzB3TQ",
        "RFV3TXlJc0ltUmxiR2wyWg",
        "WEo1WDNSNWNHVWlPaUp6ZA",
        "Q0o5IiwiY3MiOiJjZGY1Yw",
        "ODU2NTFiZTZmZDY5NjY1Yw",
        "MTVhNWQxNzdlODYifQAAAA"
    ],
    "spki": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAws_R9o5jfe448hx6yJWPQiSmU7WrOrSNsPKHZnBxg6ZpYR5aKDU69ha2lGQM_ooEG0RgZnCrTLD4_72xF5LgeBQ_qa9BDGvtcLoh9frPrD0Gn-Zwacy-NLy8QF12H-uDIAjgUq_mlneOEapVLhV3GU70FeS-8C9c7M58OOmvpLZDHjrDOl3nvnjNrvKTP68dqwQRmmEB5xto4Fwj594zrFsVdxoqndeJfEdEDxp1DZDdpfDSGcEvDn8UpUz6Z3sOi5_pttFteldtX5j1qDcwG1PhLUlzJ-a_AXLPnR33i5BcDbxGjhJvcn7SVCloVtOGrEbooDMD9_MrVXRNCldnCQIDAQAB",
    "type": "temporary"
}
Which contains one or more key IDs at the top which are separated by `A`s from the rest of the snippets. The bottom half consists of a JSON payload which is irrelevant for this procedure. The request body also includes an RSA public key which can be spoofed. This is what I'll use to decrypt the response. Such a response looks like this:
{
    "keys": [
        {
            "kty": "oct",
            "k": "fbWhuS0-WT6pUEQ083LxkZR1pI5beRLPkTaCmwC_MVwvtOOPvZImX-hWSgrxUqMO8g068GoIUSKaGC1w0YfGfyRjQ_HNa9xJOHe2fUayX9pANhqxHsyrwTLwjqNltHbmPF6NMStfPyoDAKLCm_FdGVuUjAl-O1EcPo3amzqKeGTua468s-_Cb7gA7pWmf33gv5zNSpq87aU6pMGYg_BWfoCl_FcQW7Qd5ol61zymiMgWm9FYn5xgEoonFRp8VNoYLaASIj5PmprH8-raZMe9L_gCCC1CqFH7brcKTDo7DxZaSzzdIV4dLdSLbH6YfkQL0THr3ocrZOi6wVczx956sg",
            "kid": "XSttj42FOQ-YkhBqYex1sA"
        }
    ],
    "type": "temporary"
}
The `k` value found in the `keys` list was encrypted with the public key found in the request payload. An attack could look as follows: Create your 2048-bit RSA key pair:
private_key = RSA.generate(2048)
public_key = private_key.publickey()
Export the public key in the `spki` format:
spki = public_key.export_key(format='DER')
spki_base64 = base64.b64encode(spki).decode('utf-8')
Send your request with the replaced `spki` value and get the keys by: Padding the base64 of key and key ID:
base64.b64decode(encoded_str.replace('-', '+').replace('_', '/') + '==')
And finally decrypting the bytes:
cipher = PKCS1_OAEP.new(private_key, hashAlgo=SHA256)
decrypted_message = cipher.decrypt(encrypted_key_bytes)
A full implementation can be found on my GitHub page: https://github.com/DevLARLEY/ClearKeyRSA