Breaking the perimeter by exploiting routing-based SSRF via a misconfigured load balancer
Summary
- 1. Asset discovery
- 2. Vulnerability discovery
- 3. Vulnerability exploitation
- 4. Report resolution
- 5. Lessons learned
This bug was reported in a private program in which it is not allowed to publish the vulnerabilities found. So this is a partial disclosure, only the essential technical details are exposed.
In this post I am going to show a vulnerability that I found some years ago, in my beginnings as a bounty hunter. It is not my most original post and the final impact found was not critical, but as I am always talking about the same vulnerabilities (XSS, SQLi…), I felt like making a post about a somewhat different vulnerability, just to change up my content a bit . Besides, it’s always good to look back at bugs found some time ago to draw conclusions and think what could have been done better.
1. Asset discovery
I found this asset through amass + httpx. If you are looking for http services on subdomains of the domain example.com and you have your config file in the path /home/user/.config/amass/config.ini, you can use the following command
amass enum -brute -d example.com '/home/user/.config/amass/config.ini' | httpx -title -tech-detect -status-code -ip -p 66,80,81,443,445,457,1080,1100,1241,1352,1433,1434,1521,1944,2301,3000,3128,3306,4000,4001,4002,4100,5000,5432,5800,5801,5802,6082,6346,6347,7001,7002,8080,8443,8888,30821
Amass is an OSINT tool to perform network mapping of attack surfaces and external asset discovery which is a very famous tool used in the recon step in bug bounty. The output of the above amass command is a list of subdomains of the given domain, i.e, a list of potential targets.
Httpx is a multi-purpose HTTP toolkit allow to run multiple probers. In this case, the input of httpx is a list of subdomains and the output is a list of subdomains that have an http service in any of the ports given as a parameter. Also it shows some additional information about the service such as the title, the detected technologies… that I have specified in the parameters to be displayed.
2. Vulnerability discovery
The main page worked over HTTP and it didn’t look like a big deal. It only displayed the text It works!, as can be seen in the following evidence:
Although it is difficult to decide which part belongs to the discovery and which part belongs to the exploitation, I got the initial clue from the Burp Scanner, as can be seen in the following capture:
This alert indicates the presence of an External service interaction (DNS) in the server root. This type of alert is usually not a symptom of a vulnerability as it may be due to intentional web functionality and is usually in the Host header of the request. However, it can sometimes allow access to the internal network, as can be seen in the following PortSwigger links:
However, when I looked at the alert in detail I saw the following:
As you can see, the injection is not happening in the Host header, but in the HTTP verb path itself. This caught my attention because I had not seen it until then, so to check that it was not a false positive I sent the request again to a new instance of burp collaborator and checked that indeed several DNS requests were coming, as can be seen in the following capture:
In principle, a DNS request to an external server doesn’t mean much. If it were an HTTP request, it would be much more interesting. However, the fact that the request is made using the path and not through the Host header struck me as unusual. That’s why I decided to dig a little deeper here .
3. Vulnerability exploitation
3.1. Steps of exploitation
3.1.1. Accessing the internal network
The main risk of this typec of vulnerability is that it could allow making requests to the internal network. Because of this, I continued investigating the target to see if I could find any private IP addresses disclosed in the HTML, JavaScript, comments, or elsewhere but I found nothing. Burpsuite also didn’t flag anything interesting.
So, I decided to use a tool that I believe isn’t used much in bug bounty hunting: nikto. I executed the following command
nikto -h http://example.com/
where the -h option specifies the host.
The result was this:
This file is quite interesting because it seems to be the admin interface of a load balancer, specifically for Apache Module mod_proxy_balancer. It reveals the following info:
This is the Apache balancer manager interface or mod_proxy_balancer module. As you can see, there’s a lot of useful information here, such as potential private IPs that could be used for further exploitation. Additionally, there’s a panel to edit the load balancer’s configuration, something I didn’t test since this was a production system. However, I reported it separately and received a bounty of just over $50 for it.
To confirm whether I could exploit an SSRF, I tested all the private IPs listed. Only one yielded an interesting response, 10.80.3.122, as shown below:
WildFly Application Server is an open-source, Java-based application server developed by the JBoss Community under the Red Hat umbrella. It is part of the WildFly family and serves as a platform for building, deploying, and hosting Java EE (Enterprise Edition) applications.
The capture above shows how I was able to make a request to the internal network. This confirmed the existence of a routing-based SSRF vulnerability, likely caused by a misconfiguration in the load balancer. It’s essentially an SSRF vulnerability, but instead of being exploited via the Host header, it works through the HTTP verb’s full URL path.
While exploring further, I clicked on the Render tab to get a clearer view of all the possible options. That’s when I discovered there was an admin console available:
Inspecting the HTML code revealed that the admin console was located at the path /console.
Unfortunately, I didn’t save the request in the burp project, but when I tried to access it, I got a response like
You don’t have permission to access this resource
Damn!
3.1.2. Scanning the internal network
As you can see, all the IPs found belong to the Class A network (10.X.X.X or 10.0.0.0/8) and appear to be part of the same subnet (10.80.3.X or 10.80.3.0/24). The recon I could have performed in a situation like this is absolutely crazy:
- Available IPs.
- Open ports and running services.
- Specific recon for each service, such as identifying routes and files on HTTP services like the one I found.
Although I conducted some recon across these categories, I honestly don’t think I went as deep as I could have, mainly for 3 reasons:
- This bug was one of my first findings as a bug bounty hunter.
- Since I was directly interacting with the internal network, I was concerned about being detected by the company’s internal security measures, such as firewalls, IDSs, IDPs… so I chose not to make too much noise.
- The internal network wasn’t explicitly in the program’s scope. While it’s unlikely I would have faced severe consequences, probing too deeply into the internal network might have cjeopardized the bounty or even led to warnings or penalties.
Even so, I did a lot of recon through Burpsuite intruder varying IPs, ports, paths… but it would be unfeasible to show all that recon in this post. An example of this recon is the following port scan I performed on the same IP where the WildFly service was running, the IP 10.80.3.122:
Here is some interesting behavior:
- HTTP codes 200, 301 and 502. After exploring the responses I came to the conclusion that these responses may correspond to open ports. It is not clear that all the ports associated with this response are open ports but it seems that any open port falls into this category. For example, port 22 has an SSH service on version SSH-2.0-OpenSSH_5.3.
- HTTP code 503. It seems to be the default HTTP code for closed port and is the code returned by all other ports.
In conclusion, I demonstrated that it is possible to run a port scan on the internal network. Specifically, the result shows that IP 10.80.3.122 has potentially open ports 22, 111, 1556, 1556, 3237, 3528, 7777, 8009, 8080, 9990, 9999, 10000, 10050, 13782, 36174, 54186 and 55422.
So since reporting something like
Exposure of an internal *WildFly service*
felt too specific and challenging to explain in terms of impact, I decided to approach the issue from a broader and more impactful perspective. I reported it as
Routing-based SSRF on the balancer allows access to and scanning of the internal network
3.2. Why does this vulnerability exist?
In summary, the fundamental reason is that the Apache Balancer is either misconfigured or running with its default configuration. This vulnerability could be broken down into two misconfigurations:
-
Exposed Apache balancer manager interface. The Apache balancer manager interface is exposed to the internet when its access should be restricted through authentication or limited to internal network or VPN access. This could be mitigated by adding a directive like the following to the Apache configuration:
<Location "/balancer-manager"> Require ip 10.0.0.0/8 Require all denied </Location>
This directive allows access to the /balancer-manager path only from IP addresses in the 10.0.0.0/8 range.
-
Incorrect routing to internal assets. The balancer allows requests to be made to both external and internal network assets. This could be due to misconfiguration of directives like ProxyRequests, ProxyPass or ProxyPassReverse in the Apache configuration. For instance, when ProxyRequests is enabled, as shown in the following directive
ProxyRequests On
Apache allows the use of full URLs and acts as an open proxy.
These are just theories but align with the observed behavior of the vulnerability.
4. Report resolution
As I said before, the subdomain was not important so it was classified as low criticity. In addition, although I was not able to demonstrate a significant impact, the simple fact of gaining access to the internal network was enough for them to classify it as a vulnerability of high severity because of the consequences that it could have in the future if they decide to add other important assets to the network. Therefore, the report was classified as
- Asset criticity: Low
- Vulnerability severity: High
- Bounty: More than $100
5. Lessons learned
- Pay attention to all subdomains, even those that may not seem important, because they can still impact other subdomains or key assets. In terms of CVSS, you could say that all subdomains should be considered, since the Scope component of the vulnerability might be Changed. For example, in this case I gained access to the internal network through a subdomain with no apparent functionality, where potentially critical assets for the company could be located.
- Don’t hesitate to dive deeper into recon when necessary. In this case, I did quite a bit of recon, but since I was just starting out as a bug bounty hunter, I probably didn’t do as much as I should have. There was also the risk of being detected during exploitation, as I was interacting with internal network assets, which could lead to the vulnerability being patched before I had a chance to report it. Still, I will never know if I would have found anything important if I had done a more thorough recon.
- Sometimes, while searching for or exploiting a vulnerability, you may stumble upon others that you can report separately. In this case, the main vulnerability was an SSRF, but along the way I found an exposed load balancer console that allowed modifying its configuration. I considered these to be two separate vulnerabilities with different impacts, even though the second helped me exploit the first, and I ended up getting two separate bounties. I don’t think this is common, but it’s always worth reporting both vulnerabilities properly and giving it a try.
- Think carefully about how you want to approach your report. In this case, I didn’t find the impact I was hoping for, which was gaining access to one of the internal assets. However, instead of focusing on what I couldn’t do, I decided to focus on what I could do and explain that clearly. So, I reported that I could perform scans on the internal network, rather than a failed attempt to access an admin console. I didn’t get a critical, okay, but I did get a high.