Category: Web Exploitation
Difficulty: Medium
Points: 100
Description
Logging into the site triggers a chain of redirects. The flag is split into two parts, each hidden in a different place along the redirect chain.
Overview
- Part 1 is embedded as a base64-encoded value in the
idquery parameter of the final redirect URL. - Part 2 is embedded in a JavaScript
<script>tag in the HTML of the redirect destination page. - Both parts must be base64-decoded and concatenated to form the flag.
Solution
Step 1: Log In and Follow the Redirect
- Submitting any credentials triggers a redirect chain.
- The
requestslibrary follows redirects by default, butr.urlgives the final URL after all redirects have resolved.
r = requests.post(url + "/login", data={"username": "test", "password": "test!"})
redirect = r.url
Step 2: Decode Part 1 from the URL
- The final redirect URL contains an
idparameter carrying a base64-encoded string. - Appending
==compensates for any missing base64 padding before decoding.
clue = redirect.split("id=")[-1] + "=="
pt1 = base64.b64decode(clue).decode()
Step 3: Decode Part 2 from the Script Tag
- The HTML response contains a
<script>tag with a string literal that holds a secondid=value. - A regex extracts the quoted string, and the same base64 decode is applied.
script = soup.find("script").get_text()
clue = re.search('"(.*?)"', script).group(1).split("id=")[-1]
pt2 = base64.b64decode(clue).decode()
Step 4: Concatenate
- The two decoded parts are joined directly.
result = pt1 + pt2
Why the Flag is Split
- Splitting across the redirect URL and the script body ensures that neither interception point alone reveals the full flag.
- Tools that only follow redirects without inspecting intermediate responses (like a browser with no devtools) miss part 2.
Flag
picoCTF{proxies_all_the_way_3d9e3697}