Are you looking for effective ways to secure your Spring applications? Look no further! Our very own Dr. Ing. Max Maass has created a 3-part series on Spring Actuator Security, covering everything from identifying vulnerabilities to implementing best practices. Join Max in part three of this series, as he delves into the world of Spring Actuators and learn how to keep your applications safe and secure.
In the previous article, we discussed how you can leverage static code analysis using semgrep to detect misconfigured Spring Actuators. However, you may not always have access to the source code of the application: maybe you want to know if you have exposed actuators somewhere in your corporate network. Or maybe you are a bug bounty hunter and want to check if there are juicy actuators that you can get your hands on.
In any case, being able to find a URL to access and point at and say "look, there's an exposed actuator right there, do something about it" can be a much better call to action than an abstract "I found this weird thing in your code, can you take a look?". So, let's talk about catching actuators in the act.
We will be using the vulnerable demo application I provide on GitHub as an example for the rest of this blog post. It provides the actuators endpoint on
http://localhost:8081/actuator. If you want to follow along at home, now is a good time to set it up.
Part 3: Finding Exposed Actuators using Dynamic Testing with ffuf
- Exploiting Public Actuators
- Exposing the Environment
3.1 Reading Logs
3.2 Changing Log Configuration
- Closing Notes
1. Finding Actuators Using Your Browser
The most basic approach for finding exposed actuators is to do so by hand. Trivially, you can simply visit a website running Spring and try to access
/actuator/ to see what happens. If actuators are enabled and the default path wasn't changed, it will show you the list of active endpoints. Easy.
However, it is fairly easy to miss something with this approach. Maybe the actuators are living under a different base path. Or maybe the actuators are running on a different port. Clearly, this approach has its limitations. So, like any good engineer, let's start looking at how this process can be improved.
2. Automatically Querying Many URLs for Actuators
The first thing we may want to do is to automate the process of checking these URLs. For this, I prefer to use the swiss army knife of request generators, ffuf. Written in Go, it is a lightning fast tool for sending lots of HTTP requests based on patterns you provide, and filtering the responses to show you only those you are interested in.
To demonstrate a simple example: Here's how we can check our demo application at
http://localhost:8081/ using ffuf:
In the first command, we create a file containing the string "actuator" on a single line. With the second command, we invoke ffuf, tell it to use the word list "endpoints.txt" and alias it to PATH, and to generate and query one URL for each line in the endpoints.txt file, replacing PATH with the line from the file. The output looks like this:
ffuf tells us that by templating the value "actuator" into the URL, it received a response with the HTTP response code 200, with a size of 1758 bytes. This does not guarantee that actuators are actually running there, but it's a good first indicator that we should be taking a closer look.
Now, sometimes system operators try to be clever and move the actuator endpoint to a different URL - maybe they integrate them into an existing URL scheme, or maybe they are just sick of people scraping their systems (cough) and want to make it a bit harder to find. This is where ffuf can start to really help us. For example, let's say we have a list of commonly used alternative actuator base URLs - for example, by going through public code repositories and looking at how they are using the
management.endpoints.web.base-path setting. We may end up with something like this:
Given this list, we can easily check if any of these endpoints is available on the server using ffuf, using the same command as above. For example, if we change the actuator base path in the demo application to
internal and run the wordlist against it, we get:
ffuf just generated all the requests for us, and only returned those that returned an HTTP status code that indicated something interesting (by default:
200,204,301,302,307,401,403,405,500, as shown above).
3. Scanning many pages
So far, we have only scanned a single page - but maybe your engagement covers a large number of (sub)domains, and you want to check all of them. Or you collected a list of subdomains for Bug Bounty hunting using a tool like amass. In any case, let's assume that the (sub)domains are in a file called domains.txt.
ffuf will help you scan all combinations of domains and endpoints quite easily:
Here, ffuf will combine all domains from
domains.txt with all endpoints listed in
endpoints.txt, and request them all. Note that the number of requests can explode quite quickly, since it means you are sending
len(endpoints) * len(domains)requests.
However, it also reveals a potential issue: Since ffuf will only filter based on response code at the moment, you will likely get quite a number of false positives. But we don't want to waste our time checking hundreds of false positives! Can't we do anything to narrow it down a bit more?
Actually, we can.
3.1. Anatomy of the Actuators Endpoint
First, let's take a look at what the actuator response looks like. With all actuators enabled, accessing
/actuator on the application results in a response like this:
If we change the configuration to only enable the health check actuator (the default), the result is shorter:
And even if we disable all actuator endpoints (by removing the "include" setting and configuring
management.endpoints.web.exposure.exclude="health"), we still get a response on
/actuator that looks like this:
(While this response does not indicate any direct danger, it can still be interesting in a black-box engagement, as it tells you that the application is running Spring.)
Looking at these responses, we can see that the response will always start with the
_links key in the JSON, which in turn will always start with the
self key. So, this seems like a good indicator to use when building our automation.
3.2. Filtering Responses with ffuf
With this knowledge, we can start using the filtering functionality of ffuf. Briefly, you can tell it that a response must contain specific strings (using matchers), or inversely, cannot contain them (using filters). Using the knowledge about actuators, let's write a matcher that requires the response to contain the string
With this command, ffuf will only return responses that contain this substring, significantly cutting down on false positive results. You can also refine this further using additional matchers and filters, if you find that you are still getting too many incorrect responses. While this will likely not be necessary for this use case, in other situations, being able to filter based on response size or HTTP response code could be the more fruitful approach.
6. Other Options, and Why You Should Still Use ffuf
Well, the existing Nuclei and ZAP rules are quite limited - they will not find actuators that are located on a different endpoint than /actuator (in the case of Nuclei), or only check for the /actuator/health endpoint (in the case of ZAP). So, you will likely miss a lot of potential targets when using them.
More importantly, I prefer to understand how my workflows are working and how I can tweak them. With ffuf, I have full control over the requests and can craft them however I need them. Add an authentication header (or a header required by the bug bounty program)? Sure, let's just add
-H "Authorization: Bearer ... to the call. Or a cookie?
-b "CookieName=value CookieName2=value2". Record the responses you receive?
ffuf is my swiss army knife for creating lots of HTTP requests, and I believe that it deserves a place in the toolbox of any security tester dealing with HTTP - regardless of if you want to use it for fuzzing, enumeration, or recon. This article only scratches the surface, and does not even mention features like recursion, or using extensionsfor your wordlists. If you want to know more, I recommend the epic tome of knowledge aptly named "Everything you need to know about ffuf" by Codingo and p4fg as a starting point.
This concludes the third article in my series on Spring Actuators. In the next and final article of the series, I will switch perspectives to the defender and discuss how you can secure an application that is using actuators.
Once again, I would like to thank the Security Community at my employer, iteratec, for their helpful input on this issue.
Max Maas – Security Expert at iteratec. I break your software before other people do, and then help you secure it afterwards :)
Dieser Blogbeitrag erschien ursprünglich auf: https://blog.maass.xyz/spring-actuator-security-part-3-finding-exposed-actuators-using-dynamic-testing-with-ffuf