
WordPress RCE脆弱性CVE-2019-8942およびCVE-2019-8943の詳細な技術分析と概念実証エクスプロイト。パストラバーサルと画像アップロード操作を介したLFIからRCEへの昇格を実証します。
CVE-2019-8942 は、author権限で WordPress Web サーバーに対して RCE を実行するために、LFI の脆弱性とファイルアップロード機能を組み合わせて悪用する脆弱性です。影響を受ける WordPress のバージョンは 4.9.9 より前と 5.0.1 より前の 5.x です。Post Meta の wp_attached_file の値を任意の文字列(例: .jpg?file.php)に変更できるため、リモートでコードを実行できます。author権限の攻撃者は、Exif メタデータに悪質な PHP コードを含む画像ファイルをアップロードすることで、任意のコードを実行できます。この悪用には CVE-2019-8943 を利用できます。
CVE-2019-8943 では、バージョン 5.0.3 までの WordPress が wp_crop_image() メソッドにパストラバーサル(Path Traversal)の脆弱性を抱えています。画像切り抜き機能を使用できる権限を持つ攻撃者(author)は、.jpg?/../../file.jpg のような 2 つの拡張子を含むファイル名を利用して、画像ファイルを任意のディレクトリに書き込むことができます。
author 権限を持つユーザーアカウント。ユーザーが RCE を実行できる主な原因は、Post Meta が上書き可能であるという欠陥にあります。
メタデータとは、データに関する説明データです。このケースでは、メタデータはブログの情報(タイトル、投稿日、著者名など)です。
WordPress バージョン 4.9.8 のソースコードでは、画像が更新されると edit_post() 関数が呼び出されます。ここで注目すべき点は、この関数が $_POST 配列を直接操作することです。wp_update_post は、編集が許可されているデータフィールドをチェックせずに、$post_data を直接パラメータとして受け取ります。
function edit_post( $post_data = null ) {
global $wpdb;
if ( empty($post_data) )
$post_data = &$_POST;
...
if ( isset($post_data['meta']) && $post_data['meta'] ) {
foreach ( $post_data['meta'] as $key => $value ) {
if ( !$meta = get_post_meta_by_id( $key ) )
continue;
if ( $meta->post_id != $post_ID )
continue;
if ( is_protected_meta( $meta->meta_key, 'post' ) || ! current_user_can( 'edit_post_meta', $post_ID, $meta->meta_key ) )
continue;
if ( is_protected_meta( $value['key'], 'post' ) || ! current_user_can( 'edit_post_meta', $post_ID, $value['key'] ) )
continue;
update_meta( $key, $value['key'], $value['value'] );
}
}
...
update_post_meta( $post_ID, '_edit_last', get_current_user_id() );
$success = wp_update_post( $post_data );
if ( ! $success && is_callable( array( $wpdb, 'strip_invalid_text_for_column' ) ) ) {
$fields = array( 'post_title', 'post_content', 'post_excerpt' );
foreach ( $fields as $field ) {
if ( isset( $post_data[ $field ] ) ) {
$post_data[ $field ] = $wpdb->strip_invalid_text_for_column( $wpdb->posts, $field, $post_data[ $field ] );
}
}
wp_update_post( $post_data );
}
wp-admin/includes/post.php
投稿権限を持つユーザーは Post Meta の値を上書きできます。具体的には、攻撃者はメタデータ _wp_attached_file の値を編集できます。これによりファイル名は変更されませんが、WordPress が編集時に操作するファイルが変更されます。これがパストラバーサル(Path Traversal)の悪用につながります。

悪用前の
wp_postmeta
アップロードされたファイルが YYYY/MM/name.jpg の形式であることがわかります。WordPress はファイルを YYYY/MM/ ディレクトリに保存するため、パストラバーサルの悪用を連想できます。
wp_crop_image() 関数では、author ユーザーが画像の切り抜きを行うと、WordPress は画像が存在することを 2 つの方法で確認します。1 つ目の方法は、wp-content/uploads ディレクトリ内で _wp_attached_file に基づいて画像を検索する方法です。
function wp_crop_image( $src, $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $src_abs = false, $dst_file = false ) {
$src_file = $src;
if ( is_numeric( $src ) ) { // Handle int as attachment ID
$src_file = get_attached_file( $src );
if ( ! file_exists( $src_file ) ) {
// If the file doesn't exist, attempt a URL fopen on the src link.
// This can occur with certain file replication plugins.
$src = _load_image_to_edit_path( $src, 'full' );
} else {
$src = $src_file;
}
}
$editor = wp_get_image_editor( $src );
...
function get_attached_file( $attachment_id, $unfiltered = false ) {
$file = get_post_meta( $attachment_id, '_wp_attached_file', true );
wp-admin/includes/image.php
上記の方法が失敗した場合、WordPress は自身のサーバーから画像をダウンロードします。wp-content/uploads ディレクトリへのパスと _wp_attached_file に含まれるファイル名を含む URL を生成します。ローカルから直接取得するのではなく画像のダウンロードを試みるのは、一部のプラグインがその URL が送信されたときに画像を生成するためです。
WordPress が wp_get_image_editor() メソッドで画像のダウンロードに成功すると、画像の切り抜きが行われます。切り抜かれた画像はファイルシステムに保存されます。ファイル名は、攻撃者が制御する get_post_meta() から返される $src 変数の値です。WordPress は wp_mkdir_p() メソッド(9行目)でディレクトリを作成し、save() でその場所に画像を保存します。save() メソッドはパストラバーサルの悪用を一切チェックしていないことがわかります。
...
$src = $editor->crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $src_abs );
if ( is_wp_error( $src ) )
return $src;
if ( ! $dst_file )
$dst_file = str_replace( basename( $src_file ), 'cropped-' . basename( $src_file ), $src_file );
wp_mkdir_p( dirname( $dst_file ) );
$dst_file = dirname( $dst_file ) . '/' . wp_unique_filename( dirname( $dst_file ), basename( $dst_file ) );
$result = $editor->save( $dst_file );
_wp_attached_file に渡すペイロードに #/../../ という文字が含まれる場合、1つ目の画像読み込み方法ではそのようなパスは存在しないため、WordPress は画像を読み込むための 2 つ目の方法として、画像をダウンロードする URL を生成します。ここでパスは http://localhost/wp-admin/wp-content/uploads/YYYY/MM/name.jpg#/name.jpg の形式になります。これは URL であるため、#(フラグメントを示す)以降の文字は無視されます。見つかるファイル名は name.jpg#/name.jpg となり、このうち name.jpg# はディレクトリであり、これは有効な名前です。次に、/name.jpg#/../../name.jpg のようなペイロードを使用してパストラバーサルを悪用します。

_wp_attach_fileや_wp_page_templateなどの値フィールドが変更されていることがわかります
パストラバーサルから RCE へ
各 WordPress ページはテーマを使用し、テンプレートファイルを含む /wp-content/themes/theme_name/ フォルダーを持ちます。場合によっては、投稿に対してテーマを選択することが可能です。ユーザーは Post Meta テーブルの _wp_page_template を任意のファイル名に設定するだけで済みます。ただし、テーマディレクトリ内のファイルにのみ有効であるという制限があります。通常、このディレクトリにはアクセスしたりファイルをアップロードしたりすることはできません。しかし、パストラバーサルの悪用を利用して、このディレクトリにファイルを書き込むことができます。author の攻撃者は投稿を作成し、_wp_page_template パラメータの値を画像ファイルで上書きします。画像ファイルには悪質な PHP コードが埋め込まれており、画像が読み込まれると PHP コードが実行されます -> RCE。
Step 1: wpscan を使用して Web サイトが使用しているテーマを特定します => twentyseventeen

Step 2: ツール Exiftool を使用して画像に悪質な PHP コードを挿入します
exiftool demo.jpg -documentname="<?php phpinfo();?>"

Step 3: author アカウントでサイトの管理画面にログインし、メディア -> 新規追加 -> 画像のアップロードに進みます。

Step 4: 画像にアクセスし、Edit more details をクリックします

Step 5: Update をクリックし、Burpsuite を使用してリクエストをインターセプトします -> Send to repeater


Step 6: Edit image をクリックして画像を切り抜き、Save をクリックします。画像を保存するリクエストをインターセプトし、Send to repeater に送信します。


Step 7: Step 5 のリクエストを使用し、リクエストにパラメータ &meta_input[_wp_attached_file]=YYYY/MM/file.jpg#/file を追加します。このリクエストは、file.jpg# フォルダーを作成し、画像 file をこのディレクトリにコピーする準備を行います。
Step 8: 画像を保存するために Step 6 のリクエストを送信します。file.jpg# ディレクトリが作成され、画像が YYYY/MM/file.jpg# ディレクトリに保存されていることが確認できます。
Step 9: Step 7 と同様に、Step 5 のリクエストを使用し、リクエストにパラメータ &meta_input[_wp_attached_file]=YYYY/MM/file.jpg#/../../../../themes/twentyseventeen/file を追加します。WordPress のディレクトリ構造とテーマ名がわかっているため、上記のようなペイロードを作成できます。

Step 10: 画像を保存するために Step 6 のリクエストを送信します。画像が新しいパスに保存されていることが確認できます。
