以struts2 s2-21为例,分析跟踪请求从tomcat容器到struts2框架的代码处理流程。
tomcat部分:
tomcat 调用JIoEndpoint.java 的run()创建socket
然后调用processor.process(socket)负责解析http 协议并返回结果内容。
其中processor 是HttpProcessor 的一个实例,事实上tomcat 对HTTP 请求的解析都是通过
HttpProcessor 这个类中的process()这个方法实现的。跟入process()这个函数,主要功能有以下几个:
parseRequestLine()和parseHeaders()分别解析消息头第一行、请求头其他字段等。
prepareRequest()
通过prepareRequest 方法组装request filter,用于处理http 消息体
adapter.service(request, response)
将request 交给tomcat 处理,返回response
inputBuffer.endRequest()
将response 返回给客户端
其中,adapter.service(request, response)将请求交给容器处理。tomcat从connector 到servlet 处理HTTP 请求。http 请求会依次进入engine、host、wrapper,一直到servlet,这里开始关联struts2。
其中filter是struts2的FilterDispatcher实例。执行这个doFilter 方法才开始进入struts2 的代码逻辑,在
这之后程序的控制权由容器转交给struts2。
struts2处理http请求
其实Struts2 的核心就是一个Filter,它的作用只是处理HTTP 请求(request)然后返回给客户端(response),其doFilter方法是struts2 处理HTTP 请求的入口。后面struts2 将HTTP 请求经过一系列处理之后,交给了参数拦截器(ParametersInterceptor),用来设置参数属性。当提交a=b参数时,struts2会自动执行对应的set a方法去设置属性值,具体实现依靠OGNL完成。
参数拦截器(ParametersInterceptor)中的doIntercept 方法:
首先参数拦截器会获取action 实例
Object action = invocation.getAction();
然后生成OGNL 上下文
ActionContext ac = invocation.getInvocationContext();
这里的ac 便是OGNL 上下文了。包括_root、_memberAccess等属性。
通过retriveParamerers,即参数拦截器,获取http请求参数。
调用ActionContext.getParameters() 实现,获得Map 型的参数集parameters。遍历HttpServletRequest、HttpSession、ServletContext 中的数据,并将其复制到Webwork 的Map 中实现,至此之后,所有数据操作均在此Map 结构中进行,从而将内部结构与Servlet API 相分离。
这里newStack 是从OGNL 上下文中取出的ValueStack,保存的是action 的实例。
这里 newStack.setParameter(name, value); 便是将HTTP 请求的参数设置到action 实例当中。此过程中会调用set 方法设置属性。这里newStack.setParameter(name, value); 的执行逻辑都是通过OGNL 实现的,实际上就是遍历上下文去找对应的set 方法。这也是为何利用ognl实现漏洞的原因。
在ParametersInterceptor.java的参数拦截器函数doIntercept()中,其中newStack.setParameter(name, value);函数中,判断参数是否符合要求。
这个 this. excludeParams 便是 struts2-core.jar 中 struts-default.xml 中配置的正则了。因此在s2-021漏洞中可以绕过官方修复。
s2-021 poc:Class['ClassLoader'].resources.dirContext.docBase=xxxx
因为每个action 必然继承容器的 classLoader ,所以每个action 中肯定有对应 classLoader 中的属性。这里请求参数是 Class['ClassLoader'].resources.dirContext.docBase ,跟踪代码最终找到
调用的是tomcat 源码中BaseDirContext 类中的 setDocBase() 方法.