URLConnection
在java中,Java抽象出來了一個URLConnection類,它用來表示應用程序以及與URL建立通信連接的所有類的超類,通過URL類中的openConnection方法獲取到URLConnection的類對象。
Java中URLConnection支持的協議可以在sun.net.www.protocol看到。

由上圖可以看到,支持的協議有以下幾個(當前jdk版本:1.7.0_80):
file ftp mailto http https jar netdoc gopher
雖然看到有gopher,但是gopher實際在jdk8版本以后被閹割了,jdk7高版本雖然存在,但是需要設置具體可以看 https://bugzilla.redhat.com/show_bug.cgi?id=865541以及http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/rev/8067bdeb4e31 其中每個協議都有一個Handle,Handle定義了這個協議如何去打開一個連接。
我們來使用URL發起一個簡單的請求
public class URLConnectionDemo {
public static void main(String[] args) throws IOException {
URL url = new URL("https://www.baidu.com");
// 打開和url之間的連接
URLConnection connection = url.openConnection();
// 設置請求參數
connection.setRequestProperty("user-agent", "javasec");
connection.setConnectTimeout(1000);
connection.setReadTimeout(1000);
...
// 建立實際連接
connection.connect();
// 獲取響應頭字段信息列表
connection.getHeaderFields();
// 獲取URL響應
connection.getInputStream();
StringBuilder response = new StringBuilder();
BufferedReader in = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
response.append("/n").append(line);
}
System.out.print(response.toString());
}
}
大概描述一下這個過程,首先使用URL建立一個對象,調用url對象中的openConnection來獲取一個URLConnection的實例,然后通過在URLConnection設置各種請求參數以及一些配置,在使用其中的connect方法來發起請求,然后在調用getInputStream來獲請求的響應流。 這是一個基本的請求到響應的過程。
SSRF
SSRF(Server-side Request Forge, 服務端請求偽造)。 由攻擊者構造的攻擊鏈接傳給服務端執行造成的漏洞,一般用來在外網探測或攻擊內網服務。
SSRF漏洞形成的原因大部分是因為服務端提供了可以從其他服務器獲取資源的功能,然而并沒有對用戶的輸入以及發起請求的url進行過濾&限制,從而導致了ssrf的漏洞。
通常ssrf容易出現的功能點如下面幾種場景
- 抓取用戶輸入圖片的地址并且本地化存儲
- 從遠程服務器請求資源
- 對外發起網絡請求
- ……
黑客在使用ssrf漏洞的時候,大部分是用來讀取文件內容或者對內網服務端口探測,或者在域環境情況下且是win主機下進行ntlmrelay攻擊。
URL url = new URL(url);
URLConnection connection = url.openConnection();
connection.connect();
connection.getInputStream();
StringBuilder response = new StringBuilder();
BufferedReader in = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
response.append("/n").append(line);
}
System.out.print(response.toString());
比如上面代碼中的url可控,那么將url參數傳入為file:///etc/passwd
URL url = new URL("file:///etc/passwd");
URLConnection connection = url.openConnection();
connection.connect();
...
以上代碼運行以后則會讀取本地/etc/passwd文件的內容。

但是如果上述代碼中將url.openConnection()返回的對象強轉為HttpURLConnection,則會拋出如下異常
Exception in thread "main" java.lang.ClassCastException: sun.net.www.protocol.file.FileURLConnection cannot be cast to java.net.HttpURLConnection
由此看來,ssrf漏洞也對使用不同類發起的url請求也是有所區別的,如果是URLConnection|URL發起的請求,那么對于上文中所提到的所有protocol都支持,但是如果經過二次包裝或者其他的一些類發出的請求,比如
HttpURLConnection
HttpClient
Request
okhttp
……
那么只支持發起http|https協議,否則會拋出異常。
如果傳入的是http://192.168.xx.xx:80,且192.168.xx.xx的80端口存在的,則會將其網頁源碼輸出出來

但如果是非web端口的服務,則會爆出Invalid Http response 或Connection reset異常。如果能將此異常拋出來,那么就可以對內網所有服務端口進行探測。
java中默認對(http|https)做了一些事情,比如:
- 默認啟用了透明NTLM認證
- 默認跟隨跳轉
關于NTLM認證的過程這邊不在復述,大家可以看該文章《Ghidra 從 XXE 到 RCE》 默認跟隨跳轉這其中有一個坑點,就是

它會對跟隨跳轉的url進行協議判斷,所以Java的SSRF漏洞利用方式整體比較有限。
- 利用file協議讀取文件內容(僅限使用
URLConnection|URL發起的請求) - 利用http 進行內網web服務端口探測
- 利用http 進行內網非web服務端口探測(如果將異常拋出來的情況下)
- 利用http進行ntlmrelay攻擊(僅限
HttpURLConnection或者二次包裝HttpURLConnection并未復寫AuthenticationInfo方法的對象)
對于防御ssrf漏洞的攻擊,不單單要對傳入的協議進行判斷過濾,也要對其中訪問的地址進行限制過濾。
Java Web安全
推薦文章: