Gravi vulnerabilità risolte in All In One SEO Plugin Versione 4.1.5.3

Pubblicato: 2021-12-15

Durante un audit interno del plug-in All In One SEO, abbiamo scoperto una vulnerabilità di SQL Injection e un bug di escalation dei privilegi.

Se sfruttata, la vulnerabilità di SQL Injection potrebbe concedere agli aggressori l'accesso a informazioni privilegiate dal database del sito interessato (ad es. nomi utente e password con hash).

Il bug dell'escalation dei privilegi che abbiamo scoperto potrebbe concedere ai malintenzionati l'accesso a endpoint API REST protetti a cui non dovrebbero avere accesso. Ciò potrebbe in definitiva consentire agli utenti con account con privilegi limitati, come gli abbonati, di eseguire l'esecuzione di codice in remoto sui siti interessati.

Abbiamo segnalato le vulnerabilità all'autore del plug-in via e-mail e di recente hanno rilasciato la versione 4.1.5.3 per risolverle. Ti consigliamo vivamente di aggiornare all'ultima versione del plug-in e di disporre di una soluzione di sicurezza consolidata sul tuo sito, come Jetpack Security.

Dettagli

Nome del plugin: All In One SEO
URI del plug-in: https://wordpress.org/plugins/all-in-one-seo-pack/
Autore: https://aioseo.com/

Le vulnerabilità

Escalation dei privilegi autenticati

Versioni interessate: tutte le versioni comprese tra 4.0.0 e 4.1.5.2 incluse.
ID CVE: CVE-2021-25036
CVSSv3.1: 9.9
CCSS: 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;
	}

I controlli dei privilegi applicati da All In One SEO per proteggere gli endpoint dell'API REST contenevano un bug molto sottile che avrebbe potuto garantire agli utenti con account con privilegi bassi (come gli abbonati) l'accesso a ogni singolo endpoint registrato dal plug-in.

Il metodo Api::validateAccess() si basa sulla route API REST richiesta per sapere quali controlli dei privilegi applicare a una determinata richiesta. Dal momento che non tiene conto del fatto che WordPress tratta le route API REST come stringhe senza distinzione tra maiuscole e minuscole, la modifica di un singolo carattere in maiuscolo ignorerebbe completamente la routine di controllo dei privilegi.

Ciò è particolarmente preoccupante perché alcuni degli endpoint del plug-in sono piuttosto sensibili. Ad esempio, l' aioseo/v1/htaccess può riscrivere il .htaccess di un sito con contenuto arbitrario. Un utente malintenzionato potrebbe abusare di questa funzione per nascondere le backdoor .htaccess ed eseguire codice dannoso sul server.

Iniezione SQL autenticata

Versioni interessate: tutte le versioni comprese tra 4.1.3.1 e 4.1.5.2 incluse.
ID CVE: CVE-2021-25037
CVSSv3.1: 7.7
CCSS: 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 )" )
            ->whereIn( 'post_type', $postTypes )
            ->whereIn( 'post_status', [ 'publish', 'draft', 'future', 'pending' ] )
            ->orderBy( 'post_title' )
            ->limit( 10 )
            ->run()
            ->result();

Il metodo PostsTerms::searchForObjects(), accessibile tramite il percorso dell'API REST /wp-json/aioseo/v1/objects , è sfuggito all'input dell'utente solo utilizzando wpdb::esc_like() prima di aggiungere tale input a una query SQL. Poiché questo metodo non è progettato per evitare le virgolette, un utente malintenzionato potrebbe comunque inserirle e forzare la query a divulgare informazioni riservate dal database, come le credenziali dell'utente.

Sebbene questo endpoint non fosse concepito per essere accessibile agli utenti con account con privilegi limitati, il suddetto vettore di attacco di escalation dei privilegi ha consentito loro di abusare di questa vulnerabilità.

Sequenza temporale

01-12-2021 – Primo contatto con All In One SEO
2021-12-02 – Inviamo loro i dettagli su queste vulnerabilità
08-12-2021 – Viene rilasciato All In One SEO 4.1.5.3

Conclusione

Ti consigliamo di controllare quale versione del plug-in All In One SEO sta utilizzando il tuo sito e, se rientra nell'intervallo interessato, aggiornalo il prima possibile!

In Jetpack, lavoriamo sodo per assicurarci che i tuoi siti Web siano protetti da questo tipo di vulnerabilità. Ti consigliamo di disporre di un piano di sicurezza per il tuo sito che includa la scansione e il backup di file dannosi. Jetpack Security è un'ottima opzione di sicurezza di WordPress per garantire la sicurezza del tuo sito e dei visitatori.

Crediti

Ricercatore originale: Marc Montpas

Grazie al resto del team di Jetpack Scan per feedback, aiuto e correzioni.