Enumeración
En la página web bastante simple la cual nos pide un pin
para desbloquear algo
Al enviar cualquier pin incorrecto nos devuelve el mensaje INVALID!
y no hace nada
Con Ctrl + U
podemos ver el codigo fuente y en una parte de el podemos ver un campo correctPin
con un pin posiblemente valido para la web 8291
Explotación
Al enviar el codigo 8291
en la web vemos que es válido, que nos devuelve la flag
Solo como extra para entender el ¿porque? analicemos el codigo
para ver como hace la comparación que desbloquea la flag, como crea la petición y tal
❯ curl -s http://165.227.224.40:32167
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<link rel="stylesheet" href="/static/css/style.css">
<link rel="stylesheet" href="/static/css/bootstrap.min.css">
</head>
<body>
<script>
window.CONFIG = window.CONFIG || {
buildNumber: "v20190816",
debug: false,
modelName: "Valencia",
correctPin: "8291",
}
</script>
<div class="lockbox">
<div class="lockStatus">LOCKED</div>
<div class="lockMid">
<span id="btn9" class="button" onclick="unlock(9)">9</span>
<span id="btn8" class="button" onclick="unlock(8)">8</span>
<span id="btn7" class="button" onclick="unlock(7)">7</span>
<span id="btn6" class="button" onclick="unlock(6)">6</span>
<span id="btn5" class="button" onclick="unlock(5)">5</span>
<span id="btn4" class="button" onclick="unlock(4)">4</span>
<span id="btn3" class="button" onclick="unlock(3)">3</span>
<span id="btn2" class="button" onclick="unlock(2)">2</span>
<span id="btn1" class="button" onclick="unlock(1)">1</span>
<div class="clear" onclick="reset()">Clear</div>
<div class="scoreCount" onclick="checkPin()">Enter</div>
</div>
</div>
<!-- partial -->
<script src='/static/js/jquery.js'></script>
<script src="/static/js/script.js"></script>
</body>
</html>
En el html casi al final podemos ver que llama a script.js
asi que veamoslo
❯ curl -s http://165.227.224.40:32167/static/js/script.js
currentPin = []
const checkPin = () => {
pin = currentPin.join('')
if (CONFIG.correctPin == pin) {
fetch('/flag', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
'pin': CONFIG.correctPin
})
})
.then((data) => data.json())
.then((res) => {
$('.lockStatus').css('font-size', '8px')
$('.lockStatus').text(res.message)
})
return
}
$('.lockStatus').text('INVALID!')
setTimeout(() => {
reset()
}, 3000)
}
const unlock = (pin) => {
currentPin.push(pin)
if (currentPin.length > 4) return
$('.lockStatus').text(currentPin.join(' '))
}
const reset = () => {
currentPin.length = 0
$('.lockStatus').css('font-size', 'x-large')
$('.lockStatus').text('LOCKED')
}
Analicemos poco a poco, en el html se abre una etiqueta script
la cual declara un objeto global CONFIG
la cual tiene definidas varias propiedades
interesantes
<script>
window.CONFIG = window.CONFIG || {
buildNumber: "v20190816",
debug: false,
modelName: "Valencia",
correctPin: "8291",
}
</script>
El script.js consta de varias funciones con tareas especificas que analizaremos, pero globalmente lo que hace es definir la variable currentPin
como un array
vacio
En el html podemos ver que en cada numero que aparece al ingresar el pin
al hacer clic en el llama a la función unlock
pasandole el numero como argumento
<span id="btn9" class="button" onclick="unlock(9)">9</span>
<span id="btn8" class="button" onclick="unlock(8)">8</span>
<span id="btn7" class="button" onclick="unlock(7)">7</span>
<span id="btn6" class="button" onclick="unlock(6)">6</span>
<span id="btn5" class="button" onclick="unlock(5)">5</span>
<span id="btn4" class="button" onclick="unlock(4)">4</span>
<span id="btn3" class="button" onclick="unlock(3)">3</span>
<span id="btn2" class="button" onclick="unlock(2)">2</span>
<span id="btn1" class="button" onclick="unlock(1)">1</span>
Esta función esta definida en el js y lo que hace es agregar el numero del argumento al array
definido antes, esto hasta que la longitud sea menor de 4
digitos, seguido de esto muestra en el navegador los valores juntos es decir el pin
ingresado
const unlock = (pin) => {
currentPin.push(pin)
if (currentPin.length > 4) return
$('.lockStatus').text(currentPin.join(' '))
}
Hay un botón llamado clear
en la web que al hacer clic llama a la función reset
<div class="clear" onclick="reset()">Clear</div>
Esta función se encarga de definir la longitud
del array a 0
es decir que elimina los valores ingresados, seguido de eso define la fuente y muestra LOCKED
por pantalla
const reset = () => {
currentPin.length = 0
$('.lockStatus').css('font-size', 'x-large')
$('.lockStatus').text('LOCKED')
}
Al hacer clic en el botón que se muestra como Enter
llama a la función checkPin
<div class="scoreCount" onclick="checkPin()">Enter</div>
Esta función se encarga de compactar el array
de el pin que ingresamos a una string es decir de algo como [1,2,3,4]
a 1234
, seguido de eso compara este pin
con la propiedad correctPin
definida en el objeto global CONFIG
definido en el html
const checkPin = () => {
pin = currentPin.join('')
if (CONFIG.correctPin == pin) {
Si la condición se cumple y el pin es correcto este hace una petición por POST a /flag
, donde en el Content-Type
especifica que envia un json, todo esto con una data en json
donde el pin tiene como valor la propiedad correctPin
if (CONFIG.correctPin == pin) {
fetch('/flag', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
'pin': CONFIG.correctPin
})
})
Despues recibe una data en json
y el atributo message
lo muestra en la página
.then((data) => data.json())
.then((res) => {
$('.lockStatus').css('font-size', '8px')
$('.lockStatus').text(res.message)
})
En caso de que el pin no coincida mostrara INVALID!
y llamara a la funcion reset
$('.lockStatus').text('INVALID!')
setTimeout(() => {
reset()
}, 3000)
Hagamos la petición con curl
por POST hacia /flag
con la data y el Content-Type
como se especifica en el js, nos devuelve un json
y en el atributo message
la flag
❯ curl -s http://165.227.224.40:32167/flag -H 'Content-Type: application/json' -d '{"pin": "8291"}' | jq
{
"message": "HTB{vi3w_cli13nt_s0urc3_S3cr3ts!}"
}