CTF 19 – Runes Challenge

(Inspired by the PMNH Challenge)

Your objective is to create a working alert(1) payload that executes inside the <script> tag shown below.

🔒 Restrictions:
– Max 275 characters
– Only special characters and Unicode rune characters are allowed
– No letters (a-zA-Z) or digits (0-9)

💡 Hints:
– Unicode characters (like runes) can be used as JavaScript variables, e.g., ᚠ = ''
– You cannot call function names like alert(1) directly due to letter restrictions
– Instead, build your own eval using string-based logic
– Certain built-in values, when used in unexpected expressions, may reveal useful strings. For example, combining them with other types can sometimes coerce them into a readable form. Once you have a string, remember: JavaScript lets you access individual characters using index notation — just like an array but only if the output itself is a string aka "test"[0] returns "t".
– A great trick from JSFuck:
[]["filter"]["constructor"]("alert(1)")() or here a better example how to "construct" your payloads []["fil"+"ter"]["const"+"ructor"]("ale"+"rt(1)")()
– This works entirely on strings, no named functions but the eval sink can run any function

👇 Scroll to the bottom and hit the button to see all allowed characters printed in your browser console

📚 Highly encouraged: study the full JSFuck reference or your gone have a bad time
https://github.com/aemkei/jsfuck/blob/main/jsfuck.js
https://jsfuck.com/




<?php

header("Content-Type: text/html; charset=utf-8");

$error = '';
$scriptPayload = '';

// Handle GET input
if (isset($_GET['input'])) {
    $input = $_GET['input'];
    echo "total length off payload input: ".mb_strlen($input, 'UTF-8');
    // Length check (UTF-8)
    if (mb_strlen($input, 'UTF-8') > 275) {
        $error = '❌ Forbidden: Input exceeds 275 character limit.';
    }
    // Reject letters and digits
    elseif (preg_match('/[a-zA-Z0-9]/', $input)) {
        $error = '❌ Forbidden: Letters and digits are not allowed.';
    }
    // Reject anything outside of punctuation + runes
    elseif (preg_match('/[^\p{S}\p{P}\x{16A0}-\x{16FF}]/u', $input)) {
        $error = '❌ Forbidden: Only special characters and Unicode runes are allowed.';
    } else {

        $scriptPayload = $input;
    }
}
?>

<div style="margin-top: 30px; background: #f0f0f0; padding: 15px; border-radius: 8px;">
    <h1>CTF 19 – Runes Challenge</h1>
    <h2>
        (Inspired by the PMNH Challenge)<br><br>

        Your objective is to create a working alert(1) payload that executes inside the &lt;script&gt; tag shown below.<br><br>

        🔒 Restrictions:<br>
        – Max 275 characters<br>
        – Only special characters and Unicode rune characters are allowed<br>
        – No letters (a-zA-Z) or digits (0-9)<br><br>

        💡 Hints:<br>
        – Unicode characters (like runes) can be used as JavaScript variables, e.g., <code>ᚠ = ''</code><br>
        – You cannot call function names like <code>alert(1)</code> directly due to letter restrictions<br>
        – Instead, build your own eval using string-based logic<br>
        – Certain built-in values, when used in unexpected expressions, may reveal useful strings.  
  For example, combining them with other types can sometimes coerce them into a readable form.  
  Once you have a string, remember: JavaScript lets you access individual characters using index notation — just like an array but only if the output itself is a string aka <code>"test"[0]</code> returns "t".<br>

        
        – A great trick from JSFuck:<br>
        <code>[]["filter"]["constructor"]("alert(1)")()</code> or here a better example how to "construct" your payloads <code>[]["fil"+"ter"]["const"+"ructor"]("ale"+"rt(1)")()</code><br>
        – This works entirely on strings, no named functions but the eval sink can run any function <br><br>

        👇 Scroll to the bottom and hit the button to see all allowed characters printed in your browser console<br><br>

        📚 Highly encouraged: study the full JSFuck reference or your gone have a bad time<br>
        <a href="https://jsfuck.com/" target="_blank">
            https://github.com/aemkei/jsfuck/blob/main/jsfuck.js
        </a><br>
        <a href="https://jsfuck.com/" target="_blank">
            https://jsfuck.com/
        </a></br>
    </h2>
</div>

<!-- Console Helper -->
<button onclick="showAllowedChars()" style="margin-top:10px;">Show all Allowed Characters in your console (Console)</button>


<form method="get" action="" style="margin-top: 20px;">
    <label for="input">Enter your rune/special-char payload (max 275 chars):</label><br>
    <input type="text" name="input" id="input"
           style="width: 100%; font-family: monospace;"
           value="<?= htmlspecialchars($_GET['input'] ?? '') ?>"><br><br>
    <button type="submit">Send Payload</button>
</form>

<!-- Error + Output Section -->
<div style="margin-top: 20px;">
    <?php if ($error): ?>
        <div style="color: red; font-weight: bold;">
            <?= htmlspecialchars($error) ?>
        </div>
    <?php endif; ?>

    <?php if (!$error): ?>
        <script>
        // filler comment ignore payload is placed under this.
        <?= $scriptPayload ?>
        </script>
    <?php endif; ?>
</div>

<?php
// Always show the PHP source
highlight_file(__FILE__);
echo '<hr>';
?>


<script>
function showAllowedChars() {
    // generates all allowed chars in console make good use off this.
    const specials = `!"#$%&'()*+,-./:;<=>?@[\\]^_\`{|}~`;
    const runes = [];
    for (let i = 0x16A0; i <= 0x16FF; i++) {
        runes.push(String.fromCharCode(i));
    }

    console.log("Allowed Special Characters:");
    console.log(specials.split('').join(','));
    console.log("Allowed Unicode Runes (U+16A0–U+16FF):");
    console.log(runes.join(','));
}
</script>