以struts2 s2-21為例,分析跟蹤請(qǐng)求從tomcat容器到struts2框架的代碼處理流程。
tomcat部分:
tomcat 調(diào)用JIoEndpoint.java 的run()創(chuàng)建socket
然后調(diào)用processor.process(socket)負(fù)責(zé)解析http 協(xié)議并返回結(jié)果內(nèi)容。
其中processor 是HttpProcessor 的一個(gè)實(shí)例,事實(shí)上tomcat 對(duì)HTTP 請(qǐng)求的解析都是通過(guò)
HttpProcessor 這個(gè)類(lèi)中的process()這個(gè)方法實(shí)現(xiàn)的。跟入process()這個(gè)函數(shù),主要功能有以下幾個(gè):
parseRequestLine()和parseHeaders()分別解析消息頭第一行、請(qǐng)求頭其他字段等。
prepareRequest()
通過(guò)prepareRequest 方法組裝request filter,用于處理http 消息體
adapter.service(request, response)
將request 交給tomcat 處理,返回response
inputBuffer.endRequest()
將response 返回給客戶(hù)端
其中,adapter.service(request, response)將請(qǐng)求交給容器處理。tomcat從connector 到servlet 處理HTTP 請(qǐng)求。http 請(qǐng)求會(huì)依次進(jìn)入engine、host、wrapper,一直到servlet,這里開(kāi)始關(guān)聯(lián)struts2。

其中filter是struts2的FilterDispatcher實(shí)例。執(zhí)行這個(gè)doFilter 方法才開(kāi)始進(jìn)入struts2 的代碼邏輯,在
這之后程序的控制權(quán)由容器轉(zhuǎn)交給struts2。
struts2處理http請(qǐng)求
其實(shí)Struts2 的核心就是一個(gè)Filter,它的作用只是處理HTTP 請(qǐng)求(request)然后返回給客戶(hù)端(response),其doFilter方法是struts2 處理HTTP 請(qǐng)求的入口。后面struts2 將HTTP 請(qǐng)求經(jīng)過(guò)一系列處理之后,交給了參數(shù)攔截器(ParametersInterceptor),用來(lái)設(shè)置參數(shù)屬性。當(dāng)提交a=b參數(shù)時(shí),struts2會(huì)自動(dòng)執(zhí)行對(duì)應(yīng)的set a方法去設(shè)置屬性值,具體實(shí)現(xiàn)依靠OGNL完成。
參數(shù)攔截器(ParametersInterceptor)中的doIntercept 方法:

首先參數(shù)攔截器會(huì)獲取action 實(shí)例
Object action = invocation.getAction();
然后生成OGNL 上下文
ActionContext ac = invocation.getInvocationContext();
這里的ac 便是OGNL 上下文了。包括_root、_memberAccess等屬性。
通過(guò)retriveParamerers,即參數(shù)攔截器,獲取http請(qǐng)求參數(shù)。


調(diào)用ActionContext.getParameters() 實(shí)現(xiàn),獲得Map 型的參數(shù)集parameters。遍歷HttpServletRequest、HttpSession、ServletContext 中的數(shù)據(jù),并將其復(fù)制到Webwork 的Map 中實(shí)現(xiàn),至此之后,所有數(shù)據(jù)操作均在此Map 結(jié)構(gòu)中進(jìn)行,從而將內(nèi)部結(jié)構(gòu)與Servlet API 相分離。
這里newStack 是從OGNL 上下文中取出的ValueStack,保存的是action 的實(shí)例。
這里 newStack.setParameter(name, value); 便是將HTTP 請(qǐng)求的參數(shù)設(shè)置到action 實(shí)例當(dāng)中。此過(guò)程中會(huì)調(diào)用set 方法設(shè)置屬性。這里newStack.setParameter(name, value); 的執(zhí)行邏輯都是通過(guò)OGNL 實(shí)現(xiàn)的,實(shí)際上就是遍歷上下文去找對(duì)應(yīng)的set 方法。這也是為何利用ognl實(shí)現(xiàn)漏洞的原因。
在ParametersInterceptor.java的參數(shù)攔截器函數(shù)doIntercept()中,其中newStack.setParameter(name, value);函數(shù)中,判斷參數(shù)是否符合要求。


這個(gè) this. excludeParams 便是 struts2-core.jar 中 struts-default.xml 中配置的正則了。因此在s2-021漏洞中可以繞過(guò)官方修復(fù)。
s2-021 poc:Class['ClassLoader'].resources.dirContext.docBase=xxxx
因?yàn)槊總€(gè)action 必然繼承容器的 classLoader ,所以每個(gè)action 中肯定有對(duì)應(yīng) classLoader 中的屬性。這里請(qǐng)求參數(shù)是 Class['ClassLoader'].resources.dirContext.docBase ,跟蹤代碼最終找到
調(diào)用的是tomcat 源碼中BaseDirContext 類(lèi)中的 setDocBase() 方法.