2021/11/20から開催されていたn1CTFに参加してきました。
残念ながら解ける問題がなく、チャレンジだけで終了しました。
今回は、他のチャレンジャーのWriteUpを参考に、解けなかった問題について振り返っていこうと思います。
もくじ
Signin (Web)
以下のようなPHPスクリプトで稼働しているWEBサイトにアクセスできます。
<?php
//flag is /flag
$path=$_POST['path'];
$time=(isset($_GET['time'])) ? urldecode(date(file_get_contents('php://input'))) : date("Y/m/d H:i:s");
$name="/var/www/tmp/".time().rand().'.txt';
$black="f|ht|ba|z|ro|;|,|=|c|g|da|_";
$blist=explode("|",$black);
foreach($blist as $b){
if(strpos($path,$b) !== false){
die('111');
}
}
if(file_put_contents($name, $time)){
echo "<pre class='language-html'><code class='language-html'>logpath:$name</code></pre>";
}
$check=preg_replace('/((\s)*(\n)+(\s)*)/i','',file_get_contents($path));
if(is_file($check)){
echo "<pre class='language-html'><code class='language-html'>".file_get_contents($check)."</code></pre>";
}
どうやら/flag
というパスにフラグが存在するものの、POSTで送られてきたpath
のデータはブラックリストでフィルタリングされてしまうようです。
この問題については、当初はpath
の値に、ブラックリストには引っかからない文字列を使ったeval文などを用いて、最終的に$check
に/flag
が格納されている状態を作る問題と想定していましたが、残念ながら誤りでした。
この問題のFlagを取るためには、$time=(isset($_GET['time'])) ? urldecode(date(file_get_contents('php://input'))) : date("Y/m/d H:i:s");
の行に着目します。
isset($_GET[‘time’])
この記法は、変数の存在確認を行います。
つまり、GETクエリにtime
が含まれているかどうかを判断しているというわけです。
filegetcontents(‘php://input’)
GETクエリにtime
が含まれているとき、このスクリプトが呼び出されます。
file_get_contents('php://input')
は、HTTPヘッダ以下の生データを取得することができます。
参考:ajax - PHP “php://input” vs $_POST - Stack Overflow
参考:PHP filegetcontents()とは - Qiita
date()のバイパス
上記のとおりfile_get_contents('php://input')
で文字列として取得したリクエストデータをdate()
関数に渡しています。
ここで、公式ドキュメントを読むと、date()
のフォーマットのサンプルが出てきました。
<?php
// Assuming today is March 10th, 2001, 5:16:18 pm, and that we are in the
// Mountain Standard Time (MST) Time Zone
$today = date("F j, Y, g:i a"); // March 10, 2001, 5:16 pm
$today = date("m.d.y"); // 03.10.01
$today = date("j, n, Y"); // 10, 3, 2001
$today = date("Ymd"); // 20010310
$today = date('h-i-s, j-m-y, it is w Day'); // 05-16-18, 10-03-01, 1631 1618 6 Satpm01
$today = date('\i\t \i\s \t\h\e jS \d\a\y.'); // it is the 10th day.
$today = date("D M j G:i:s T Y"); // Sat Mar 10 17:16:18 MST 2001
$today = date('H:m:s \m \i\s\ \m\o\n\t\h'); // 17:03:18 m is month
$today = date("H:i:s"); // 17:16:18
$today = date("Y-m-d H:i:s"); // 2001-03-10 17:16:18 (the MySQL DATETIME format)
?>
これを見ると分かるようにdate('\i\t \i\s \t\h\e jS \d\a\y.');
の形式でバックスラッシュを付けることで、date()
関数による日付への変換がバイパスされる仕様のようです。
この仕様を利用して、以下のようにPOSTデータに\/\f/l\/a/g
を送信すると、最終的に$time
に/flag
が格納され、Flagを取得できます。
まとめ
ほんとはRevの問題の復習をしたかったのだけど、WriteUpが見つかりませんでした。
見つかったら更新しようと思います。