はまやんはまやんはまやん

hamayanhamayan's blog

Chain Race [DarkCTF]

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">&lt;?php<br />session_start</span><span style="color: #007700">();<br />include&nbsp;</span><span style="color: #DD0000">'flag.php'</span><span style="color: #007700">;<br /><br /></span><span style="color: #0000BB">$login_1&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">0</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$login_2&nbsp;</span><span style="color: #007700">=&nbsp;</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">])&nbsp;&amp;&amp;&nbsp;isset(</span><span style="color: #0000BB">$_GET</span><span style="color: #007700">[</span><span style="color: #DD0000">'secret'</span><span style="color: #007700">]))){<br />&nbsp;&nbsp;&nbsp;&nbsp;</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 />&nbsp;&nbsp;&nbsp;&nbsp;die();<br />}<br /><br /></span><span style="color: #0000BB">$login_1&nbsp;</span><span style="color: #007700">=&nbsp;</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">],&nbsp;</span><span style="color: #DD0000">"admin"</span><span style="color: #007700">)&nbsp;?&nbsp;</span><span style="color: #0000BB">1&nbsp;</span><span style="color: #007700">:&nbsp;</span><span style="color: #0000BB">0</span><span style="color: #007700">;<br /><br /></span><span style="color: #0000BB">$temp_name&nbsp;</span><span style="color: #007700">=&nbsp;</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&nbsp;((</span><span style="color: #0000BB">$_GET</span><span style="color: #007700">[</span><span style="color: #DD0000">'secret'</span><span style="color: #007700">]&nbsp;==&nbsp;</span><span style="color: #DD0000">"0x1337"</span><span style="color: #007700">)&nbsp;||&nbsp;</span><span style="color: #0000BB">$_GET</span><span style="color: #007700">[</span><span style="color: #DD0000">'user'</span><span style="color: #007700">]&nbsp;==&nbsp;</span><span style="color: #DD0000">"admin"</span><span style="color: #007700">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;die(</span><span style="color: #DD0000">"nope"</span><span style="color: #007700">);<br />}<br /><br />if&nbsp;(</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">],&nbsp;</span><span style="color: #DD0000">"0x1337"</span><span style="color: #007700">)&nbsp;==&nbsp;</span><span style="color: #0000BB">0</span><span style="color: #007700">){<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$login_2&nbsp;</span><span style="color: #007700">=&nbsp;</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">,&nbsp;</span><span style="color: #DD0000">"your_fake_flag"</span><span style="color: #007700">);<br /><br />if&nbsp;(</span><span style="color: #0000BB">$login_1&nbsp;</span><span style="color: #007700">&amp;&amp;&nbsp;</span><span style="color: #0000BB">$login_2</span><span style="color: #007700">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(@</span><span style="color: #0000BB">unlink</span><span style="color: #007700">(</span><span style="color: #0000BB">$temp_name</span><span style="color: #007700">))&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;die(</span><span style="color: #DD0000">"Nope"</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />echo&nbsp;</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

検証

いつもの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}