Skip to main content
Trym HåkanssonSwitch to Labs
All writing

TECHNICAL REFERENCE · PERSONALLY PUBLISHED · NOT AN EMPLOYER PUBLICATION

CVE-2025-68664 in langchain-core: patch and exposure review

A source-based guide to the langchain-core serialization injection flaw, patched versions and practical exposure review.

Patch first. Then work out which applications could actually move attacker-shaped data through dumps() or dumpd() and later into load() or loads().

That order follows the primary sources. NVD describes CVE-2025-68664 as a serialization injection vulnerability in langchain-core. GitHub's reviewed advisory rates it critical at CVSS 9.3 and lists the patched versions as 0.3.81 and 1.2.5. LangChain merged the upstream serialization patch in pull request 34455.

The vulnerability details here come from NVD, GitHub and the upstream remediation. The detection guidance is a defensive adaptation based on Microsoft security operations and detection-engineering experience. It is not independent vulnerability research.

What failed at the trust boundary

LangChain uses an lc key to mark its serialized objects. Before the patch, dumps() and dumpd() did not escape a plain, user-controlled dictionary that contained the same key.

The vulnerable sequence was:

  1. Attacker-influenced data contained an lc structure.
  2. The application passed that data through dumps() or dumpd().
  3. The serialized data later reached load() or loads().
  4. The loader interpreted the injected structure as a LangChain object instead of plain user data.

The GitHub advisory lists model response fields such as additional_kwargs and response_metadata as common sources of attacker-influenced content. Prompt injection is therefore one route into the vulnerable data flow, but the bug is still a serialization and deserialization flaw. A prompt alone is not evidence that a specific application reached the vulnerable loader.

What impact is supported by the advisory

GitHub documents two direct impact classes:

  • extraction of environment-variable secrets when deserialization used secrets_from_env=True, which was the old default;
  • instantiation of classes within trusted namespaces using attacker-controlled parameters, potentially triggering constructor side effects such as network or file operations.

The patch also introduced stricter defaults:

  • allowed_objects="core" limits the default object set;
  • secrets_from_env changed from True to False;
  • an init_validator blocks Jinja2 templates by default.

Cyata's research discusses code-execution paths under additional conditions and says direct code execution from loads() alone was not confirmed in its Jinja2 path. The useful wording is therefore precise: the advisory supports secret extraction and controlled object instantiation. It does not support saying that every vulnerable installation gives immediate remote code execution.

Who needs to review exposure

GitHub lists these vulnerable flows, among others:

  • astream_events(version="v1");
  • Runnable.astream_log();
  • dumps() or dumpd() on untrusted data followed by load() or loads();
  • direct deserialization of untrusted data;
  • RunnableWithMessageHistory;
  • InMemoryVectorStore.load() with untrusted documents;
  • untrusted cache generations;
  • untrusted manifests from hub.pull();
  • MultiVectorRetriever with byte stores containing untrusted documents.

Do not turn this into a checklist where package presence equals exploitability. Package presence establishes patch scope. Reachability and data flow establish application exposure.

Verify the installed package

Check the resolved environment, not only the declared requirement:

python -m pip show langchain-core
python -c "import langchain_core; print(langchain_core.__version__)"

For containers and serverless packages, run the check against the built artifact or software bill of materials. A lockfile in the repository does not prove the deployed image resolved the same version.

Upgrade on the relevant release line:

# 0.3 release line
python -m pip install "langchain-core>=0.3.81,<0.4"

# 1.x release line
python -m pip install "langchain-core>=1.2.5,<2"

Rebuild the deployable artifact and verify the version again after dependency resolution.

Defender XDR inventory starting point

If Defender Vulnerability Management surfaces the package in DeviceTvmSoftwareInventory, this query can group vulnerable versions. Coverage of Python packages depends on the environment and inventory integration, so absence is not proof of safety.

DeviceTvmSoftwareInventory
| where SoftwareName has "langchain"
| extend ParsedVersion = parse_version(SoftwareVersion)
| where
    ParsedVersion < parse_version("0.3.81")
    or (
        ParsedVersion >= parse_version("1.0.0")
        and ParsedVersion < parse_version("1.2.5")
    )
| summarize
    Devices = make_set(DeviceName, 100),
    DeviceCount = dcount(DeviceName)
    by SoftwareName, SoftwareVersion
| order by DeviceCount desc

Pair this with dependency scanning, container registry inventory and SBOM data. Python libraries often live in virtual environments or images that endpoint software inventory does not enumerate reliably.

Find likely runtime locations

Process telemetry can help identify where Python services mention LangChain. It cannot reveal the loaded package version or confirm exploitation.

DeviceProcessEvents
| where Timestamp > ago(30d)
| where FileName in~ (
    "python.exe",
    "python",
    "python3",
    "uvicorn",
    "gunicorn"
)
| where ProcessCommandLine has_any (
    "langchain",
    "langgraph",
    "langchain_core"
)
| summarize
    FirstSeen = min(Timestamp),
    LastSeen = max(Timestamp),
    Accounts = make_set(AccountName, 20),
    Commands = make_set(ProcessCommandLine, 20)
    by DeviceId, DeviceName, FileName, FolderPath
| order by LastSeen desc

Use the result to find owners and deployment manifests. Do not convert it into an exploitation alert.

Review the actual code path

After patching, answer four concrete questions for each application:

  1. Can user input, retrieved content, tool output or model output populate a serialized dictionary?
  2. Does the application call dumps() or dumpd() on that data?
  3. Can the resulting bytes or text later reach load() or loads()?
  4. Which secrets, network routes, files and allowed classes are available to that process?

If the flow is unnecessary, remove it. If serialization is required, validate the data against a strict schema before it crosses the boundary. Keep unexpected keys out of model and tool outputs rather than trusting downstream code to interpret them safely.

Keep the patched defaults restrictive

Explicitly avoid environment-secret resolution for untrusted data:

from langchain_core.load import loads

value = loads(serialized_data, secrets_from_env=False)

Keep allowed_objects as narrow as the application permits. Do not disable the default init_validator for Jinja2 templates unless the serialized data is fully trusted and the application has a documented reason.

Migrate astream_events(version="v1") consumers to version 2. GitHub states that version 1 uses the vulnerable serialization path and version 2 does not.

What the detection can and cannot tell you

Inventory finds patch candidates. Process telemetry finds likely owners and runtimes. Neither proves that attacker-controlled data reached the vulnerable serialization flow.

Exploit assessment requires application logs, traces, serialized artifacts and code review. Look for unexpected lc structures in attacker-influenced fields, loader calls against those artifacts and outbound or file side effects from the affected process. Preserve the evidence before rotating secrets if the application may have exposed them.

The durable lesson is simple: data produced by a model or tool is still untrusted when it reaches a deserializer. Patch this CVE, then keep that boundary explicit for the next framework bug.

Sources

Questions this article answers.

What is CVE-2025-68664?

It is a serialization injection vulnerability in langchain-core. User-controlled dictionaries containing the reserved lc key were not escaped by dumps() and dumpd(), allowing later deserialization to treat them as LangChain objects.

Which versions are affected?

GitHub lists langchain-core versions below 0.3.81 and versions from 1.0.0 up to, but excluding, 1.2.5 as affected.

What can an attacker do?

The GitHub advisory documents environment-secret extraction when secrets_from_env is enabled and instantiation of allowed classes with attacker-controlled parameters, which can trigger side effects. Broader code-execution claims depend on additional conditions and should not be stated as automatic.

How should a team respond?

Upgrade first, verify the resolved dependency in each deployed artifact, review vulnerable serialization flows and retain the patched defaults for allowed objects, environment secrets and Jinja2 templates.

  1. Secure AI Adoption: Start With the Data Path

    Microsoft's 2026 Data Security Index shows a control gap among surveyed organizations. This is a practical Microsoft security sequence for finding AI use, reducing data exposure and handling incidents.