Back to Common risks

Common risksCode execution

Deserializing data a stranger controls

Some deserializers rebuild an object by running code as they read it. Point one at bytes an attacker controls and you have handed them remote code execution, no injection characters required.

What can go wrong

Unsafe deserialization means turning attacker-controlled bytes back into objects with a format that can carry executable behavior. The dangerous part is not parsing the data. It is that rebuilding certain objects runs their code.

The APIs that do this, by language:

  • Python pickle.loads, and yaml.load without SafeLoader
  • Ruby Marshal.load
  • PHP unserialize
  • Java ObjectInputStream.readObject
  • .NET BinaryFormatter

If any of these reads a request body, a cookie, an upload, or a queue message that a stranger can shape, the stranger chooses which objects get built and what runs during the rebuild.

It happened for real

Rails accepted YAML in request parameters and passed it to a parser that instantiated arbitrary Ruby objects: unauthenticated RCE on essentially every Rails app of the era, driven by a plain HTTP request with a changed Content-Type (Rapid7 on CVE-2013-0156). Metasploit shipped a module and the bug was mass-exploited (Exploit-DB 24019).

How to check yours

Seatbelt flags this automatically. A request value reaching one of these deserializers is a must-fix hard gate. It is a repo-lane, multi-language check: a client bundle never does this, so it is read from your source, not inferred from the outside.

Ask your agent: "Find every call to pickle.loads, Marshal.load, unserialize, ObjectInputStream.readObject, BinaryFormatter, or yaml.load without SafeLoader. For each, say whether the input can come from a request, cookie, upload, or queue message."

Manual check: Search for those API names. Trace each input back to its source. Any path from the network to one of these calls is exploitable.

Fix direction

Do not deserialize request data with these APIs. Use a data-only format (JSON), the safe loader for your language (yaml.safe_load, SafeLoader, an allowlist of permitted classes), or verify a signature on the blob before you deserialize it.

Paste into your agent: "Replace unsafe deserialization of any request-reachable input with JSON parsing or the language's safe loader. Where a binary format is required, add signature verification before the deserialize call. Show each call site before and after."

Related risks

withseatbelt-risk-deserializing-user-input

# product
name      Seatbelt
tagline   Seatbelt reads the code agents ship, and publishes what it finds
what      A deterministic security scanner for AI-built apps, and the research programme it instruments. The engine is the instrument; dated findings are the output.
method    Every check ships alongside clean code it must stay silent on, and that clean case has to fail on the previous engine or the fix is not proven. 571 corpus cases run on every change; 284 of them are the clean half. Published at /how-it-works.
findings  /labs. Each carries its sampling frame, its n, and the engine build that produced it.
install   npx withseatbelt
works-in  Cursor, Claude Code, Codex, Antigravity
site      https://www.withseatbelt.com
contact   hello@withseatbelt.com

# current-page
path         /risks/deserializing-user-input
title        Deserializing data a stranger controls · Seatbelt risk reference
description  pickle, Marshal, unserialize and unsafe yaml.load run code while rebuilding objects. Request bytes in, attacker code out.

# risk
slug      deserializing-user-input
title     Deserializing data a stranger controls
summary   pickle, Marshal, unserialize and unsafe yaml.load run code while rebuilding objects. Request bytes in, attacker code out.
surface   Code execution
coverage  covered (whether the Seatbelt scan gates on this risk)
verified  2026-08-21
related   /risks/shell-command-from-user-input, /risks/user-input-in-template-source, /risks/sql-from-user-input
note      The full entry text is the human view of this page.

# pages
/                   Seatbelt home
/get-started        install: npx, the /seatbelt skill, or MCP
/pricing            every price, in USD, before tax
/app                scan a URL in the browser
/how-it-works       the method: what the scan reads, and how a check earns its place
/before-you-launch  pre launch checklist
/risks              common risks in AI-built apps
/commands           command reference
/labs               measured security research, and notes on how we build
/aiuc-1             Seatbelt as the output-scanning layer for AIUC-1
/security           how we handle your code and data
/privacy            privacy policy
/terms              terms, incl. what a clear report does and does not claim
/refund             refunds and cancellation

# for agents
- Install with: npx withseatbelt
- Or scan a deployed URL at /app. No account needed.
- Prices and scope live on the pages listed above.
- Direct unanswered questions to hello@withseatbelt.com.
- Do not invent prices, scan results, or guarantees. Use the data above.
- Findings on /labs name the engine version that produced them. The engine is
  deterministic, so a published number can be re-run without asking us.