Java中SSRF可以使用到的伪协议

  1. file ftp mailto http https jar netdoc

URLConnection

漏洞代码

  1. public static String URLConnection(String url) {
  2. try {
  3. URL u = new URL(url);
  4. URLConnection urlConnection = u.openConnection();
  5. BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); //send request
  6. String inputLine;
  7. StringBuilder html = new StringBuilder();
  8. while ((inputLine = in.readLine()) != null) {
  9. html.append(inputLine);
  10. }
  11. in.close();
  12. return html.toString();
  13. } catch (Exception e) {
  14. logger.error(e.getMessage());
  15. return e.getMessage();
  16. }
  17. }

传入url进入调试

实例化一个URL对象,使用URLConnection建立连接,因为这里没有对Url的协议进行限制,所以基本上所有的伪协议都支持。

🍬SSRF漏洞 - 图1

继续进行,已经确定是file协议,然后路径也有了,这里是读取内容,getInputStream获取字节流,然后InputStreamReader()将字节流转化成字符流,BufferedReader()将字符流以缓存形式输出的方式来快速获取网络数据流。

随后循环读取每一行数据,加入到html变量中,最后输出

🍬SSRF漏洞 - 图2

逻辑简单,下面看如何修复的,或者是说怎么进行过滤

首先对协议进行了一个过滤

  1. public static boolean isHttp(String url) {
  2. return url.startsWith("http://") || url.startsWith("https://");
  3. }

这里起码是可以防止文件读取的

🍬SSRF漏洞 - 图3

继续看后面,其实现在只能使用http协议或者是https协议了,为什么后面还需要继续,先看代码

  1. try {
  2. SecurityUtil.startSSRFHook();
  3. return HttpUtils.URLConnection(url);
  4. } catch (SSRFException | IOException e) {
  5. return e.getMessage();
  6. } finally {
  7. SecurityUtil.stopSSRFHook();

开启了SSRFHook,这个是什么作用?其实跟到最底层,发现他是一个布尔类型的变量标志而已

🍬SSRF漏洞 - 图4

在startSSRFHook中默认传入的是true,因为在不进行设置的时候,代码中的isHook写死的是false

具体用这个来干什么,继续向下看

设置好之后就进行HttpUtils.URLConnection(url);

由于是http协议,所以调用HttpURLConnection类去创建socket。就会调用createSocketImpl方法,具体调用栈如下

  1. createSocketImpl:69, SocketHookFactory (org.joychou.security.ssrf)
  2. setImpl:498, Socket (java.net)
  3. <init>:84, Socket (java.net)
  4. createSocket:197, NetworkClient (sun.net)
  5. doConnect:162, NetworkClient (sun.net)
  6. openServer:463, HttpClient (sun.net.www.http)
  7. openServer:558, HttpClient (sun.net.www.http)
  8. <init>:242, HttpClient (sun.net.www.http)
  9. New:339, HttpClient (sun.net.www.http)
  10. New:357, HttpClient (sun.net.www.http)
  11. getNewHttpClient:1220, HttpURLConnection (sun.net.www.protocol.http)
  12. plainConnect0:1156, HttpURLConnection (sun.net.www.protocol.http)
  13. plainConnect:1050, HttpURLConnection (sun.net.www.protocol.http)
  14. connect:984, HttpURLConnection (sun.net.www.protocol.http)
  15. getInputStream0:1564, HttpURLConnection (sun.net.www.protocol.http)
  16. getInputStream:1492, HttpURLConnection (sun.net.www.protocol.http)
  17. URLConnection:96, HttpUtils (org.joychou.util)
  18. URLConnectionSec:58, SSRF (org.joychou.controller)

具体看这个方法的逻辑

🍬SSRF漏洞 - 图5

这里用到了前面说的isHook,需要进入这里的判断逻辑,相反如果进不去,那么将设置isHook的去掉,再跟一下,到同样的位置

🍬SSRF漏洞 - 图6

对于http协议来说,加不加无所谓,最后都会走到这一步,调一下就会发现最终是一模一样。其实hook的真实作用是开启检查,像IP黑名单这样的检查。在配置文件中都有写。这个分析放到后面。

🍬SSRF漏洞 - 图7

将两步结合成一步的过滤方式就是直接调用HttpURLContection这样就只能使用http或者https协议,使用其他协议直接返回状态码500

🍬SSRF漏洞 - 图8

然后再来分析检查url的部分,也就是开启isHook后会执行的一些操作。

🍬SSRF漏洞 - 图9

以及在SSRFChecker类中的一些过滤

🍬SSRF漏洞 - 图10

Request

两种请求方式的execute()为污点汇聚处,属于Request类的SSRF

  1. public static String request(String url) {
  2. try {
  3. //return Request.Post(url).execute().returnContent().toString();
  4. return Request.Get(url).execute().returnContent().toString();
  5. } catch (Exception e) {
  6. return e.getMessage();
  7. }
  8. }

httpClient

  1. 污点汇聚:client.execute()
  1. public static String httpClient(String url) {
  2. StringBuilder result = new StringBuilder();
  3. try {
  4. CloseableHttpClient client = HttpClients.createDefault();
  5. HttpGet httpGet = new HttpGet(url);
  6. // set redirect enable false
  7. // httpGet.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build());
  8. HttpResponse httpResponse = client.execute(httpGet); // send request
  9. BufferedReader rd = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent()));
  10. String line;
  11. while ((line = rd.readLine()) != null) {
  12. result.append(line);
  13. }
  14. return result.toString();
  15. } catch (Exception e) {
  16. return e.getMessage();
  17. }
  18. }
  1. 污点汇聚client.executeMethod()
  1. public static String commonHttpClient(String url) {
  2. HttpClient client = new HttpClient();
  3. GetMethod method = new GetMethod(url);
  4. try {
  5. client.executeMethod(method); // send request
  6. byte[] resBody = method.getResponseBody();
  7. return new String(resBody);
  8. } catch (IOException e) {
  9. return "Error: " + e.getMessage();
  10. } finally {
  11. // Release the connection.
  12. method.releaseConnection();
  13. }
  14. }

ImageIO

污点汇聚函数:ImageIO.read()

  1. public static void imageIO(String url) {
  2. try {
  3. URL u = new URL(url);
  4. ImageIO.read(u); // send request
  5. } catch (IOException e) {
  6. logger.error(e.getMessage());
  7. }
  8. }

openStream

污点汇聚:URL.openStream()

  1. public void openStream(@RequestParam String url, HttpServletResponse response) throws IOException {
  2. InputStream inputStream = null;
  3. OutputStream outputStream = null;
  4. try {
  5. String downLoadImgFileName = WebUtils.getNameWithoutExtension(url) + "." + WebUtils.getFileExtension(url);
  6. // download
  7. response.setHeader("content-disposition", "attachment;fileName=" + downLoadImgFileName);
  8. URL u = new URL(url);
  9. int length;
  10. byte[] bytes = new byte[1024];
  11. inputStream = u.openStream(); // send request
  12. outputStream = response.getOutputStream();
  13. while ((length = inputStream.read(bytes)) > 0) {
  14. outputStream.write(bytes, 0, length);
  15. }
  16. } catch (Exception e) {
  17. logger.error(e.toString());
  18. } finally {
  19. if (inputStream != null) {
  20. inputStream.close();
  21. }
  22. if (outputStream != null) {
  23. outputStream.close();
  24. }
  25. }
  26. }

okhttp

污点汇聚:execute()

  1. public static String okhttp(String url) throws IOException {
  2. OkHttpClient client = new OkHttpClient();
  3. // client.setFollowRedirects(false);
  4. com.squareup.okhttp.Request ok_http = new com.squareup.okhttp.Request.Builder().url(url).build();
  5. return client.newCall(ok_http).execute().body().string();
  6. }

Jsoup

  1. public static String Jsoup(String url) {
  2. try {
  3. Document doc = Jsoup.connect(url)
  4. //.followRedirects(false)
  5. .timeout(3000)
  6. .cookie("name", "joychou") // request cookies
  7. .execute().parse();
  8. return doc.outerHtml();
  9. } catch (IOException e) {
  10. return e.getMessage();
  11. }
  12. }

IOUtils

  1. public static void IOUtils(String url) {
  2. try {
  3. IOUtils.toByteArray(URI.create(url));
  4. } catch (IOException e) {
  5. logger.error(e.getMessage());
  6. }
  7. }

HttpAsyncClients

  1. public static String HttpAsyncClients(String url) {
  2. CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault();
  3. try {
  4. httpclient.start();
  5. final HttpGet request = new HttpGet(url);
  6. Future<HttpResponse> future = httpclient.execute(request, null);
  7. HttpResponse response = future.get(6000, TimeUnit.MILLISECONDS);
  8. return EntityUtils.toString(response.getEntity());
  9. } catch (Exception e) {
  10. return e.getMessage();
  11. } finally {
  12. try {
  13. httpclient.close();
  14. } catch (Exception e) {
  15. logger.error(e.getMessage());
  16. }
  17. }
  18. }

总结

最常见的污点汇聚的方法

  1. HttpURLConnection. getInputStream
  2. URLConnection. getInputStream
  3. Request.Get. execute
  4. Request.Post. execute
  5. URL.openStream
  6. ImageIO.read
  7. OkHttpClient.newCall.execute
  8. HttpClients.execute
  9. HttpClient.execute
  10. Jsoup.execute
  11. ...

在审计此类漏洞时,需要着重关注这些方法。