Skip to content
KitploitKITPLOIT
StrumentiExploitsBlog
Log in
Invia
StrumentiExploitsBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

FeedContattoPrivacy© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2026-11349 — Modern Events Calendar Lite <= 7.33.0 — SQL Injection non autenticata | Kitploit
Strumenti/GitHubGitHub/hann1bl3l3ct3r/cve-2026-11349
Analisi delle VulnerabilitàExploitSfruttamento di Applicazioni WebSicurezza WebPenetration TestingSicurezza dei Database
GitHubhann1bl3l3ct3r/cve-2026-11349

CVE-2026-11349

Modern Events Calendar Lite <= 7.33.0 — SQL Injection non autenticata

Vedi Repository
1214 mesi faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

Modern Events Calendar Lite <= 7.33.0 — Iniezione SQL non autenticata tramite mec_list_load_more (atts[include] / atts[exclude])

Riepilogo

DettaglioValore
PluginModern Events Calendar Lite
Slugmodern-events-calendar-lite
AutoreWebnus
Versioni interessate<= 7.33.0 (l'attuale release Lite distribuita dal vendor). Il bug è presente nell'intero arco post-w.org; confermato in laboratorio su 6.5.6 e 7.33.0, confermato staticamente in 5.21.2. 6.5.6 = ultima build wordpress.org (congelata alla chiusura del 2022-05-11); 7.33.0 = build attuale distribuita da mec.webnus.net
Installazioni attiveConteggio wordpress.org nascosto dalla chiusura; storicamente 100.000+. Ancora attivamente distribuito/aggiornato dal vendor (Lite tramite mec.webnus.net; lo stesso codebase 7.x è alla base del MEC Pro attivamente venduto)
CWECWE-89 (SQL Injection)
VulnerabilitàIniezione SQL cieca non autenticata (basata sul tempo / booleana / basata sugli errori)
Privilegio richiestoNessuno (wp_ajax_nopriv_* — pre-autenticazione)
Interazione utenteNessuna
CVSS v3.17.5 (Alto) — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
StatoVerificato in laboratorio end-to-end su 7.33.0 (attuale) e 6.5.6 (WordPress 6.6.5, MariaDB 10.x)
CVE / GHSACVE-2026-11349

Descrizione

Modern Events Calendar Lite registra una famiglia di azioni "load more" non autenticate su admin-ajax.php per le sue skin di elenco eventi (list, grid, masonry, agenda, timeline, tile, custom). Ogni handler legge l'array atts controllato dall'attaccante dalla richiesta, lo passa a un helper chiamato sanitize_deep_array() che — quando chiamato con i suoi argomenti predefiniti — non esegue alcuna sanitizzazione — e poi concatena i valori di atts['include'] (e atts['exclude']) grezzi in un frammento SQL post_id IN (...) eseguito con $wpdb->get_results() e senza $wpdb->prepare().

Poiché i punti di ingresso sono registrati su wp_ajax_nopriv_*, non è richiesta alcuna autenticazione, account, nonce o interazione utente. Un attaccante remoto non autenticato può iniettare SQL arbitrario nella clausola WHERE di una SELECT contro wp_mec_dates e leggere qualsiasi dato nel database WordPress (hash delle password utente, segreti/chiavi di wp_options, dati di altri plugin) tramite tecniche cieche basate sul tempo / booleane / basate sugli errori.


Causa principale

1. Un "sanitizzatore" che non sanitizza nulla sul percorso predefinito

app/libraries/main.php:9607:

public function sanitize_deep_array($inputs, $type = 'text', $excludes = array(), $path = '')
{
    if(!is_array($inputs)) return $inputs;

    $sanitized = array();
    foreach($inputs as $key => $val)
    {
        $p = $path.$key.'.';
        if((is_array($excludes) and in_array(trim($p, '. '), $excludes))
            or (is_array($excludes) and !count($excludes)))   // line 9615
        {
            $sanitized[$key] = $val;   // <-- RAW passthrough, no sanitization
            continue;
        }
        // ... (sanitize_text_field / (int) / esc_url / ... only reached when $excludes is non-empty)
    }
    return $sanitized;
}

Il controllo (is_array($excludes) and !count($excludes)) rende la funzione una completa no-op quando $excludes è l'array vuoto predefinito — ogni valore viene copiato così com'è. L'intento era evidentemente "se esiste una lista di esclusione, salta quelle chiavi"; la logica booleana salta invece tutto ogni volta che non viene fornita alcuna lista di esclusione.

2. Il chiamante non fornisce $excludes

app/skins/list.php:499-501 (load_more()):

$this->sf = (isset($_REQUEST['sf']) and is_array($_REQUEST['sf']))
    ? $this->main->sanitize_deep_array($_REQUEST['sf']) : array();
$apply_sf_date = isset($_REQUEST['apply_sf_date']) ? sanitize_text_field($_REQUEST['apply_sf_date']) : 1;
$atts = $this->sf_apply(((isset($_REQUEST['atts']) and is_array($_REQUEST['atts']))
    ? $this->main->sanitize_deep_array($_REQUEST['atts']) : array()), $this->sf, $apply_sf_date);  // line 501

sanitize_deep_array($_REQUEST['atts']) viene chiamato con un singolo argomento → $excludes assume il valore predefinito array() → il ramo no-op di cui sopra → $atts è il grezzo, non attendibile $_REQUEST['atts'].

3. Concatenazione grezza nella clausola IN (...)

app/libraries/skins.php:

// line 603 (exclude → NOT IN)
if(isset($this->atts['exclude']) and is_array($this->atts['exclude']) and count($this->atts['exclude']))
    $where_AND .= " AND `post_id` NOT IN (".implode(',', $this->atts['exclude']).")";

// line 606 (include → IN)
if(isset($this->atts['include']) and is_array($this->atts['include']) and count($this->atts['include']))
    $where_AND .= " AND `post_id` IN (".implode(',', $this->atts['include']).")";

Gli elementi dell'array vengono passati a implode() direttamente nella stringa SQL senza cast a intero e senza escaping. (absint()/(int) su ogni elemento avrebbe chiuso questa falla.)

4. Esecuzione senza prepared statement

app/libraries/db.php:79:

public function select($query, $result = 'loadObjectList')
{
    $query = $this->_prefix($query);          // only swaps `#__` for the table prefix
    $database = $this->get_DBO();
    if($result == 'loadObjectList') return $database->get_results($query, OBJECT_K);  // line 87 — no prepare()
    // ...
}

La stringa completamente costruita viene passata così com'è a $wpdb->get_results().

Flusso del taint (richiesta → sink):

$_REQUEST['atts']                                       (attacker-controlled, unauthenticated)
  → app/skins/list.php:501  sanitize_deep_array($atts)  (NO-OP: default empty $excludes)
  → MEC_skin::initialize($atts)                         ($this->atts = raw atts)
  → app/libraries/skins.php:606  "... post_id IN (".implode(',', $this->atts['include']).")"
  → app/libraries/db.php:87  $wpdb->get_results($query) (no prepare)

Raggiungibilità

app/skins/list.php:51-52:

$this->factory->action('wp_ajax_mec_list_load_more',        array($this, 'load_more'));
$this->factory->action('wp_ajax_nopriv_mec_list_load_more', array($this, 'load_more'));  // <-- unauthenticated

La registrazione nopriv rende l'endpoint raggiungibile prima dell'autenticazione. La stessa struttura di load_more() e il query builder condiviso in skins.php sono presenti nelle skin sorelle, ciascuna con la propria azione wp_ajax_nopriv_*, quindi la stessa iniezione è raggiungibile tramite una qualsiasi di:

Azione AJAX (nopriv)Handler della skin
mec_list_load_moreapp/skins/list.php:497
mec_grid_load_moreapp/skins/grid.php:497
mec_masonry_load_moreapp/skins/masonry.php:229
mec_agenda_load_moreapp/skins/agenda.php:242
mec_timeline_load_moreapp/skins/timeline.php:242
mec_tile_load_moreapp/skins/tile.php:446
mec_custom_load_moreapp/skins/custom.php:233

Nessun nonce viene verificato in load_more(), e l'azione non richiede che alcuno shortcode del plugin sia presente in una pagina: gli handler AJAX vengono registrati incondizionatamente all'init.


Prova di concetto (verificata in laboratorio, MEC Lite 6.5.6, WordPress 6.6.5, MariaDB 10.x)

Tutte le richieste sono non autenticate (nessun cookie, nessun nonce). Il valore iniettato viene inserito in atts[include][]; il payload chiude le due parentesi aperte del gruppo IN ((...) AND (... IN ( e aggiunge un OR <sleep> di livello superiore così che la condizione venga valutata per ogni riga scansionata, poi commenta i )) ORDER BY ... finali:

TARGET='https://victim.example'          # plain permalinks: use admin-ajax.php directly
Scarica lo strumento