Durante una auditoría interna del plugin All In One SEO, descubrimos una vulnerabilidad de inyección SQL y un fallo de escalada de privilegios.
Si se explotara, la vulnerabilidad de inyección SQL podría permitir a los atacantes acceder a información privilegiada de la base de datos del sitio afectado (por ejemplo, nombres de usuario y contraseñas con hash).
El fallo de escalada de privilegios que descubrimos podría permitir a actores maliciosos acceder a endpoints protegidos de REST API a los que no deberían tener acceso. En última instancia, esto podría permitir que usuarios con cuentas de pocos privilegios, como los suscriptores, ejecutaran código de forma remota en los sitios afectados.
Informamos de las vulnerabilidades por correo electrónico al autor del plugin, que recientemente ha publicado la versión 4.1.5.3 para solucionarlas. Te recomendamos encarecidamente que actualices a la última versión del plugin y que tengas una solución de seguridad establecida en tu sitio, como Jetpack Security.
Detalles
Nombre del plugin: All In One SEO
URI del plugin: https://wordpress.org/plugins/all-in-one-seo-pack/
Autor: https://aioseo.com/
Las vulnerabilidades
Escalada de privilegios para usuarios autenticados
Versiones afectadas: Todas las versiones entre la 4.0.0 y la 4.1.5.2, ambas inclusive.
ID de CVE: CVE-2021-25036
CVSSv3.1: 9.9
CWSS: 92.1
/**
* Validates access from the routes array.
*
* @since 4.0.0
*
* @param \WP_REST_Request $request The REST Request.
* @return bool True if validated, false if not.
*/
public function validateAccess( $request ) {
$route = str_replace( '/' . $this->namespace . '/', '', $request->get_route() );
$routeData = isset( $this->getRoutes()[ $request->get_method() ][ $route ] ) ? $this->getRoutes()[ $request->get_method() ][ $route ] : [];
// No direct route name, let's try the regexes.
if ( empty( $routeData ) ) {
foreach ( $this->getRoutes()[ $request->get_method() ] as $routeRegex => $routeInfo ) {
$routeRegex = str_replace( '@', '\@', $routeRegex );
if ( preg_match( "@{$routeRegex}@", $route ) ) {
$routeData = $routeInfo;
break;
}
}
}
if ( empty( $routeData['access'] ) ) {
return true;
}
// We validate with any of the access options.
if ( ! is_array( $routeData['access'] ) ) {
$routeData['access'] = [ $routeData['access'] ];
}
foreach ( $routeData['access'] as $access ) {
if ( current_user_can( $access ) ) {
return true;
}
}
if ( current_user_can( apply_filters( 'aioseo_manage_seo', 'aioseo_manage_seo' ) ) ) {
return true;
}
return false;
}
Las comprobaciones de privilegios aplicadas por All In One SEO para proteger los endpoints de REST API contenían un fallo muy sutil que podría haber permitido a usuarios con cuentas de pocos privilegios (como los suscriptores) acceder a todos y cada uno de los endpoints que registra el plugin.
El método Api::validateAccess() depende de que se solicite la ruta de REST API para saber qué comprobaciones de privilegios debe aplicar a una solicitud determinada. Como no tenía en cuenta que WordPress trata las rutas de REST API como cadenas que no distinguen entre mayúsculas y minúsculas, cambiar un único carácter a mayúscula permitiría eludir por completo la rutina de comprobación de privilegios.
Esto es especialmente preocupante porque algunos de los endpoints del plugin son bastante sensibles. Por ejemplo, el endpoint aioseo/v1/htaccess puede reescribir el archivo .htaccess de un sitio con contenido arbitrario. Un atacante podría abusar de esta función para ocultar puertas traseras de .htaccess y ejecutar código malicioso en el servidor.
Inyección de SQL para usuarios autenticados
Versiones afectadas: Todas las versiones entre la 4.1.3.1 y la 4.1.5.2, ambas inclusive.
ID de CVE: CVE-2021-25037
CVSSv3.1: 7.7
CWSS: 80.4
/**
* Searches for posts or terms by ID/name.
*
* @since 4.0.0
*
* @param \WP_REST_Request $request The REST Request
* @return \WP_REST_Response The response.
*/
public static function searchForObjects( $request ) {
$body = $request->get_json_params();
if ( empty( $body['query'] ) ) {
return new \WP_REST_Response( [
'success' => false,
'message' => 'No search term was provided.'
], 400 );
}
if ( empty( $body['type'] ) ) {
return new \WP_REST_Response( [
'success' => false,
'message' => 'No type was provided.'
], 400 );
}
$searchQuery = aioseo()->db->db->esc_like( $body['query'] );
$objects = [];
$dynamicOptions = aioseo()->dynamicOptions->noConflict();
if ( 'posts' === $body['type'] ) {
$postTypes = aioseo()->helpers->getPublicPostTypes( true );
foreach ( $postTypes as $postType ) {
// Check if post type isn't noindexed.
if ( $dynamicOptions->searchAppearance->postTypes->has( $postType ) && ! $dynamicOptions->searchAppearance->postTypes->$postType->show ) {
$postTypes = aioseo()->helpers->unsetValue( $postTypes, $postType );
}
}
$objects = aioseo()->db
->start( 'posts' )
->select( 'ID, post_type, post_title, post_name' )
->whereRaw( "( post_title LIKE '%{$searchQuery}%' OR post_name LIKE '%{$searchQuery}%' OR ID = '{$searchQuery}' )" )
->whereIn( 'post_type', $postTypes )
->whereIn( 'post_status', [ 'publish', 'draft', 'future', 'pending' ] )
->orderBy( 'post_title' )
->limit( 10 )
->run()
->result();
El método PostsTerms::searchForObjects(), accesible únicamente mediante la ruta /wp-json/aioseo/v1/objects de REST API, solo escapaba la entrada del usuario mediante wpdb::esc_like() antes de añadir dicha entrada a una consulta de SQL. Puesto que este método no está diseñado para escapar las comillas, un atacante aún podría inyectarlas y obligar a la consulta a revelar información confidencial de la base de datos, como las credenciales de los usuarios.
Aunque este endpoint no estaba pensado para que pudieran acceder a él los usuarios con cuentas de pocos privilegios, el vector de ataque de escalada de privilegios mencionado anteriormente les permitió aprovecharse de esta vulnerabilidad.
Cronología
2021-12-01 – Contacto inicial con All In One SEO
2021-12-02 – Les enviamos los detalles sobre estas vulnerabilidades
2021-12-08 – Se publica All In One SEO 4.1.5.3
Conclusión
¡Te recomendamos que compruebes qué versión del plugin All In One SEO utiliza tu sitio y, si está dentro del intervalo afectado, que lo actualices lo antes posible!
En Jetpack nos esforzamos para garantizar que tus sitios web estén protegidos frente a este tipo de vulnerabilidades. Te recomendamos que tengas un plan de seguridad para tu sitio que incluya análisis de archivos maliciosos y copias de seguridad. Jetpack Security es una excelente opción de seguridad de WordPress para garantizar la seguridad de tu sitio y de sus visitantes.
Créditos
Investigador original: Marc Montpas
Gracias al resto del equipo de Jetpack Scan por sus comentarios, ayuda y correcciones.



