java反序列化-URLDNS鏈
ysoserial
ysoserial是一個可以生成反序列化payload的工具,它可以讓使用者根據自己選擇的利用鏈,生成反序列化利用資料,通過將這些資料傳送給目標,從而執行使用者預先定義的命令,用法:
點選檢視程式碼
$ java -jar ysoserial.jar Y SO SERIAL? Usage: java -jar ysoserial.jar [payload] '[command]' Available payload types: Payload Authors Dependencies ------- ------- ------------ AspectJWeaver @Jang aspectjweaver:1.9.2, commons-collections:3.2.2 BeanShell1 @pwntester, @cschneider4711 bsh:2.0b5 C3P0 @mbechler c3p0:0.9.5.2, mchange-commons-java:0.2.11 Click1 @artsploit click-nodeps:2.3.0, javax.servlet-api:3.1.0 Clojure @JackOfMostTrades clojure:1.8.0 CommonsBeanutils1 @frohoff commons-beanutils:1.9.2, commons-collections:3.1, commons-logging:1.2 CommonsCollections1 @frohoff commons-collections:3.1 CommonsCollections2 @frohoff commons-collections4:4.0 CommonsCollections3 @frohoff commons-collections:3.1 CommonsCollections4 @frohoff commons-collections4:4.0 CommonsCollections5 @matthias_kaiser, @jasinner commons-collections:3.1 CommonsCollections6 @matthias_kaiser commons-collections:3.1 CommonsCollections7 @scristalli, @hanyrax, @EdoardoVignati commons-collections:3.1 FileUpload1 @mbechler commons-fileupload:1.3.1, commons-io:2.4 Groovy1 @frohoff groovy:2.3.9 Hibernate1 @mbechler Hibernate2 @mbechler JBossInterceptors1 @matthias_kaiser javassist:3.12.1.GA, jboss-interceptor-core:2.0.0.Final, cdi-api:1.0-SP1, javax.interceptor-api:3.1, jboss-interceptor-spi:2.0.0.Final, slf4j-api:1.7.21 JRMPClient @mbechler JRMPListener @mbechler JSON1 @mbechler json-lib:jar:jdk15:2.4, spring-aop:4.1.4.RELEASE, aopalliance:1.0, commons-logging:1.2, commons-lang:2.6, ezmorph:1.0.6, commons-beanutils:1.9.2, spring-core:4.1.4.RELEASE, commons-collections:3.1 JavassistWeld1 @matthias_kaiser javassist:3.12.1.GA, weld-core:1.1.33.Final, cdi-api:1.0-SP1, javax.interceptor-api:3.1, jboss-interceptor-spi:2.0.0.Final, slf4j-api:1.7.21 Jdk7u21 @frohoff Jython1 @pwntester, @cschneider4711 jython-standalone:2.5.2 MozillaRhino1 @matthias_kaiser js:1.7R2 MozillaRhino2 @_tint0 js:1.7R2 Myfaces1 @mbechler Myfaces2 @mbechler ROME @mbechler rome:1.0 Spring1 @frohoff spring-core:4.1.4.RELEASE, spring-beans:4.1.4.RELEASE Spring2 @mbechler spring-core:4.1.4.RELEASE, spring-aop:4.1.4.RELEASE, aopalliance:1.0, commons-logging:1.2 URLDNS @gebl Vaadin1 @kai_ullrich vaadin-server:7.7.14, vaadin-shared:7.7.14 Wicket1 @jacob-baines wicket-util:6.23.0, slf4j-api:1.6.4
因為該專案裡面就包含了URLDNS的payload生成類,可以用該專案除錯分析URLDNS鏈
URLDNS
URLDNS鏈是一條檢測鏈,因為不能“利用”的原因,他只能進行一次DNS請求,並不能執行命令,但因為其如下的優點,非常適合我們在檢測反序列化漏洞時使用:
- 使用Java內建的類構造,對第三方庫沒有依賴
- 在目標沒有回顯的時候,能夠通過DNS請求得知是否存在反序列化漏洞
通過ysoserial專案分析URLDNS呼叫鏈
下載原始碼,然後用Intellij IDEA開啟,等待maven依賴載入完畢(需要一段時間),找到pom.xml檔案,找到專案入口主類GeneratePayload.java
然後找到ysoserial.payloads.URLDNS.java類,該類可以生成這條鏈的payload,首先在註釋就可以看到利用鏈
然後看到getObject方法,ysoserial會調⽤用這個方法獲得 Payload,這個方法返回的是一個物件,這個物件就是最後將被序列化的物件,在這里是HashMap
而HashMap也就是該鏈的關鍵,因為實現了Serializable介面,有readObject方法,看到HashMap的readObject方法
點選檢視程式碼
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { ObjectInputStream.GetField fields = s.readFields(); // Read loadFactor (ignore threshold) float lf = fields.get("loadFactor", 0.75f); if (lf <= 0 || Float.isNaN(lf)) throw new InvalidObjectException("Illegal load factor: " + lf); lf = Math.min(Math.max(0.25f, lf), 4.0f); HashMap.UnsafeHolder.putLoadFactor(this, lf); reinitialize(); 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) { // use defaults } else if (mappings > 0) { 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); } } }
這裡呼叫的hash方法
繼續跟入發現又呼叫的key物件的hashCode方法
URLDNS 中使用的這個key是一個java.net.URL物件,看看其hashCode 方法發現又呼叫了handler的hashCode方法:
繼續跟進:
再跟進getHostAddress方法:
發現是URL類裡的getHostAddress方法的呼叫,再跟進一步:
InetAddress.getByName(host)的作用是根據主機名,獲取其IP地址,在網路上其實就是一次DNS查詢,這也就是這條鏈的觸發點
因此這條鏈跟到這裡就結束了,呼叫鏈如下:
1. HashMap->readObject()
2. HashMap->hash()
3. URL->hashCode()
4. URLStreamHandler->hashCode()
5. URLStreamHandler->getHostAddress()
6. InetAddress->getByName()
實際測驗
編寫如下程式碼:
點選檢視程式碼
import java.net.URL;
import java.util.HashMap;
public class Demo{
public static void main(String[] args) throws Exception {
HashMap<URL, String> hashMap = new HashMap<URL, String>();
URL url = new URL("http://yiaho.ygzip5.dnslog.cn");//dnslog.cn平臺新建連結
url.hashCode();
}
}
首先進入hashCode函式,呼叫的URLStreamHandler裡的hashCode函式
然後來到URLStreamHandler類的hashCode函式呼叫getHostAddress函式
連續跟進,最後再URL類的getHostAddress函式下呼叫了InetAddress.getByName
此時檢視dnslog就會看到一次訪問記錄
參考文章:p神的java安全漫談