What is the SSRF vulnerability (Server Side Request Forgery)?

A large part of web applications allows you to upload your own file to the server by providing the URL address, where it will be automatically downloaded to the server. In this article, we will discuss what problems may arise from such a  solution.

The article will be based on a simple functionality in the web application, i.e., we have a form in which the user can enter the URL to an external resource. Then the application downloads itself and displays some feedback (e.g., file content or information regarding the file was not able to be downloaded for some reason). Example in Figure 1. Although it may seem that most of the real applications are excluded from displaying errors, it is still a surprisingly common problem (also in the case of applications written in Java), which allows you to know a bit more information about the process of uploading files.

Figure 1. Example functionality in the application

Let’s start with the simplest mistake that can be made with this type of functionality. It applies to the majority of mechanisms that allow downloading files from external sources, regardless of the programming language, so it can be found in PHP, Python, Java, etc. This is about the addresses leading to files on the local hard disk, i.e., file:///etc/passwd. If the application does not protect in any way against this type of request and allows the content of the downloaded file to be displayed, it is “easy-win” from the attacker’s site because it gains susceptibility to downloading any files from the disk on which application is hosted. Depending on the technology used, other URL schemes may also be available to achieve the same effect. In the case of Java, for example, files from the disk can also be downloaded via the URL: netdoc://etc/passwd.

This is why the basic method of securing against such attacks is to check whether the URL provided by the user begins with http: or https: (possibly even ftp:). However, the possible security problems do not end there.

A frequently encountered model of implementing web applications is placing the application server in the local network, and the server available from the Internet is only a reverse-proxy role and redirects the request sent to it to the server in the LAN. It is often assumed (incorrectly) that burglars trying to attack from external networks have no access to hosts in the LAN, and many services are available with easy passwords or without any additional authentication. An example of a popular service that until recently had no security at the application layer is Solr.

Thus, if the web application does not provide additional security against referring to any URLs, the malicious user can use this to scan the local network. Let’s see an example in Figure 2. The user tried to refer to port 12345 on localhost and received the “Connection refused” message in response. No errors are displayed for the open port.

Figure 2. “Connection refused” message when attempting to access a closed port

Of course, not every application displays such detailed errors. In such situations, the attackers must rely on other factors to determine the openness of the port, e.g., on the response time (response time to the port filtered on the firewall may be significantly longer than the response to the open port).

Typical filters

It is possible to distinguish several typical methods when it comes to attempts to protect against such attacks, i.e., before scanning the local network.

Most often are entered the blacklists of URL values that are not allowed. For example, if you wanted to prevent localhost from being scanned, the webapp creator might have thought about rejecting requests for URLs starting with:

  • http://localhost
  • http://127.0.0.1

Unfortunately, this protection is inadequate. We can get around them in at least several ways:

1.The application’s author predicted only that the beginning of the URL cannot start with localhost/127.0.0.1. He forgot, however, that in the URLs it is possible to define the user’s name, so you can refer to the same resources, for example: http://name@localhost.

2. The local host IP address, 127.0.0.1, can be saved in many other equivalent ways. Here are examples:

  • Each part of the IP address can be saved in hexadecimal or octal form. Examples: 0177.0.0.1, 0x7f.0.0.0×01,0177.0×01.0.1 – all these entries are equivalent to 127.0.0.1 (Figure 3).
  • The IP address can be saved in numerical form. For address 127.0.0.1 this form is 2130706433 (and so http://2130706433/points to http://127.0.0.1/)
  • Some parts of the IP address may be omitted when they are equal to 0. Record 127.1 is therefore possible.
  • Finally, you can combine all of the above methods, e.g. 0x7f.1.
Fig. 3. Proof that the record 0177.0×0.0.0×1 is equivalent to 127.0.0.1

3. An attacker can run his or her own DNS server on the network, or use one of the existing servers to configure any domain, so that after the solution it points to 127.0.0.1. There is also a generally available domain, vcap.me, in which any subdomain you choose always points to 127.0.0.1. This can be verified, for example, with the address test.vcap.me.

The above examples clearly indicate that preparing a filter in the application that protects against access to unwanted resources is very difficult because attackers can in various ways write a URL indicating de facto the same resource. Hence the conclusion that the only truly effective method of protection against local network scanning is setting appropriate rules on firewalls. The host from which requests will be made should be blocked from making outgoing calls to other machines in the LAN.

Another frequently used filter by the creators of web applications is checking the file extension. Often, in such cases, there are no restrictions when it comes to hosts that we can refer to. However, the creator of the web application recognizes that if a given functionality is used to download images, it checks whether the URL points to the image file. Unfortunately, it usually does not go hand in hand with checking the contents of the file, i.e., any file is downloaded if it has the correct extension, regardless of its content.

The most common way to implement this type of filter is simply to check the last characters in the URL. If the URL ends, for example, with “.png”, we consider it to point to an image file. Such protection is, however, very easy to circumvent because just add the appropriate extension of the file after the question mark or hash. This means that according to the URL filter implemented in this way: “http://localhost/tajny-zasob/?.Png”, is a picture.

To protect against such attacks, you must parse the URL and check the actual file name. In addition, you should also check the contents of the downloaded file, i.e., do not rely solely on its extension, and verify whether the file which we expect to be a picture is, in fact, that. Typically, you can use built-in libraries for the most popular programming languages. Of course, this type of check should be carried out not only for images but also for other types of files that are expected by our application.

Summary

Server-Side Request Forgery (SSRF) vulnerability allows attackers from the Internet to scan or download resources from the local area network. Protecting against these types of attacks is not easy. First of all, do not allow the downloading of resources from schemes other than http/https. In addition, it is best to have a special host in the local network from which queries to external servers will be executed, and in the firewall configuration to prevent this host from making outgoing calls to other hosts in the local network.

If you expect a file of a particular type, you should check its extension by parsing the URL and verifying the contents of the file (e.g., a library used to process these types of files).