← Back to all posts
Code Auditing5 min read

Self-Studying Java Code Auditing: From Web Crawlers to SSRF Vulnerabilities

This article documents a self-directed approach to learning Java code auditing, beginning with building a Java GUI-based web crawler and progressing to an examination of SSRF vulnerabilities, exploitation scenarios, limitations, and remediation.

Read in Chinese→

0x00 Prerequisites

This is mainly a record of my approach to learning Java code auditing. Since I have mostly been studying on my own, my learning has been somewhat isolated and heavily reliant on searching Baidu. Nevertheless, I wanted to document and share the process so that I can summarize and reflect on it later. If I ultimately manage to learn something, I will reorganize my thoughts and provide a useful learning path for other self-learners. That is why I created the following series of articles on self-studying Java code auditing.

This article came about because I had just reinvented the wheel while learning how to write Java GUIs. I built a tool and wanted to add a web crawler. A small bug occurred along the way, so I examined the network request component. I then realized that I could also investigate SSRF vulnerabilities while I was at it.

0x01 SSRF Vulnerabilities

SSRF (Server-Side Request Forgery) allows an attacker to make a server initiate specified requests. SSRF attacks generally target internal systems that cannot be accessed from the external network.

There is no need to say much more about this, so I will just copy the basics here.

SSRF in Java differs somewhat from SSRF in PHP: fewer protocols are supported, and some protocols, such as the gopher protocol, are restricted. Therefore, Java SSRF is generally less damaging than PHP SSRF.

The features where SSRF commonly occurs are essentially the same as in PHP:

They are basically places that initiate URL requests:

  1. Parameters containing keywords such as share, url, link, src, source, target, display, 3g, target, domain, or u

  2. Loading content through a URL

  3. Downloading images or bookmarking articles

  4. Sharing articles through a URL

0x02 Detailed Introduction

I initially came across a crawler written using URLConnection, but later found that this was a relatively primitive approach. It essentially sends the request directly and receives the returned byte stream without performing any processing. Everything must be handled manually afterward. For example, even the charset has to be detected manually, because some pages use GBK while others use UTF-8. It is quite tedious to write.

Method One: Requests Initiated via URLConnection

The supported protocols include:

file ftp mailto http https jar netdoc gopher

Note:

gopher was effectively removed after JDK 8, so Java SSRF is no longer as powerful as SSRF in PHP.

First, let us look at a simple crawler:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;

class URLConnectionDemo {

    public static void main(String[] args) throws IOException {

        URL url = new URL("https://www.baidu.com");
        // 打开和url之间的连接
        URLConnection connection = url.openConnection();
        // 设置请求参数 键-通过该请求是已知的(例如,“关键字Accept ”)
        connection.setRequestProperty("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36");
        //设置指定的超时值
        connection.setConnectTimeout(1000);
        //如果是写爬虫的话,最后加上,要不一直抛出异常重试,实测如果写特别影响效率,尤其是全网乱爬
        //将读超时设置为指定的超时,以毫秒为单位
        connection.setReadTimeout(1000);
        // 建立实际连接
        connection.connect();

        // 获取响应头字段信息列表
        connection.getHeaderFields();
        System.out.println(connection.getHeaderFields());

        StringBuilder response = new StringBuilder();
        //获取流到缓冲区
        BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        String line;


        while ((line = in.readLine()) != null) {
            response.append(line);
        }

        System.out.print(response.toString());
    }
}
  1. Create a URL object and call openConnection to obtain a URLConnection instance.

  2. Set the various request parameters and other configuration options.

  3. Use its connect method to initiate the request, and use getInputStream to obtain the response stream.

  4. Finally, receive and process the response manually.

Vulnerability

If the URL is controllable, an SSRF vulnerability exists.

If the following statement is present, the gopher protocol cannot be used because an exception will be thrown and execution will be interrupted before the connection request is initiated:

con.setRequestMethod("GET");

Exploitation

When the URL is controllable, the file protocol can be used to read etc/passwd:

URL url = new URL("file:///etc/passwd");

image-20200731140914542

Method Two: Restricted HTTP Requests

There are also encapsulated HTTP request libraries. These generally impose restrictions or directly throw an exception when switching protocols, so they are less useful.

They can probably only be used for internal network probing.

HttpClient.execute
HttpClient.executeMethod
HttpURLConnection.connect
HttpURLConnection.getInputStream
URL.openStream
Request.Get(url).execute()

HttpClients vulnerability example:

I later switched to this approach for my crawler. It seems to offer good compatibility.

String url = request.getParameter("url");
CloseableHttpClient client = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url);
//发起请求
HttpResponse httpResponse = client.execute(httpGet);

0x03 Remediation

  • Restrict the protocol to HTTP and HTTPS.

  • Prevent URLs from pointing to internal IP addresses, or configure a URL allowlist.

Regarding 302 redirects, Java follows redirects by default, but protocol restrictions apply to redirected requests. Protocols such as gopher do not work, so applying the restrictions above should be sufficient.

0x04 Summary

Overall, SSRF vulnerabilities in Java are relatively restricted. Also, it seems that developers rarely use native implementations and more commonly use encapsulated libraries.

In most cases, the available options are probably limited to:

  • Using the file protocol to read arbitrary files (limited to approaches such as URLConnection)
  • Using the HTTP protocol for port scanning
  • Using HTTP to perform NTLM relay attacks (you can research this yourselves…)

References

Ling Tian Laboratory – Yuanzhang’s GitHub repository javaweb-sec

https://joychou.org/java/javassrf.html