SSRF 形成的原因大都是由於伺服器端提供了從其他伺服器應用獲取資料的功能且沒有對目標地址做過濾與限制。比如從指定 URL 地址獲取網頁文字內容,載入指定地址的圖片,下載等等。這裡主要介紹java中URLConnection()
和openStream()
兩個方法產生SSRF的原理和修復方法
@RequestMapping(value = "/urlConnection/vuln", method = {RequestMethod.POST, RequestMethod.GET})
public String URLConnectionVuln(String url) {
return HttpUtils.URLConnection(url);
}
這裡呼叫的是HttpUtils.URLConnection(url)
public static String URLConnection(String url) {
try {
URL u = new URL(url);
URLConnection urlConnection = u.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); //send request
// BufferedReader in = new BufferedReader(new InputStreamReader(u.openConnection().getInputStream()));
String inputLine;
StringBuilder html = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
html.append(inputLine);
}
in.close();
return html.toString();
} catch (Exception e) {
logger.error(e.getMessage());
return e.getMessage();
}
}
跟進URLConnection
方法,而URLConnection
裡又呼叫了URL.openConnection()
來發起請求, 這個請求可以直接執行url協定(偽協定)
漏洞利用:
使用file協定讀檔案
使用http協定存取百度
修復方法:
這裡先是對url呼叫了SecurityUtil.isHttp()
來進行檢查
@GetMapping("/urlConnection/sec")
public String URLConnectionSec(String url) {
// Decline not http/https protocol
if (!SecurityUtil.isHttp(url)) {
return "[-] SSRF check failed";
}
try {
SecurityUtil.startSSRFHook();
return HttpUtils.URLConnection(url);
} catch (SSRFException | IOException e) {
return e.getMessage();
} finally {
SecurityUtil.stopSSRFHook();
}
}
SecurityUtil.isHttp()比較簡單,就是判斷url是否是以http://或https://開頭
public static boolean isHttp(String url) {
return url.startsWith("http://") || url.startsWith("https://");
}
單純的ban掉其他協定顯然是不夠的,還不能夠防止對內網進行探測,於是在獲取url內容之前,開啟了一個hook來對使用者行為進行監聽,SecurityUtil.startSSRFHook()
,就有效防止了ssrf攻擊
openStream()
方法的實現也是呼叫了 openConnection
生成一個 URLConnection
物件,然後再通過這個物件呼叫的getInputStream()
方法的
@GetMapping("/openStream")
public void openStream(@RequestParam String url, HttpServletResponse response) throws IOException {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
String downLoadImgFileName = WebUtils.getNameWithoutExtension(url) + "." + WebUtils.getFileExtension(url);
// download
response.setHeader("content-disposition", "attachment;fileName=" + downLoadImgFileName);
URL u = new URL(url);
int length;
byte[] bytes = new byte[1024];
inputStream = u.openStream(); // send request
outputStream = response.getOutputStream();
while ((length = inputStream.read(bytes)) > 0) {
outputStream.write(bytes, 0, length);
}
} catch (Exception e) {
logger.error(e.toString());
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
}
通過WebUtils.getNameWithoutExtension(url) + "." + WebUtils.getFileExtension(url)
來獲取下載檔名,然後執行inputStream = u.openStream();
來看一下openStream(),也是呼叫了openConnection()
,也會根據傳入的協定的不同來進行處理
public final InputStream openStream() throws java.io.IOException {
return openConnection().getInputStream();
}
由此可以得知,openStream()
方法同樣也可以進行ssrf來探測內網以及檔案下載,修復方案同上
關鍵詞:
URLConnection、openConnection、openStream
漏洞利用:
關於SSRF漏洞利用相關可以看這篇文章,總結的很詳細!
從一文中瞭解SSRF的各種繞過姿勢及攻擊思路