Critical
Server-Side / Tool Backend
Insecure Deserialization → RCE (YAML/pickle)
A tool parses attacker-supplied YAML/pickle/Java-serialized data with an unsafe loader. The YAML tag `!!python/object/apply` calls any Python callable → RCE.
Remote Code Execution
LLM05ASI05T11CWE-502CWE-94
Tells spot it before you commit
- » A tool ingests user-supplied YAML/pickle/Java-serialized/JSON-with-types.
- » The system prompt says "allow unsafe tags", or errors mention Python objects.
- » Single-line chat input - so payloads must use YAML flow style, not block style.
Method
- Confirm execution with a benign command (
id; env). - If the tool hides values (prints only keys/"success"), force the value into an ERROR to print it.
- Also probe other unsafe deserializers -
pickle.loads, JavareadObject, RubyMarshal, .NETBinaryFormatter.
Payloads templates - adapt to authorized scope
output: !!python/object/apply:subprocess.check_output [["sh","-c","id; env; true"]]
!!python/object/apply:int [!!python/object/apply:subprocess.check_output [["sh","-c","cat /flag* 2>/dev/null; env | grep -iE 'flag|ctf|secret'; true"]]]
Why it works
yaml.load (not safe_load) constructs arbitrary Python objects from tags, so
!!python/object/apply:<callable> executes any function. The trust boundary is the
parser, and an unsafe loader has none - attacker data becomes attacker code.
Impact
Remote code execution in the tool backend: read secrets/flags, pivot internally, and exfiltrate env/argv (see secrets-in-argv-env).
Defenses
- Use
yaml.safe_load/ safe deserializers only; never load untrusted pickle/Java/Ruby/.NET blobs. - Schema-validate parsed structures; run tool backends least-privilege and sandboxed.
- Reject type/tag directives in user-supplied serialized data.
Mappings
OWASP LLM
LLM05 - Improper Output Handling
OWASP ASI
ASI05 - Unexpected Code Execution
Agentic Threats
T11 - Unexpected RCE & Code Attacks
CWE
CWE-502 - Deserialization of Untrusted DataCWE-94 - Improper Control of Generation of Code (Code Injection)