fastjson BCEL不出网打法
BasicDataSource
如果目标服务器没有外网、无法反连,自然就用不了JdbcRowSetImpl
利用链这种JNDI注入的利用方式,而TemplatesImpl利用链虽然原理上也是利用了 ClassLoader 动态加载恶意代码,但是需要开启Feature.SupportNonPublicField
,并且实际应用中其实不多见,这里就引出BasicDataSource,我们可以直接在Payload中直接传入字节码并且不需要出网,不需要开启特殊的参数,适用范围较广,目标需要引入tomcat依赖,虽说也是一种限制,但还算是比较常见的。
环境搭建
fastjson1.2.37
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.37</version>
</dependency>
需要引入Tomcat相关的包:
BasicDataSource
类在旧版本的 tomcat-dbcp
包中,对应的路径是 org.apache.tomcat.dbcp.dbcp.BasicDataSource
。所以相关MVN 依赖写法
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/dbcp -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>dbcp</artifactId>
<version>6.0.53</version>
</dependency>
在Tomcat 8.0之后包路径有所变化,更改为了 org.apache.tomcat.dbcp.dbcp2.BasicDataSource
,所以构造PoC的时候需要注意一下,相关MVN依赖写法
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-dbcp -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-dbcp</artifactId>
<version>9.0.8</version>
</dependency>
poc
这里poc可以分成两种,JSON.parseObject()
与 parse()
,因为parseObject()
会额外的将 Java 对象转为 JSONObject
对象,即调用 JSON.toJSON()
,在处理过程中会调用所有的 setter 和 getter 方法,所以poc中少一步嵌套的操作
parse()
{
{
"x":{
"@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
"driverClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassName": "$$BCEL$$$l$8b$I$A$..."
}
}: "x"
}
PoC中的细节
两层{}
先是将 {“@type”: “org.apache.tomcat.dbcp.dbcp2.BasicDataSource”……}
这一整段放到JSON Value的位置上,之后在外面又套了一层 “{}”。之后又将 Payload 整个放到了JSON 字符串中 Key 的位置上,为什么要这样做呢。
其实看完下面的分析就能理解这是因为需要触发 BasicDataSource.getConnection()
方法。由于FastJson中的 JSON.parse()
会识别并调用目标类的 setter 方法以及某些满足特定条件的 getter 方法,然而 getConnection()
并不符合特定条件,所以正常来说在 FastJson 反序列化的过程中并不会被调用。
原PoC中很巧妙的利用了 JSONObject对象的 toString() 方法实现了突破。JSONObject是Map的子类,在执行toString() 时会将当前类转为字符串形式,会提取类中所有的Field,自然会执行相应的 getter 、is等方法。
所以在 {“@type”: “org.apache.tomcat.dbcp.dbcp2.BasicDataSource”……}
这一整段外面再套一层{},反序列化生成一个 JSONObject 对象并放在 JSON Key 的位置上,在 JSON 反序列化的时候,FastJson 会对 JSON Key 自动调用 toString() 方法,从而顺带执行了下面的东西
BCEL
BCEL的全名是Apache Commons BCEL,Apache Commons项目下的一个子项目,包含在JDK的原生库中。我们可以通过BCEL提供的两个类 Repository 和 Utility 来利用:Repository 用于将一个Java Class先转换成原生字节码,当然这里也可以直接使用javac命令来编译java文件生成字节码;Utility 用于将原生的字节码转换成BCEL格式的字节码
生成的BCEL格式大概如下:
$$BCEL$$$l$8b$I$A$A$A$A$A$A$AmQ$......
将这种格式的字符串,作为“字节码”传入new ClassLoader().loadClass(code).newInstance();
将会被实例化,当我们在Fastjson反序列化中构造出这种链,将会造成反序列化漏洞
这里的使用思路是:如果 classname 中包含 $$BCEL$$
,这个 ClassLoader 则会将$$BCEL$$
后面的字符串按照BCEL编码进行解码,作为Class的字节码,并调用 defineClass()
获取它的Class 对象。
JSON.parseObject()
{
"@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
"driverClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassName": "$$BCEL$$$l$8b......"
}
parseObject()
会额外的将 Java 对象转为 JSONObject 对象,即调用 JSON.toJSON()
,在处理过程中便会调用所有的 setter 和 getter 方法。所以对于 JSON.parseObject()
来说,直接传入构造的Payload便能触发。
断点分析
漏洞的触发点在com.alibaba.fastjson.JSON.parse
跟进handleResovleTask
跟入JSONPath.eval
跟进此处的eval后
继续跟进getPropertyValue
再跟进到JavaBeanSerializer
跟进FieldSerializer
继续跟进get,达到最终触发点method.invoke
而这里的javaObject正是BasicDataSouce,这里再看一下BasicDataSouce的构造链,直接来到org.apache.tomcat.dbcp.dbcp2.BasicDataSource
BasicDataSource.getConnection()
-> createDataSource()
-> createConnectionFactory()
这里driverClassName
和driverClassLoader
都是可控的,由用户输入,我们指定ClassLoader为com.sun.org.apache.bcel.internal.util.ClassLoader
,设置ClassName为BCEL…这种格式后,在newInstance方法执行后被实例化。因为第二个参数initial为true,Class.forName()
在动态加载类时便会进行初始化,并且这里在动态加载的过程中会执行 static 代码段,最终达到命令执行的结果。
为什么指定这个ClassLoader
简单来说它会从 classname 中提取 Class 的 bytecode 数据;
说的细点就是:如果此时 classname 中包含 $$BCEL$$
,这个 ClassLoader 便会将$$BCEL$$
后面的字符串按照BCEL编码进行解码,作为Class的字节码,并调用 defineClass()
去获取这个 Class 对象;
举个实例:将 classname 赋值为 经过BCEL编码的字节码(假设对应的类为Evil.class),我们将需要执行的代码写在 Evil.class 的 static 代码块中即可。
更多推荐
所有评论(0)