HTTP Parameter Pollution

26 de marzo de 2025


¿Qué es Server-Side Parameter Pollution (SSPP)?

La contaminación de parámetros en el lado del servidor (Server-Side Parameter Pollution, SSPP), o también conocida como HTTP Parameter Pollution es una vulnerabilidad de seguridad web que ocurre cuando una aplicación web incorpora datos proporcionados por el usuario en una solicitud a una API interna sin aplicar una validación o codificación adecuada. Esta falla permite que un atacante manipule los parámetros de la solicitud para alterar la funcionalidad prevista del sistema. Dependiendo de la implementación de la aplicación, la explotación de esta vulnerabilidad puede permitir la sobrescritura de valores existentes, la inyección de nuevos parámetros no autorizados o la modificación del comportamiento interno de la aplicación. En algunos casos, los atacantes pueden aprovechar SSPP para eludir restricciones de seguridad, acceder a información sensible o incluso escalar privilegios dentro del sistema afectado.

Este tipo de ataque se puede manifestar de diversas maneras, dependiendo de cómo la aplicación maneje los parámetros. Por ejemplo, los parámetros pueden ser parte de la URL, del cuerpo de la solicitud en métodos como POST, o incluso en los encabezados de la solicitud HTTP. Los sistemas más susceptibles a esta vulnerabilidad son aquellos que no realizan una validación adecuada de los parámetros o que no codifican correctamente los datos antes de insertarlos en solicitudes internas que son procesadas por la API del servidor.

En un escenario típico, la aplicación web recibe datos del usuario, como un nombre de usuario o una búsqueda, y luego utiliza esos datos en solicitudes internas para recuperar información de una base de datos o de otros servicios. Sin embargo, si los parámetros no se sanitizan correctamente, un atacante puede manipularlos para alterar la solicitud interna. Esto puede generar varios efectos no deseados, tales como la exposición de datos privados, la modificación del comportamiento de la aplicación o incluso la ejecución de comandos no autorizados.

La gravedad de este tipo de vulnerabilidad varía dependiendo de cómo la aplicación maneja estos parámetros maliciosos y de los controles de seguridad que tenga implementados. En algunos casos, un atacante podría modificar los parámetros para acceder a datos a los que no deberían tener acceso, o incluso realizar acciones como cambiar configuraciones críticas, todo sin necesidad de contar con privilegios especiales.

¿Dónde puede ocurrir Server-Side Parameter Pollution?

  1. Query String (Cadena de consulta en la URL):
    Muchas aplicaciones web reciben parámetros a través de la query string, como ?parametro=valor. Estos parámetros son enviados al servidor y luego utilizados para ejecutar diversas operaciones, como búsquedas o acceso a recursos específicos. Un atacante puede modificar estos parámetros o agregar nuevos para alterar el comportamiento de la aplicación.

  2. Cuerpo de la solicitud (POST, PUT):
    Los formularios web y las solicitudes POST o PUT suelen enviar datos en el cuerpo de la solicitud, que pueden contener parámetros que se procesan de manera similar a los de la query string. Si estos datos no son validados correctamente, los atacantes pueden inyectar parámetros adicionales para manipular el flujo de la aplicación (Derivando en lo que se conoce como Mass Assignment Attack).

  3. Cabeceras HTTP:
    Las cabeceras HTTP también pueden ser manipuladas por el atacante para inyectar parámetros maliciosos. Los servidores que procesan las solicitudes HTTP sin una validación adecuada pueden ser vulnerables a este tipo de ataque.

  4. Rutas en APIs REST:
    En las aplicaciones que utilizan APIs REST, los parámetros pueden ser incluidos directamente en la ruta de la URL. Un atacante puede manipular estos parámetros en la ruta para acceder a recursos no autorizados o modificar la lógica de la API.

  5. Datos estructurados (JSON, XML):
    Las aplicaciones modernas a menudo comunican datos en formatos estructurados como JSON o XML. Si estos datos no se validan adecuadamente antes de ser procesados en el servidor, un atacante podría inyectar parámetros maliciosos dentro de los datos estructurados, lo que podría tener consecuencias graves, como la escalada de privilegios o la exposición de información sensible.

Prueba de Concepto

Para este ejemplo se supondrán dos servicios PHP, el primero de ellos estará expuesto por el puerto 80 y será accesible desde cualquier red. Habrá una API interna que se ejecutará en el puerto 8000, únicamente accesible desde la interfaz loopback (127.0.0.1).

En archivo index.php se define una función que recibe un parámetro llamado nombre y devuelve un JSON con información acerca de este usuario. Ahora bien, se definirá de forma explícita que no se deben mostrar los detalles del usuario, si no información que se considera pública. Imaginemos que también está implementado un sistema de autenticación y, en caso de que lo estuviera, si se permitiría ver la información detallada. El código de este archivo principal sería el siguiente:

<?php

if (isset($_GET['name'])) {
    $name = $_GET['name'];
    $name = urldecode($_GET['name']);
    $request_detailed = false;

    $apiUrl = "http://localhost:8000/api.php?name=" . $name . "&detailed=" . ($request_detailed ? "true" : "false");
    $response = file_get_contents($apiUrl);

    echo "<h1>API Response</h1>";
    echo "<pre>" . htmlspecialchars($response) . "</pre>";

} else {
    echo "<h1>Please enter a name as a parameter in the URL.</h1>";
}

?>

En el archivo api.php están unas estrucutas JSON hardcodeadas, pero en realidad se esperaría que se consultara una base de datos o un servicio de terceros para obtener la información.

<?php

if (isset($_GET['name'])) {
    $name = $_GET['name'];

    $detailed = isset($_GET['detailed']) && $_GET['detailed'] === "true";
    $users = [
        "peter" => [
            "name" => "Peter Parker",
            "email" => "peter.parker@example.com",
            "phone" => "+1 555-1234",
            "subscription" => "Premium",
            "username" => "peter",
        ],
        "john" => [
            "name" => "John Doe",
            "email" => "john.doe@example.com",
            "phone" => "+1 555-5678",
            "subscription" => "Standard",
            "username" => "john",
        ],
    ];

    if (isset($users[$name])) {
        $user = $users[$name];

        if ($detailed) {
            $response = [
                "username" => $user["username"],
                "name" => $user["name"],
                "email" => $user["email"],
                "phone" => $user["phone"],
                "subscription" => $user["subscription"],
                "status" => "success",
                "name" => $_GET['name']
            ];
        } else {

            $response = [
                "username" => $user["username"],
                "name" => $user["name"],
                "status" => "success",
                "name" => $_GET['name']
            ];
        }

        echo json_encode($response);
    } else {
        echo json_encode(["message" => "User not found.", "status" => "error", "name" => $_GET['name']]);
    }
} else {
    echo json_encode(["message" => "Name not provided.", "status" => "error", "name" => $_GET['name']]);
}
?>

Para desplegar los servicios, suponiendo cada script se encuentra en un directorio distinto:

# Para index.php
php -S 0.0.0.0:80
# Para api.php	
php -S localhost:8000

El funcionamiento normal sería el siguiente:

Como se está haciendo una llamada a la API con la función file_get_contents(), se podrían añadir más parámetros dentro del array de $_GET['name'], pero en este caso, no sería suficiente ya que PHP solo tiene en cuenta el último valor que se le asigna a la variable. El formato de los caracteres especiales tiene que estar en URL encode para que se almacene en la posición del array esperada y así que se asigne al valor de $name. Por ejemplo, al introducir http://localhost/?name=john%26detailed=true, se obtendría el siguiente resultado:

Sin embargo, se podría tratar de comentar el resto de la query, utilizando el caracter # en formato URL encode.

En este caso, la respuesta es exitosa, ya que la asignación de detailed=false se ignora.

Puedes descargar en el código fuente desde este ejemplo en el siguiente repositorio de Github: https://github.com/rubbxalc/parameter-pollution-php-poc