Vulnerabilități severe remediate în versiunea 4.1.5.3 a pluginului SEO All In One

Publicat: 2021-12-15

În timpul unui audit intern al pluginului All In One SEO, am descoperit o vulnerabilitate SQL Injection și o eroare de escaladare a privilegiilor.

Dacă este exploatată, vulnerabilitatea SQL Injection ar putea acorda atacatorilor acces la informații privilegiate din baza de date a site-ului afectat (de exemplu, nume de utilizator și parole hash).

Eroarea de escaladare a privilegiilor pe care am descoperit-o poate acorda actorilor răi acces la punctele finale protejate API REST la care nu ar trebui să aibă acces. În cele din urmă, acest lucru ar putea permite utilizatorilor cu conturi cu privilegii reduse, cum ar fi abonații, să execute codul de la distanță pe site-urile afectate.

Am raportat vulnerabilitățile autorului pluginului prin e-mail și au lansat recent versiunea 4.1.5.3 pentru a le rezolva. Vă recomandăm insistent să actualizați la cea mai recentă versiune de plugin și să aveți o soluție de securitate stabilită pe site-ul dvs., cum ar fi Jetpack Security.

Detalii

Nume plugin: All In One SEO
URI plugin: https://wordpress.org/plugins/all-in-one-seo-pack/
Autor: https://aioseo.com/

Vulnerabilitățile

Escalarea privilegiilor autentificată

Versiuni afectate: fiecare versiune între 4.0.0 și 4.1.5.2 inclusiv.
CVE-ID: 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;
	}

Verificările de privilegii aplicate de All In One SEO pentru a securiza punctele finale REST API conțineau o eroare foarte subtilă care ar fi putut acorda utilizatorilor cu conturi cu privilegii reduse (cum ar fi abonații) acces la fiecare punct final pe care îl înregistrează pluginul.

Metoda Api::validateAccess() se bazează pe ruta API REST care este solicitată pentru a ști ce verificări de privilegii trebuie aplicate pentru o anumită solicitare. Deoarece nu a luat în considerare faptul că WordPress tratează rutele REST API ca șiruri care nu țin cont de majuscule, schimbarea unui singur caracter în majuscule ar ocoli complet rutina de verificare a privilegiilor.

Acest lucru este deosebit de îngrijorător, deoarece unele dintre punctele finale ale pluginului sunt destul de sensibile. De exemplu, punctul aioseo/v1/htaccess poate rescrie .htaccess al unui site cu conținut arbitrar. Un atacator ar putea abuza de această caracteristică pentru a ascunde ușile din spate .htaccess și pentru a executa cod rău intenționat pe server.

Injecție SQL autentificată

Versiuni afectate: fiecare versiune între 4.1.3.1 și 4.1.5.2 inclusiv.
CVE-ID: 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 )" )
            ->whereIn( 'post_type', $postTypes )
            ->whereIn( 'post_status', [ 'publish', 'draft', 'future', 'pending' ] )
            ->orderBy( 'post_title' )
            ->limit( 10 )
            ->run()
            ->result();

Metoda PostsTerms::searchForObjects(), care este accesibilă prin ruta /wp-json/aioseo/v1/objects REST API, a scăpat doar de intrarea utilizatorului folosind wpdb::esc_like() înainte de a adăuga intrarea menționată la o interogare SQL. Deoarece această metodă nu este concepută pentru a scăpa de ghilimele, un atacator le poate injecta și forța interogarea să scurgă informații sensibile din baza de date, cum ar fi acreditările utilizatorului.

Deși acest punct final nu a fost menit să fie accesibil utilizatorilor cu conturi cu privilegii reduse, vectorul de atac de escaladare a privilegiilor menționat mai sus a făcut posibil ca aceștia să abuzeze de această vulnerabilitate.

Cronologie

2021-12-01 – Contactul inițial cu All In One SEO
2021-12-02 – Le trimitem detalii despre aceste vulnerabilități
2021-12-08 – All In One SEO 4.1.5.3 este lansat

Concluzie

Vă recomandăm să verificați ce versiune a pluginului All In One SEO folosește site-ul dvs. și, dacă se află în intervalul afectat, actualizați-l cât mai curând posibil!

La Jetpack, muncim din greu pentru a ne asigura că site-urile dvs. sunt protejate de aceste tipuri de vulnerabilități. Vă recomandăm să aveți un plan de securitate pentru site-ul dvs. care să includă scanarea fișierelor rău intenționate și backup-uri. Jetpack Security este o opțiune excelentă de securitate WordPress pentru a vă asigura că site-ul și vizitatorii sunt în siguranță.

credite

Cercetator original: Marc Montpas

Mulțumim restului echipei Jetpack Scan pentru feedback, ajutor și corecții.