What we built, Fusion AI, runs at about a third the cost of a traditional external pentest, a human tester still signs off on every finding, and it is not here to replace anybody.
We have been hearing that one a lot. So when Melisa from our Business Capture team sat down with Brian Fehrman and me for this episode of AI Security Ops, she started with, “What is this thing you built, and is it the same hype everyone else is selling?”
AI security is getting attention because AI has stopped being a side experiment. It is now part of how work gets done. Employees use copilots to write, research, code, and analyze. Product teams are adding AI into customer experiences. Developers are building applications on top of foundation models. Business teams are experimenting with agents that can read email, summarize documents, query data, and trigger workflows. That is a very different world from the one many AI review processes were designed for. An AI system can pass a benchmark and still fail in production. It can behave safely in a clean test environment and then encounter real […]
If you weren’t taking deepfakes seriously before, it’s too late now to ignore them.
According to new research from Malwarebytes, one in three people who use AI every day said it’s okay to generate pornography of people without their consent.
Nearly 10 years ago, “deepfake” technology provided hobbyists and film editors with artificial intelligence (AI) tools to swap the face of one person onto the body of another. In its infancy, this technology brought silly film experiments like swapping Tom Cruise in Mission Impossible with Keanu Reeves. Today, this same technology produces something far more harmful—fake nude images of teenagers.
On the Lock and Code podcast today with host David Ruiz, we are re-visiting an interview from 2024, in which we spoke with a lawyer named David Chiu about his lawsuit against 16 deepfake nude generation websites.
The websites named in that lawsuit often needed just one image of a person to generate fake pornography. And while nearly everyone has at least one image of themselves online, even if they had hundreds, the path towards deletion is somewhat understood—start by deactivating and deleting popular social media accounts. But for teenagers today, raised mostly online, and who share images directly with friends and boyfriends and girlfriends and exes, it’s likely impossible to remove every visual trace of themselves. Also, they shouldn’t have to face this problem alone.
The Lock and Code podcast frequently discusses structural problems that require individual management. You have to skirt corporate data collection. You have to find the automated license plate readers in your hometown. You have to review every single message you get with a certain antagonism, to guard yourself against scams.
So, it’s rare to encounter a solution that benefits more than one person.
Chiu serves as the City Attorney for San Francisco, which means his department can file a lawsuit on behalf of not just the people of San Francisco, but also California, and that’s what his team did in going after the deepfake websites.
Since then, Chiu’s department has shut down 10 deepfake nude websites, and it received a settlement agreement from a company called Briver LLC to no longer operate any website that creates nonconsensual deepfake pornography.
And, as California goes, so goes the nation.
In May of last year, the Take It Down Act became effective as law in the United States, which criminalizes “revenge porn” and AI-generated nonconsensual intimate imagery. The law is not perfect but so far it is being used as intended. Last month, two men in the US were among the first to be charged with violating the Take It Down act for allegedly creating deepfake nudes that, according to the AP, “included both celebrities as well as private women, including recent high school graduates.”
Today, we revisit our conversation with San Francisco City Attorney David Chiu about the important fight against deepfake porn and the clear threat that his department found against the public.
“At least one of these websites specifically promotes the non-consensual nature of this. So, and I’ll just quote, ‘Imagine wasting time taking her out on dates when you can just use website X to get her nudes.'”
The model behind a security workflow shapes how fast a threat is caught, how accurately an incident is investigated, and how much a defender can trust the result. We treat that choice with care. Today we’re taking a clear step forward: Check Point has joined OpenAI’s Daybreak initiative through its Trusted Access for Cyber (TAC) program. These are real steps in how we bring AI into our defensive operations, and in the security we deliver to our customers. What Trusted Access for Cyber Gives Us Trusted Access for Cyber is OpenAI’s program for vetted security organizations that need its most […]
AI agents need memory. Frameworks like LangGraph provide it through checkpointers – persistence layers that store execution state. But what happens when that persistence layer isn’t locked down?
Key Points
Check Point Research analyzed LangGraph, an open-source framework for stateful AI agents with over 50 million monthly downloads, and uncovered three vulnerabilities in its persistence layer.
Two of them chain into remote code execution: a SQL injection in the SQLite checkpointer (CVE-2025-67644) and an unsafe msgpack deserialization (CVE-2026-28277).
A third, parallel issue (CVE-2026-27022) introduces the same injection class into the Redis checkpointer.
Who’s at risk: teams self-hosting LangGraph with the SQLite or Redis checkpointer, where the application exposes get_state_history() with a user-controlled filter. LangChain’s managed cloud service, LangSmith Deployment (formerly LangGraph Platform), runs PostgreSQL and is not vulnerable.
LangChain patched all three issues. Users should update to langgraph-checkpoint-sqlite 3.0.1+, langgraph 1.0.10+, and langgraph-checkpoint-redis 1.0.2+.
Background
LangGraph is an open-source framework for building stateful, multi-agent AI systems with built-in persistence. It’s an extension of LangChain, with over 50 million monthly downloads according to PyPI stats.
Checkpointers are LangGraph’s persistence layer that stores execution state at each step. LangGraph supports two checkpointer implementations: SQLite and PostgreSQL.
Vulnerability #1: SQL Injection (CVE-2025-67644)
The SQLite Checkpointer Database Schema: The SQLite checkpointer uses an internal table called checkpoints with the following structure:
CREATE TABLE checkpoints (
thread_id TEXT NOT NULL,
checkpoint_ns TEXT NOT NULL DEFAULT '',
checkpoint_id TEXT NOT NULL,
parent_checkpoint_id TEXT,
type TEXT,
checkpoint BLOB,
metadata BLOB,
PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id)
);
The metadata column stores additional contextual information about each checkpoint in JSON format. For example:
When calling the list() function on sqliteSaver (the checkpointer), the filter parameter is used to query checkpoints based on their metadata:
def list(
self,
config: RunnableConfig | None,
*,
filter: dict[str, Any] | None = None, # Used to filter by metadata
before: RunnableConfig | None = None,
limit: int | None = None,
) -> Iterator[CheckpointTuple]:
The filter parameter is passed to an internal function called _metadata_predicate, which constructs the SQL WHERE clause to query checkpoints by their metadata fields.
# process metadata query
for query_key, query_value in filter.items():
operator, param_value = _where_value(query_value)
predicates.append(
f"json_extract(CAST(metadata AS TEXT), '$.{query_key}') {operator}"
)
param_values.append(param_value)
return (predicates, param_values)
The Injection
The vulnerability exists in how _metadata_predicate handles the query_key from the filter dictionary. Notice this critical line:
f"json_extract(CAST(metadata AS TEXT), '$.{query_key}') {operator}"
An attacker-controlled filter could provide a query_key with a ' character that will escape the JSON path string and inject arbitrary SQL code.
Injection -> Arbitrary Deserialization
To understand how SQL injection leads to arbitrary deserialization, we need to see the complete picture. Here’s the SQL query that gets executed in list():
query = f"""SELECT thread_id, checkpoint_ns, checkpoint_id, parent_checkpoint_id, type, checkpoint, metadata
FROM checkpoints
{where}
ORDER BY checkpoint_id DESC"""
This query retrieves checkpoint data from the database, including the checkpoint’s BLOB column. The results are then processed:
async for (
thread_id,
checkpoint_ns,
checkpoint_id,
parent_checkpoint_id,
type,
checkpoint, # ← This comes directly from the SQL query results
metadata,
) in cur: # ← cur contains the query results
# ...
yield CheckpointTuple(
# ...
self.serde.loads_typed((type, checkpoint)), # ← Deserialization
# ...
)
The checkpoint contains serialized data, and when fetched gets deserialized.
The Attack
Using SQL injection in the WHERE clause, an attacker can inject a UNION SELECT that adds their own row to the query results:
SELECT thread_id, checkpoint_ns, checkpoint_id, parent_checkpoint_id, type, checkpoint, metadata
FROM checkpoints
WHERE ... (injected: ') UNION SELECT 'thread1', 'ns', 'checkpoint1', NULL, 'msgpack', X'', '{}' -- )
ORDER BY checkpoint_id DESC
The injected UNION SELECT returns a fake checkpoint row where the checkpoint column contains attacker-controlled serialized data. When the code loops through the query results, it deserializes this malicious checkpoint’s BLOB, giving the attacker arbitrary deserialization
JSON – The json.loads() with object_hook was discussed in our LangGrinch research, but does not lead to code execution
Msgpack – This is the one we are interested in
What is msgpack?
MessagePack (msgpack) is a binary serialization format designed to be faster and more compact than JSON. LangGraph uses ormsgpack, a Rust-based implementation with Python bindings.
Msgpack Extensions
MessagePack allows developers to define custom extension types to handle additional data types beyond its built-in primitives. LangGraph implemented its own extension handler to support serialization of custom Python objects.
This gives an attacker arbitrary code execution – by calling os.system() with attacker-controlled commands, they can execute any shell command on the server.
The Attack Chain: Combining Both Vulnerabilities
Now let’s walk through how an attacker chains these two vulnerabilities together to achieve remote code execution.
The Entry Point: When a developer exposes get_state_history(), it internally calls the checkpointer’s list() method to retrieve historical checkpoints:
def get_state_history(
self,
config: RunnableConfig,
*,
filter: Optional[Dict[str, Any]] = None,
before: Optional[RunnableConfig] = None,
limit: Optional[int] = None,
) -> Iterator[StateSnapshot]:
# ...
for checkpoint_tuple in self.checkpointer.list(config, filter=filter, before=before, limit=limit):
# Process and return checkpoint data
If the filter parameter comes from user input without sanitization, an attacker controls the dictionary keys passed to the SQL injection vulnerability.
The Attack Flow
1. Craft Malicious Payload: The attacker prepares a msgpack payload containing instructions to execute arbitrary code (e.g., run a shell command).
2. Exploit SQL Injection: The attacker sends a malicious filter parameter that exploits the SQL injection vulnerability. This injection adds a fake checkpoint row to the database query results, where the checkpoint column contains their malicious msgpack payload.
3. Trigger Deserialization: When the application processes the query results, it encounters the injected fake checkpoint and deserializes the malicious msgpack data.
4. Code Execution: The unsafe deserialization executes the attacker’s payload, giving them remote code execution on the server.
Vulnerability #3: SQL Injection in the Redis Checkpointer (CVE-2026-27022)
The same injection class affects langgraph-checkpoint-redis: user-controlled keys in the filter dictionary are interpolated directly into the query instead of bound as parameters. Preconditions match CVE-2025-67644 (the application exposes get_state_history() with a user-controlled filter and uses the Redis checkpointer). Patched in langgraph-checkpoint-redis 1.0.2.
Additional SQL Injection Findings
Beyond the primary SQL injection in the filter parameter, we identified additional defense-in-depth SQL injection issues in both the SQLite and PostgreSQL checkpointers. These involved direct concatenation of integer values (such as LIMIT and ttl parameters) into SQL queries instead of using parameterized bindings.
Since Python doesn’t enforce type hints at runtime, these parameters could still accept malicious string input. We worked with the LangChain team during disclosure to remediate these issues using parameterized queries.
Disclosure Timeline
2025-11-19: CVE-2025-67644 (SQL injection), CVE-2026-28227 (msgpack deserialization) And CVE-2026-27022 (Redis injection) disclosed to LangChain team
2025-12-10: CVE-2025-67644 fixed and publicly released in langgraph-checkpoint-sqlite 3.0.1
2026-02-20: CVE-2026-27022 fixed and publicly released in langgraph-checkpoint-redis 1.0.2
2026-03-05: CVE-2026-28277 fixed and publicly released in langgraph-checkpoint 4.0.1
Note on Vendor Response
The LangChain team responded quickly to fix the critical SQL injection vulnerability, which effectively breaks the attack chain described in this research. They continue to work methodically on additional remediation efforts, including the msgpack deserialization issue.
Additional Research
There was significant community research into LangGraph security during November and December 2025. Other security researchers independently discovered CVE-2025-67644 and CVE-2026-28277. Full credits can be found in LangChain’s security advisories.