Enumeración
Además de un host se nos proporciona un zip
que al descomprimirlo nos deja 2 archivos, lo que parece ser un binario compilado para linux y su codigo en C
❯ ls
test test.c
❯ ./test
Enter payload here: test
❯ cat test.c
#include <stdio.h>
#include <stdlib.h>
/*
This is not the challenge, just a template to answer the questions.
To get the flag, answer the questions.
There is no bug in the questionnaire.
*/
void gg(){
system("cat flag.txt");
}
void vuln(){
char buffer[0x20] = {0};
fprintf(stdout, "\nEnter payload here: ");
fgets(buffer, 0x100, stdin);
}
void main(){
vuln();
}
0x1
Al conectarnos con netcat
al host y puerto que se nos proporciona un texto explicando (que he omitido) y después nos pregunta si el binario es de 32
o 64
bits
❯ netcat 46.101.98.159 34192
This is a simple questionnaire to get started with the basics.
<----snip---->
[*] Question number 0x1:
Is this a '32-bit' or '64-bit' ELF? (e.g. 1337-bit)
>>
Con el comando file
podemos ver que es un ejecutable de linux para 64
bits
❯ file test
test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=5a83587fbda6ad7b1aeee2d59f027a882bf2a429, for GNU/Linux 3.2.0, not stripped
Enviamos la respuesta como se nos muestra en la pregunta y nos dice que es correcta
[*] Question number 0x1:
Is this a '32-bit' or '64-bit' ELF? (e.g. 1337-bit)
>> 64-bit
♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠
♠ ♠
♠ Correct ♠
♠ ♠
♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠
0x2
Nos devuelve una nueva pregunta, es si el linked
del binario es dinamyc
o static
[*] Question number 0x2:
What's the linking of the binary? (e.g. static, dynamic)
>>
En el output del comando file
, nos dice que es dinamyc
asi que lo enviamos
[*] Question number 0x2:
What's the linking of the binary? (e.g. static, dynamic)
>> dynamic
♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠
♠ ♠
♠ Correct ♠
♠ ♠
♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠
0x3
La siguiente pregunta que nos dan es si es un binario stripped
o not stripped
[*] Question number 0x3:
Is the binary 'stripped' or 'not stripped'?
>>
Nuevamente el output
anterior del comando file
nos da la respuesta, la enviamos
[*] Question number 0x3:
Is the binary 'stripped' or 'not stripped'?
>> not stripped
♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠
♠ ♠
♠ Correct ♠
♠ ♠
♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠
0x4
La siguiente nos pregunta que protecciones
tiene y nos muestra algunos ejemplos
[*] Question number 0x4:
Which protections are enabled (Canary, NX, PIE, Fortify)?
>>
Esto podemos verlo con checksec
, el binario tiene todas las protecciones desactivadas excepto NX
, No Execute
que evita la ejecución de shellcode
❯ checksec test
[*] '/home/kali/test'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Enviamos la respuesta con la unica proteccion, nos devuelve que es correcta
[*] Question number 0x4:
Which protections are enabled (Canary, NX, PIE, Fortify)?
>> NX
♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠
♠ ♠
♠ Correct ♠
♠ ♠
♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠
0x5
Nos muestra otro texto y nos lleva a la pregunta 5 la cual es el nombre de la función personalizada qu es vulnerable que se llama desde la función principal main()
<----snip---->
[*] Question number 0x5:
What is the name of the custom function that gets called inside `main()`? (e.g. vulnerable_function())
>>
Si leemos el codigo .c podemos ver que el nombre la función que se llama es vuln()
Enviamos el nombre de la función y nuevamente se nos devuelve que es correcta
[*] Question number 0x5:
What is the name of the custom function that gets called inside `main()`? (e.g. vulnerable_function())
>> vuln()
♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠
♠ ♠
♠ Correct ♠
♠ ♠
♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠
0x6
Ahora se nos pide ya sea en hex o decimal el valor del tamaño del buffer
definido
[*] Question number 0x6:
What is the size of the 'buffer' (in hex or decimal)?
>>
En el codigo c vemos que el tamaño es 0x20
o 32
bytes si lo pasamos a decimal
void vuln(){
char buffer[0x20] = {0};
fprintf(stdout, "\nEnter payload here: ");
fgets(buffer, 0x100, stdin);
}
Podemos enviar los valores 0x20
o 32
ambos deberian devolvernos que es correcto
[*] Question number 0x6:
What is the size of the 'buffer' (in hex or decimal)?
>> 0x20
♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠
♠ ♠
♠ Correct ♠
♠ ♠
♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠
0x7
Ahora se nos pregunta cual es la función que nunca se llama nos muestra el ejemplo
[*] Question number 0x7:
Which custom function is never called? (e.g. vuln())
>>
En el codigo c podemos ver que podemos ver que esta es la función llamada gg()
void gg(){
system("cat flag.txt");
}
Enviamos el nombre de la función como respuesta y nos devuelve que es correcta
[*] Question number 0x7:
Which custom function is never called? (e.g. vuln())
>> gg()
♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠
♠ ♠
♠ Correct ♠
♠ ♠
♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠
0x8
Ahora nos pregunta cual es la funcion estándar que es vulnerable a Buffer Overflow
[*] Question number 0x8:
What is the name of the standard function that could trigger a Buffer Overflow? (e.g. fprintf())
>>
La función vulnerable se encuentra en vuln() es fgets
aunque hay otros como strcpy
void vuln(){
char buffer[0x20] = {0};
fprintf(stdout, "\nEnter payload here: ");
fgets(buffer, 0x100, stdin);
}
Enviamos el nombre de la función como el ejemplo y nos devuelve que es correcta
[*] Question number 0x8:
What is the name of the standard function that could trigger a Buffer Overflow? (e.g. fprintf())
>> fgets()
♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠
♠ ♠
♠ Correct ♠
♠ ♠
♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠
0x9
Ahora se nos pregunta la cantidad de bytes necesarios para corromper el programa
[*] Question number 0x9:
Insert 30, then 39, then 40 'A's in the program and see the output.
After how many bytes a Segmentation Fault occurs (in hex or decimal)?
>>
Con ayuda de gdb
con pwndbg
instalado podemos crear un patron de 50
caracteres especialmente diseñados usando la función cyclic
, una vez creado corremos el programa pasandole el input cuando lo pida y el programa corrompe
❯ gdb -q test
Reading symbols from test...
(No debugging symbols found in test)
pwndbg> cyclic 50
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaaga
pwndbg> run
Starting program: /home/kali/test
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Enter payload here: aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaaga
Program received signal SIGSEGV, Segmentation fault.
0x00000000004011f9 in vuln ()
pwndbg>
Ahora buscamos el contenido de la dirección rsp
y con cyclic -l
buscamos el offset
que nos dice son 40
bytes antes de que corrompa el programa
pwndbg> x/gx $rsp
0x7fffffffe578: 0x6161616161616166
pwndbg> cyclic -l 0x6161616161616166
Finding cyclic pattern of 8 bytes: b'faaaaaaa' (hex: 0x6661616161616161)
Found at offset 40
pwndbg>
Simplemente enviamos como respuesta la cantidad de bytes, nos devuelve correcto
[*] Question number 0x9:
Insert 30, then 39, then 40 'A's in the program and see the output.
After how many bytes a Segmentation Fault occurs (in hex or decimal)?
>> 40
♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠
♠ ♠
♠ Correct ♠
♠ ♠
♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠
0xa
Para la pregunta final se nos pregunta el valor de la dirección de la función gg()
[*] Question number 0xa:
What is the address of 'gg()' in hex? (e.g. 0x401337)
>>
Nuevamente abrimos gdb
y con la funcion propia print
function podemos apuntar a la función gg
y nos muestra su dirección que tiene el valor 0x401176
❯ gdb -q test
Reading symbols from test...
(No debugging symbols found in test)
pwndbg> print gg
$1 = {<text variable, no debug info>} 0x401176 <gg>
pwndbg>
Al enviar la direccion nos dice que es correcto y finalmente se nos devuelve la flag
[*] Question number 0xa:
What is the address of 'gg()' in hex? (e.g. 0x401337)
>> 0x401176
♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠
♠ ♠
♠ Correct ♠
♠ ♠
♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠ ♠
Great job! It's high time you solved your first challenge! Here is the flag!
HTB{l34rn_th3_b451c5_b3f0r4_u_5t4rt}