Chain Race
Catamob, z3phyr
All files are included. Source code is the key.
http://race.darkarmy.xyz:8999
alternate: http://18.215.163.255:8999/
調査
このサイトのURLを入れてみると、ソースコードが取得できていることが分かる。
HTTPレスポンスを見る感じServer: Apache/2.4.18 (Ubuntu)
とだけあり、言語情報はない。
ソースコードを見ると、testhook.phpにPOSTしているので、phpなのだが、index.phpは刺さらない。ふむ。
php://filter/convert.base64-encode/resource=testhook.php
は反応がない。
Writeup
DarkCTF Writeup - こんとろーるしーこんとろーるぶい
file:///etc/passwdは刺さるらしい!
これだけ見て以降解いてみる。
fileプロトコル
root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin ... _apt:x:104:65534::/nonexistent:/bin/false localhost8080:x:5:60:darksecret-hiddenhere:/usr/games/another-server:/usr/sbin/nologin
localhost8080
というどう見ても怪しいユーザーがある。ここにアクセスしてみる。
<code><span style="color: #000000"> <span style="color: #0000BB"><?php<br />session_start</span><span style="color: #007700">();<br />include </span><span style="color: #DD0000">'flag.php'</span><span style="color: #007700">;<br /><br /></span><span style="color: #0000BB">$login_1 </span><span style="color: #007700">= </span><span style="color: #0000BB">0</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$login_2 </span><span style="color: #007700">= </span><span style="color: #0000BB">0</span><span style="color: #007700">;<br /><br />if(!(isset(</span><span style="color: #0000BB">$_GET</span><span style="color: #007700">[</span><span style="color: #DD0000">'user'</span><span style="color: #007700">]) && isset(</span><span style="color: #0000BB">$_GET</span><span style="color: #007700">[</span><span style="color: #DD0000">'secret'</span><span style="color: #007700">]))){<br /> </span><span style="color: #0000BB">highlight_file</span><span style="color: #007700">(</span><span style="color: #DD0000">"index.php"</span><span style="color: #007700">);<br /> die();<br />}<br /><br /></span><span style="color: #0000BB">$login_1 </span><span style="color: #007700">= </span><span style="color: #0000BB">strcmp</span><span style="color: #007700">(</span><span style="color: #0000BB">$_GET</span><span style="color: #007700">[</span><span style="color: #DD0000">'user'</span><span style="color: #007700">], </span><span style="color: #DD0000">"admin"</span><span style="color: #007700">) ? </span><span style="color: #0000BB">1 </span><span style="color: #007700">: </span><span style="color: #0000BB">0</span><span style="color: #007700">;<br /><br /></span><span style="color: #0000BB">$temp_name </span><span style="color: #007700">= </span><span style="color: #0000BB">sha1</span><span style="color: #007700">(</span><span style="color: #0000BB">date</span><span style="color: #007700">(</span><span style="color: #DD0000">"ms"</span><span style="color: #007700">).@</span><span style="color: #0000BB">$_COOKIE</span><span style="color: #007700">[</span><span style="color: #DD0000">'PHPSESSID'</span><span style="color: #007700">]);<br /></span><span style="color: #0000BB">session_destroy</span><span style="color: #007700">();<br />if ((</span><span style="color: #0000BB">$_GET</span><span style="color: #007700">[</span><span style="color: #DD0000">'secret'</span><span style="color: #007700">] == </span><span style="color: #DD0000">"0x1337"</span><span style="color: #007700">) || </span><span style="color: #0000BB">$_GET</span><span style="color: #007700">[</span><span style="color: #DD0000">'user'</span><span style="color: #007700">] == </span><span style="color: #DD0000">"admin"</span><span style="color: #007700">) {<br /> die(</span><span style="color: #DD0000">"nope"</span><span style="color: #007700">);<br />}<br /><br />if (</span><span style="color: #0000BB">strcasecmp</span><span style="color: #007700">(</span><span style="color: #0000BB">$_GET</span><span style="color: #007700">[</span><span style="color: #DD0000">'secret'</span><span style="color: #007700">], </span><span style="color: #DD0000">"0x1337"</span><span style="color: #007700">) == </span><span style="color: #0000BB">0</span><span style="color: #007700">){<br /> </span><span style="color: #0000BB">$login_2 </span><span style="color: #007700">= </span><span style="color: #0000BB">1</span><span style="color: #007700">;<br />}<br /><br /></span><span style="color: #0000BB">file_put_contents</span><span style="color: #007700">(</span><span style="color: #0000BB">$temp_name</span><span style="color: #007700">, </span><span style="color: #DD0000">"your_fake_flag"</span><span style="color: #007700">);<br /><br />if (</span><span style="color: #0000BB">$login_1 </span><span style="color: #007700">&& </span><span style="color: #0000BB">$login_2</span><span style="color: #007700">) {<br /> if (@</span><span style="color: #0000BB">unlink</span><span style="color: #007700">(</span><span style="color: #0000BB">$temp_name</span><span style="color: #007700">)) {<br /> die(</span><span style="color: #DD0000">"Nope"</span><span style="color: #007700">);<br /> }<br />echo </span><span style="color: #0000BB">$flag</span><span style="color: #007700">;<br />}<br />die(</span><span style="color: #DD0000">"Nope"</span><span style="color: #007700">);<br /></span> </span> </code>
んー、phpコードっぽいけど、html形式なので、repl.itに貼ってレンダリング結果をみてみる。
<?php session_start(); include 'flag.php'; $login_1 = 0; $login_2 = 0; if(!(isset($_GET['user']) && isset($_GET['secret']))){ highlight_file("index.php"); die(); } $login_1 = strcmp($_GET['user'], "admin") ? 1 : 0; $temp_name = sha1(date("ms").@$_COOKIE['PHPSESSID']); session_destroy(); if (($_GET['secret'] == "0x1337") || $_GET['user'] == "admin") { die("nope"); } if (strcasecmp($_GET['secret'], "0x1337") == 0){ $login_2 = 1; } file_put_contents($temp_name, "your_fake_flag"); if ($login_1 && $login_2) { if (@unlink($temp_name)) { die("Nope"); } echo $flag; } die("Nope");
このサイトっぽい。
flagを出すにはlogin_1と2をtrueにして、@unlink($temp_name)
をfalseにする必要がある。
unlink?
@
はエラーを出さない演算子なので、まあいいとして、unlinkはファイルの削除関数。
成功すればtrueが返るので、失敗させることが条件。
login_1とlogin_2をtrueにするには
入れる所は、
$login_1 = strcmp($_GET['user'], "admin") ? 1 : 0;
if (strcasecmp($_GET['secret'], "0x1337") == 0) { $login_2 = 1; }
となっているが、
if (($_GET['secret'] == "0x1337") || $_GET['user'] == "admin") { die("nope"); }
があるので、単純には入れられない。
どっちも配列で指定すればOK。
/?user[]=admin&secret[]=0x1337
unlinkのfalse
上は見たことがあるからいいが、こっちが問題。
ここで"Race"を使うのか?と思ったが、セッションIDを使ってる。違うか…
session_start();
$temp_name = sha1(date("ms").@$_COOKIE['PHPSESSID']);
session_destroy();
file_put_contents($temp_name, "your_fake_flag");
if ($login_1 && $login_2) {
if (@unlink($temp_name)) {
die("Nope");
}
echo $flag;
}
再度Writeup
- DarkCTF Writeup - こんとろーるしーこんとろーるぶい
- マジ?
- Chain Race - Web challenge | ctf
- ctfs/Write-ups.md at master · saw-your-packet/ctfs
検証
いつものideoneで確認してみる。
date("ms")はmonthとsecond説
dr5VkN - Online PHP Interpreter & Debugging Tool - Ideone.com
説立証。
PHP: DateTime::format - Manual
ほんとやんけ。
session_start直後はCOOKIEに何も入ってない説
qD6j9v - Online PHP Interpreter & Debugging Tool - Ideone.com
ideoneでsession作れるのかな?と思いつつ説立証。
というかしれっと@入ってるやんけ。
つまり?
1秒以内にリクエストすれば、$temp_nameは被らせることができる。
つまりrace-conditionを作れる。
適当にやって出す darkCTF{9h9_15_50_a3fu1}