Ранее в этом году я
Патч Обход
Исходный патч для CVE-2020-2555 не затрагивал нижнюю часть следующей цепочки гаджетов:
Любая способность вызывать ChainedExtractor.extract()все равно приведет к удаленному выполнению кода. В докладе Quynh Le показывает , что все еще можно достичь с ChainedExtractor.extract()помощью ExtractorComparatorи AbstractExtractorклассов. Давайте начнем с рассмотрения compare()метода ExtractorComparator:
Как показано выше, все еще возможно вызвать ChainedExtractor.extract(), установив this.m_extractorэкземпляр ChainedExtractor.
Точно compare()так же можно использовать метод абстрактного класса AbstractExtractor.
MultiExtractorКласс расширяет AbstractExtractorи может быть использован , чтобы достичь ChainedExtractor.extract():
Полная цепь гаджетов
Чтобы разработать полную цепочку гаджетов, нам нужна возможность вызывать compare()метод произвольно Comparatorиз readObject(). Публично задокументированы способ сделать это , используя PriorityQueueкласс , как показано на следующих ysoserial гаджетов: BeanShell1, Jython1, CommonsCollections2, CommonsBeanutils1, CommonsCollections4и Groovy1:
SiftUpUsingComparator() Можно вызвать compare( )метод произвольного Comparator:
Есть и другие способы достижения этого. Например, отправитель использовал следующий метод:
Таким образом, toString() метод класса Mutations может привести к вызову ConcurrentSkipListMap.size():
Из ConcurrentSkipListMap.size(), можно вызвать compare()метод произвольного Comparator.
Демонстрация цепочек гаджетов
Используя описанные выше методы, была построена следующая полная цепочка гаджетов ExtractorComparator:
В следующем видео показано, как цепочка гаджетов используется для получения RCE по протоколу T3.
А для AbstractExtractor примера использовалась следующая цепочка:
В следующем видео показано, как цепочка гаджетов используется для получения RCE по протоколу T3:
Использование этих уязвимостей через HTTP:
Следует отметить, что эта уязвимость находится в библиотеке
Можно использовать эти цепочки гаджетов в отношении
Эта уязвимость заключается в том BIRemotingServlet, что прослушивает TCP-порт 7780 и не требует никакой аутентификации:
BIRemotingServlet использует AMF (формат сообщения действия) для связи с клиентом.
Как показано, когда пакет AMF десериализован, произвольные объекты могут быть восстановлены в AMF3ObjectInput посредством вызова readComplexObject().
В этом примере UnicastRef объект реконструируется, что приводит к вызову распределенного сборщика мусора на стороне сервера для удаленного объекта, что позволяет нам ответить произвольным сериализованным объектом. Ответ одной из цепочек гаджетов, как описано выше, приводит к RCE.
Для получения более подробной информации об использовании десериализации Java в реализациях Java AMF, обратитесь к этому посту из
Вывод
Для получения более подробной информации об уязвимостях десериализации Java, обратитесь к этому техническому документу от Морица Бехлера. В
Источник:
Ссылка скрыта от гостей
об уязвимости десериализации в Oracle WebLogic Server. Это было исправлено Oracle и присвоено CVE-2020-2555. Однако исследователь Quynh Le из VNPT ISC сообщил об ошибке в ZDI, которая показала, как можно обойти патч. Эта ошибка, обозначенная
Ссылка скрыта от гостей
, в настоящее время
Ссылка скрыта от гостей
Oracle как используемая в активных атаках. В этом сообщении мы рассмотрим детали этой недавно исправленной уязвимости.Патч Обход
Исходный патч для CVE-2020-2555 не затрагивал нижнюю часть следующей цепочки гаджетов:
Код:
BadAttributeValueExpException.readObject()
com.tangosol.util.filter.LimitFilter.toString() //<--- CVE-2020-2555 patched here
com.tangosol.util.extractor.ChainedExtractor.extract()
com.tangosol.util.extractor.ReflectionExtractor().extract()
Method.invoke()
//...
com.tangosol.util.extractor.ReflectionExtractor().extract()
Method.invoke()
Runtime.exec()
Любая способность вызывать ChainedExtractor.extract()все равно приведет к удаленному выполнению кода. В докладе Quynh Le показывает , что все еще можно достичь с ChainedExtractor.extract()помощью ExtractorComparatorи AbstractExtractorклассов. Давайте начнем с рассмотрения compare()метода ExtractorComparator:
Код:
public int compare(T o1, T o2) {
Comparable a1 = (o1 instanceof InvocableMap.Entry) ? (Comparable)((InvocableMap.Entry)o1).extract(this.m_extractor)
: (Comparable)this.m_extractor.extract(o1);
Comparable a2 = (o2 instanceof InvocableMap.Entry) ? (Comparable)((InvocableMap.Entry)o2).extract(this.m_extractor)
: (Comparable)this.m_extractor.extract(o2);
if (a1 == null)
{
return (a2 == null) ? 0 : -1;
}
if (a2 == null)
{
return 1;
}
return a1.compareTo(a2);
}
Как показано выше, все еще возможно вызвать ChainedExtractor.extract(), установив this.m_extractorэкземпляр ChainedExtractor.
Точно compare()так же можно использовать метод абстрактного класса AbstractExtractor.
Код:
public int compare(Object o1, Object o2) { return SafeComparator.compareSafe(null, extract(o1), extract(o2)); }
MultiExtractorКласс расширяет AbstractExtractorи может быть использован , чтобы достичь ChainedExtractor.extract():
Код:
public abstract class AbstractCompositeExtractor<T, E>
extends AbstractExtractor<T, E>
[...Truncated...]
public class MultiExtractor
extends AbstractCompositeExtractor
[...Truncated...]
public Object extract(Object oTarget) {
if (oTarget == null)
{
return null;
}
ValueExtractor[] aExtractor = getExtractors();
int cExtractors = aExtractor.length;
Object[] aValue = new Object[cExtractors];
for (int i = 0; i < cExtractors; i++)
{
aValue[i] = aExtractor[i].extract(oTarget);<-----------------------
}
return new ImmutableArrayList(aValue);
}
Полная цепь гаджетов
Чтобы разработать полную цепочку гаджетов, нам нужна возможность вызывать compare()метод произвольно Comparatorиз readObject(). Публично задокументированы способ сделать это , используя PriorityQueueкласс , как показано на следующих ysoserial гаджетов: BeanShell1, Jython1, CommonsCollections2, CommonsBeanutils1, CommonsCollections4и Groovy1:
Код:
java.util . PriorityQueue . readObject ()
java.util.PriorityQueue.heapify()
java.util.PriorityQueue.siftDown()
java.util.PriorityQueue.siftDownUsingComparator()
SiftUpUsingComparator() Можно вызвать compare( )метод произвольного Comparator:
Код:
private void siftUpUsingComparator(int paramInt, E paramE) {
while (paramInt > 0) {
int i = paramInt - 1 >>> 1;
Object object = this.queue[i];
if (this.comparator.compare(paramE, object) >= 0)<----------------
break;
this.queue[paramInt] = object;
paramInt = i;
}
this.queue[paramInt] = paramE;
}
Есть и другие способы достижения этого. Например, отправитель использовал следующий метод:
Код:
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()
Таким образом, toString() метод класса Mutations может привести к вызову ConcurrentSkipListMap.size():
Код:
ConcurrentSkipListMap$SubMap.class
public int size() {
Comparator<? super K> cmp = m.comparator;
long count = 0;
for (ConcurrentSkipListMap.Node<K,V> n = loNode(cmp);
isBeforeEnd(n, cmp); <---------------------------------------------------
n = n.next) {
if (n.getValidValue() != null)
++count;
}
return count >= Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)count;
}
[...Truncated...]
boolean isBeforeEnd(ConcurrentSkipListMap.Node<K,V> n, Comparator<? super K> cmp) {
....
int c = cpr(cmp, k, hi);<------------------------------------------------------
if (c > 0 || (c == 0 && !hiInclusive))
return false;
return true;
}
[...Truncated...]
static final int cpr(Comparator c, Object x, Object y) {
return (c != null) ? c.compare(x, y) : ((Comparable)x).compareTo(y); <--------
}
Из ConcurrentSkipListMap.size(), можно вызвать compare()метод произвольного Comparator.
Демонстрация цепочек гаджетов
Используя описанные выше методы, была построена следующая полная цепочка гаджетов ExtractorComparator:
Код:
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()
В следующем видео показано, как цепочка гаджетов используется для получения RCE по протоколу T3.
А для AbstractExtractor примера использовалась следующая цепочка:
Код:
java.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()
В следующем видео показано, как цепочка гаджетов используется для получения RCE по протоколу T3:
Использование этих уязвимостей через HTTP:
Следует отметить, что эта уязвимость находится в библиотеке
Ссылка скрыта от гостей
. Любое приложение с библиотекой Coherence в пути кода, где есть путь к десериализации, также уязвимо. Одним из примеров является Oracle Business Intelligence, который развернут в Oracle WebLogic.Можно использовать эти цепочки гаджетов в отношении
Ссылка скрыта от гостей
, о котором исследователь ZDI сообщил в ZDI, для достижения удаленного выполнения кода через HTTP.Эта уязвимость заключается в том BIRemotingServlet, что прослушивает TCP-порт 7780 и не требует никакой аутентификации:
Код:
<servlet>
<servlet-name>BIRemotingServlet</servlet-name>
<servlet-class>oracle.bi.nanserver.fwk.servlet.as.BIRemotingServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>BIRemotingServelet</servlet-name>
<url-pattern>/messagebroker/as/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>BIRemotingServlet</servlet-name>
<url-pattern>/messagebroker/cs/*</url-pattern>
</servlet-mapping>
BIRemotingServlet использует AMF (формат сообщения действия) для связи с клиентом.
Код:
protected void handleRequest(HttpServletRequest paramHttpServletRequest, HttpServletResponse paramHttpServletResponse)
throws ServletException, IOException {
[...Truncated...]
RemotingSvs remotingSvs = BISvsManagerBase.getRemotingSvs(); <----------------------------------------------------
remotingSvs.processCall(); <--------------------------------------------------------------------------------------------
setContentType(paramHttpServletResponse, OutputForm.AMF3);
paramHttpServletResponse.setContentLength(byteArrayOutputStream.size());
byteArrayOutputStream.writeTo(paramHttpServletResponse.getOutputStream());
paramHttpServletResponse.flushBuffer();
}
public int processCall() throws BISvsException {
[...Truncated...]
AMF3Packet aMF3Packet1 = deserializePacket(dataInputStream); <------------------------------------------------------------
if (logger.isLoggable(Level.FINE)) {
logger.fine("De-serialized request packet: " + aMF3Packet1.toString());
}
[...Truncated...]
}
oracle.bi.nanserver.fwk.util.remoting.RemotingSvsImpl.class
public AMF3Packet deserializePacket(DataInputStream paramDataInputStream) throws BISvsException {
try {
AMFObjectInput aMFObjectInput = getAMF3DeSerializer(paramDataInputStream); <------------------------------------------------
LegacyObjectInput legacyObjectInput = new LegacyObjectInput(paramDataInputStream, aMFObjectInput);
AMF3Packet aMF3Packet = new AMF3Packet();
aMF3Packet.deserialize(legacyObjectInput); <----------------------------------------------------------------------------------
return aMF3Packet;
}
catch (Exception exception) {
handleException(exception);
return null;
}
}
public AMFObjectInput getAMF3DeSerializer(DataInputStream paramDataInputStream) throws BISvsException {
try {
Class clazz = (Class)amf3DeSerializerClass.get();
if (clazz == null) {
String str = (String)BISvsManagerBase.getContextSvs().getValue("amf3DeSerializer");
if (str == null || str.trim().length() == 0) {
clazz = oracle.bi.nanserver.fwk.util.amf.AMF3ObjectInput.class; <--------------------------------------------------------
amf3DeSerializerClass.compareAndSet(null, clazz);
logger.info("Using default AMF3 De-Serializer");
[...Truncated...]
}
Как показано, когда пакет AMF десериализован, произвольные объекты могут быть восстановлены в AMF3ObjectInput посредством вызова readComplexObject().
Код:
protected Object readComplexObject(GenericTypeInfo paramGenericTypeInfo)
throws ClassNotFoundException, IOException {
try {
int i = readAMF3IntegerVal();
if ((i & true) == 0) {
return getVisitedObject(i >> 1);
}
ClassMetadata classMetadata = readClassMetadata(i);
String str = this.proxySvs.getConcreteClassName(classMetadata.name);
if (str == null) {
str = classMetadata.name;
}
// CVE-2020-2950 patch
//if (isBlacklisted(str))
//{
// throw new SecurityException("Unsupport class type:" + str);
//}
Class clazz = Class.forName(str);
ClassProxy classProxy = this.proxySvs.getProxy(clazz);
Object object1 = classProxy.newInstance(clazz);
int j = this.objectRefList.size();
markObjectVisited(object1);
if (classMetadata.externalizable) {
if (paramGenericTypeInfo != null) {
classProxy.readExternal(new GenericResult(object1, paramGenericTypeInfo), this);
} else {
classProxy.readExternal(object1, this);
}
}
else if (clazz == oracle.bi.nanserver.fwk.util.remoting.messages.RemotingMessage.class) {
populateRemotingMessage(object1, classMetadata, classProxy);
} else {
String[] arrayOfString = classMetadata.getFieldNames();
Object[] arrayOfObject = new Object[arrayOfString.length];
for (byte b = 0; b < arrayOfString.length; b++) {
arrayOfObject[b] = readObject();
}
this.proxySvs.setFieldValues(object1, arrayOfString, arrayOfObject, classProxy);
if (classMetadata.dynamic) {
while (true) {
String str1 = readAMF3String();
if (str1 == null || str1.length() == 0) {
break;
}
Object object = readObject();
this.proxySvs.setFieldValue(object1, str1, object, classProxy);
}
}
}
В этом примере UnicastRef объект реконструируется, что приводит к вызову распределенного сборщика мусора на стороне сервера для удаленного объекта, что позволяет нам ответить произвольным сериализованным объектом. Ответ одной из цепочек гаджетов, как описано выше, приводит к RCE.
Для получения более подробной информации об использовании десериализации Java в реализациях Java AMF, обратитесь к этому посту из
Ссылка скрыта от гостей
. Цепочки гаджетов были добавлены в ysoserial , а его прослушиватель JRMP использовался для использования этой уязвимости. Следующее видео демонстрирует это в действии:Вывод
Для получения более подробной информации об уязвимостях десериализации Java, обратитесь к этому техническому документу от Морица Бехлера. В
Ссылка скрыта от гостей
Oracle не говорится, насколько распространены атаки, но их руководство ясно: исправьте сейчас. Они также предлагают руководство по ограничению трафика протокола T3 / T3S для Oracle WebLogic Server. Следующий выпуск исправлений Oracle запланирован на 14 июля 2020 года. Посмотрим, сколько ошибок десериализации останется после этого обновления.Источник:
Ссылка скрыта от гостей
Последнее редактирование: