オールインワンSEOプラグインバージョン4.1.5.3で修正された重大な脆弱性

公開: 2021-12-15

All In One SEOプラグインの内部監査中に、SQLインジェクションの脆弱性と特権昇格のバグが発見されました。

SQLインジェクションの脆弱性が悪用されると、攻撃者は影響を受けるサイトのデータベースからの特権情報(ユーザー名やハッシュ化されたパスワードなど)にアクセスできるようになる可能性があります。

私たちが発見した特権昇格のバグは、悪意のある攻撃者に、アクセスしてはならない保護されたRESTAPIエンドポイントへのアクセスを許可する可能性があります。 これにより、最終的には、サブスクライバーなどの特権の低いアカウントを持つユーザーが、影響を受けるサイトでリモートでコードが実行される可能性があります。

プラグインの作成者に電子メールで脆弱性を報告し、最近、それらに対処するためにバージョン4.1.5.3をリリースしました。 最新のプラグインバージョンに更新し、JetpackSecurityなどの確立されたセキュリティソリューションをサイトに用意することを強くお勧めします。

詳細

プラグイン名:オールインワンSEO
プラグインURI:https://wordpress.org/plugins/all-in-one-seo-pack/
著者:https://aioseo.com/

脆弱性

認証された特権の昇格

影響を受けるバージョン: 4.0.0から4.1.5.2までのすべてのバージョン。
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;
	}

All In OneSEOによってRESTAPIエンドポイントを保護するために適用された特権チェックには、プラグインが登録するすべてのエンドポイントへのアクセスを特権の低いアカウント(サブスクライバーなど)を持つユーザーに許可する可能性のある非常に微妙なバグが含まれていました。

Api :: validateAccess()メソッドは、要求されているREST APIルートに依存して、特定の要求に適用する特権チェックを認識します。 WordPressがRESTAPIルートを大文字と小文字を区別しない文字列として扱うという事実を考慮していなかったため、単一の文字を大文字に変更すると、特権チェックルーチンが完全にバイパスされます。

プラグインのエンドポイントのいくつかはかなり敏感なので、これは特に心配です。 たとえば、 aioseo/v1/htaccessエンドポイントは、サイトの.htaccessを任意のコンテンツで書き換えることができます。 攻撃者はこの機能を悪用して.htaccessバックドアを隠し、サーバー上で悪意のあるコードを実行する可能性があります。

認証されたSQLインジェクション

影響を受けるバージョン: 4.1.3.1から4.1.5.2までのすべてのバージョン。
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();

PostsTerms :: searchForObjects()メソッドは、 /wp-json/aioseo/v1/objects REST APIルートを介してアクセスでき、SQLクエリに入力を追加する前にwpdb :: esc_like()を使用してユーザー入力をエスケープするだけでした。 このメソッドは引用符をエスケープするように設計されていないため、攻撃者は引用符を挿入し、クエリにユーザーの資格情報などの機密情報をデータベースから漏洩させる可能性があります。

このエンドポイントは、特権の低いアカウントを持つユーザーがアクセスできるようには意図されていませんでしたが、前述の特権昇格攻撃ベクトルにより、ユーザーはこの脆弱性を悪用することができました。

タイムライン

2021-12-01 –オールインワンSEOとの最初の接触
2021-12-02 –これらの脆弱性に関する詳細を送信します
2021-12-08 –オールインワンSEO4.1.5.3がリリースされました

結論

サイトで使用しているAllInOne SEOプラグインのバージョンを確認し、影響を受ける範囲内にある場合は、できるだけ早く更新することをお勧めします。

Jetpackでは、お客様のWebサイトがこれらのタイプの脆弱性から保護されるように努めています。 悪意のあるファイルのスキャンとバックアップを含むサイトのセキュリティ計画を立てることをお勧めします。 Jetpack Securityは、サイトと訪問者の安全を確保するための優れたWordPressセキュリティオプションの1つです。

クレジット

元の研究者:マーク・モンパス

フィードバック、ヘルプ、および修正を提供してくれたJetpackScanチームの残りのメンバーに感謝します。