Всем привет, меня зовут Ваагн Варданян (тут нет опечатки, как многие думают :) ), работаю я в DSec исследователем безопасности SAP-систем, и в этой небольшой статье расскажу о связке уязвимостей в SAP, использование которых может привести к компрометации системы и как результат – доступу к критичной бизнес информации.
С каждой новой версией SAP NW, приложения становятся все более защищёнными, и уязвимости не дают скомпрометировать систему полностью. Но бывают ситуации, когда несколько проблем безопасности, используемых вместе, все же позволяют атакующим добиться своих целей. Сегодня мы расскажем о том, как скомпрометировать SAP NW с помощью связки уязвимостей.
В статье сначала мы поговорим о возможности получения информации из системы, об эксплуатации уязвимости, основанной на утечке информации, далее — об эскалации привилегий. Все уязвимости были найдены в последних (на момент исследования) версиях SAP (SAP NW AS JAVA 7.4). Ну что ж, понеслось.
Введение
На сервере SAP AS JAVA есть множество стандартных приложений, которые хранятся в папке C:\usr\sap\%SID%\J00\j2ee\cluster\apps\sap.com, где %SID% — это SID SAP-системы. Так, наша тестовая система имеет SID DM0.
Из приведенного ниже скриншота можно понять, что в стандартной сборке SAP NW присутствует более 1400 компонентов (если установить полную сборку, то выйдет и более 2000), которые могут быть вызваны пользователями SAP'a с различными правами.
Каждый компонент, который можно вызвать, имеет некий уровень доступа, описанный в файлах web.xml, portalapp.xml. Всего существует 4 вида прав доступа к компонентам:
Естественно, нас будут интересовать сервлеты, приложения и компоненты, которые имеют права no_safety, либо такие, у которых права вообще не предопределены, поскольку они доступны извне, и любой неавторизованный пользователь может получить к ним доступ.
Перейдем к первой уязвимости.
Уязвимость разглашения информации
После поиска файлов конфигураций файл дескрипторов, в которых есть описание прав доступа
<property name="SafetyLevel" value="no_safety"/>
или оно полностью отсутствует. Был найден webdynpro компонент tc~rtc~coll.appl.rtc~wd_chat. Вот его конфигурационный файл:
К данному сервису можно обратится по следующему адресу:
http:/SAP_IP:SAP_PORT/webdynpro/resources/sap.com/tc~rtc~coll.appl.rtc~wd_chat/Chat#
Oткрываем страницу и видим некоторый функционал по отправке сообщений. Среди прочего, в нем есть возможность добавления адресатов (пользователей):
Нажав на запись add participant, можем увидеть окно, которое предлагает поискать пользователя без авторизации.
Очень интересно, что в списке был найден пользователь с именем Jon Snow и логином J.Snow.
Гмм… Похоже, используя данную уязвимость, можно получить список логинов пользователей SAP. Однако, этого недостаточно для компрометации системы, поскольку после того, как 3 – 5 раз будет введет неправильный пароль, учетная запись будет блокирована. Так что давайте поищем другие уязвимости.
SQL injection
Следующий анонимный сервис, в котором была найдена уязвимость – UDDISecurityService.
На сервере SAP данный сервис находится по адресу: C:\usr\sap\DM0\J00\j2ee\cluster\apps\sap.com\tc~uddi\servlet_jsp\UDDISecurityService\
Как видно из конфигурационного файла, сервис также доступен анонимно:
Тег servlet-class показывает, что доступ к данному сервлету можно получить с помощью SOAP-запросов. Теперь перед нами стоит задача найти структуру SOAP-запроса. Известно, что структуру запросов можно узнать, найдя wsdl-файл, в котором описан данный сервис. Получается, что нам надо найти wsdl файл, содержащий запись UDDISecurityImplBean. Используя total commander, запустим поиск.
На сервере нашелся файл, содержащий нужную нам информацию. Поскольку он имеет wsdl-структуру, преобразовать его в SOAP-запрос можно, используя специальную утилиту. Мы выяснили, что в данном файле описано 2 метода: applyPermission и deletePermissionById.
Выберем второй метод (deletePermissionById), сгенерируем SOAP-запрос и отправим его на сервер SAP.
В ответе получим:
Ответ вернул 200-ый код, но логика обработки отправленных данных непонятна. Чтобы «познакомиться» с полным функционалом данной программы, надо найти на сервере JAVA-код, обрабатывающий SAOP-запрос. И мы обнаруживаем jar-файл, в котором есть описание обработки данного запроса, и расположенный на сервере по адресу:
C:\usr\sap\%SID%\J00\j2ee\cluster\apps\sap.com\tc~uddi\EJBContainer\applicationjars\tc~esi~uddi~server~ejb~ejbm.jar
После декомпиляции файла можем увидеть следующие классы:
Сама обработка запроса происходит в классе UDDISecurityBean.
Когда мы отправляем запрос deletePermissionById, видно, как появляется конструктор PermissionsDao(), который вызывает функцию deletePermision. Переходим в класс PermissionsDao.
Данные, передаваемые через SOAP-запрос, используются для обращения к базе данных сервера SAP без фильтрации. Можно предположить, что тут есть SQL injection. Чтобы наверняка убедиться в этом, необходимо отправить специальный запрос для SQL injection и посмотреть логи базы данных сервера SAP. Файлы логов базы данных находятся по умолчанию в папке C:\usr\sap\%SID%\J00\j2ee\cluster\server0\log\system\ и называются по умолчанию database_NN.N.log, где N – число от 0 до 9 включительно.
Отправим следующий запрос:
В ответе также получим 200-ый код:
Но в логах базы данных можно заметить следующее:
Теперь точно можно говорить о том, что у нас есть анонимная SQL-инъекция в базу данных SAP. Удалим логи с сервера и отправим следующий запрос. Если на сервере в логах не возникнут ошибки, то структура SQL-запроса правильная.
Ошибок нет.
Теперь новая задача — найти запрос, который поможет получить критичные данные из базы данных SAP, например, хеш пароля Jon Snow. Из документации SAP NW AS JAVA известно, что данные о пользователях (логин, именa, хеши пароля) хранятся в таблице UME_STRINGS.
Запрос для получения всех данных из UME_STRINGS имеет следующий вид:
Как видно, данная SQL injection vulnerability не error based, а адаптер, который используется в этом сервлете и не поддерживает sleep() функцию. Будем использовать метод умножения таблиц для увеличения времени обработки запроса и превратим данную уязвимость в time-based SQL injection. Для этого надо найти таблицу, в которой всегда во всех SAP-серверах будут содержаться данные. Такая таблица — J2EE_CONFIG, в которой хранится информация о конфигурации компонентов.
Oправим следующий запрос:
Сервер, получая запрос, пробует извлечь данные из БД, предварительно умножив 2 таблицы — UME_STRINGS и J2EE_CONFIG. Поскольку в таблицах хранятся данные большого объёма, произойдет временная нагрузка на сервер.
А ответ мы получаем спустя 32 секунды. И – готово: time-based SQL injection.
Получение хеша администратора
Как уже говорилось выше, захешированные пароли хранятся в таблице UME_STRINGS, которая имеет следующую структуру:
В таблице UME_STRINGS.PID хранятся имена пользователей.
UME_STRINGS.ATTR = 'j_password' показывает, что пользователь создан и присутствует в SAP AS JAVA стеке.
UME_STRINGS.VAL хранит хеши паролей от пользователей, логины которых записаны в UME_STRINGS.PID.
Выходит, что надо подобрать данные, содержащиеся в поле UME_STRINGS.VAL. Базовый SQL-запрос для injection выглядит так:
SELECT COUNT(*) FROM J2EE_CONFIG, UME_STRINGS WHERE UME_STRINGS.ATTR='j_password' AND UME_STRINGS.PID LIKE '%J.Snow%' AND UME_STRINGS.VAL LIKE '%'
Известно, что пароль хранится в базе данных SAP в захешированном виде, алгоритм хеширования может быть следующим:
SAPSHA
SSHA
SHA
SHA-512
Т.е. в хеше паролей могут быть такие символы:
1234567890QWERTYUIOPASDFGHJKLZXCVBNM*.,{}#+:-=qwertyuiopasdfghjklzxcvbnm/{SPACE}
Будем перебирать их, и, если символ, который мы выбрали, совпадает с символом хеша из пароля, у нас будет задержка запроса.
Ответ будет задержан на 1 секунду.
Автоматизируя этот процесс, получим хеш от пользователя Jon Snow.
{SHA-512, 10000, 24}YXNkUVdFMTIzzbAIcuqnw5RzpmdgZ38PWjhBeaGzHkV6XINN7ZDqxqgr0nYxfCaE5ncdK7kzzkzryJAn42qv9YlY034Llr4b8Rv1534chnIf1i8jZE6ylzTV5XuzvUlaXQ==
Как видно, пароль захеширован алгоритмом SHA-512. На этом исследование бы закончилось, если бы не третья уязвимость.
Криптографическая ошибка, эскалации привилегий
Эта уязвимость была найдена случайно :-)
С помощью SQL injection получили хеш пароля пользователя Jon Snow:
{SHA-512, 10000, 24}YXNkUVdFMTIzzbAIcuqnw5RzpmdgZ38PWjhBeaGzHkV6XINN7ZDqxqgr0nYxfCaE5ncdK7kzzkzryJAn42qv9YlY034Llr4b8Rv1534chnIf1i8jZE6ylzTV5XuzvUlaXQ==
Хммм. Как видно, в конце хеша есть символы ==. А что будет, если мы сделаем base64decode?
Что? Как? Пароль? Есть единственный способ проверить, действительно ли это пароль от Jon'a — зайти на портал, используя логин J.Snow и пароль asdQWE123.
Ура! Я администратор! Но как? Надо разобраться, с чем связан тот факт, что пароль находится в формате base64 в БД.
В результате поиска, мы нашли файл sap.com~tc~sec~ume~core~impl.jar, который находится в папке: C:\usr\sap\DM0\J00\j2ee\cluster\bin\ext\com.sap.security.core.sda\lib\private. Он содержит функции, которые отвечают за хеширование пароля пользователей, проверку их валидности, блокировку пользователей и т.д. Один из основных классов — PasswordHash. Рассмотрим этот класс подробнее.
Он имеет 2 конструктора, один из которых
public PasswordHash(String user, String password)
{
this._user = user;
this._password = password;
this._extra = null;
}
Как мы видим, он инициализирует внутренние переменные user и password.
Чтобы из пароля получить хеш, используется другая функция getHash класса PasswordHash.
Как видно из строки 114, эта функция вызывает createHashWithIterations функцию, которая принимает случайное значение на вход в качестве соли для генерации хеша. Перейдем в функцию createHashWithIterations(salt)
На строке 184 инициализируется переменная output, хранящая байты введенного пароля пользователя.
В строке 185 создается новая переменная pass_n_salt, состоящая из байтов пароля и случайной соли, сгенерированной в строке 112.
Далее в строке 191 вызывается функция hashWithIterations, принимающая на вход 2 параметра — output и pass_n_salt. Заметим, что в output хранится пароль asdQWE123 в байтах.
Так, интересно. Рассмотрим каждую строку по отдельности:
Строка 238 инициализирует переменную output, в которой записываются данные из переменной data (первые байты переменной data – пароль asdQWE123).
Строка 241 инициализируется переменная md класса MessageDigest, которая будет отвечать за хеширование данных.
Строка 243 начинает цикл для хеширования, _iterations = 10000.
Строки 244-245 хешируют данные по алгоритму SHA-512 и записываются в переменную data.
Строка 246, переменная output обнуляется.
Строка 247, в переменную output записываются данные из переменной pass (asdQWE123).
Строка 248, в конец переменной output записываются данные из data.
Получатся, что переменная output будет иметь следующую структуру:
Вся эта логика будет исполнена 10 000 раз, и в последнем шаге цикла в начало переменной output будет добавлен пароль, после пароля – захешированные данные. Тем самым, первые байты переменной output будут состоять из незахешированого пароля.
Блок-схема уязвимого участка кода:
pass = plain_pass
output = [plain_pass]+[random_byte]
i=0
data = sha_512(output)
output = [NULL]
output = [plain_pass]+[data]
for(i)i = i + 1
if i==10000
exit_from_loop
output == «asdQWE123blablabla»
Ошибка программистов состояла в том, что они не хешируют данные в последнем шаге цикла for. Данная уязвимость уже закрыта, и исправление выглядит вот так:
pass = plain_pass
output = [plain_pass]+[random_byte]
i=0
output = [plain_pass]+[data]
data = sha_512(output)
for(i)i = i + 1
if i==10000
exit_from_loop
Как мы видим, SAP поменял порядок шагов, теперь сначала происходит инициализация переменной оutput, а затем хеширование данных переменной.
Заключение
Итак, давайте посмотрим, какие ноты безопасности выпустил SAP для исправления данных уязвимостей.
Разглашение пользователей, логинов – SAP nota 2255990, уязвимость была исправлена 8-ого мая 2016-ого года. Имеет уже CVE-шку CVE-2016-3973 (CVSS v3 7.5).
Список уязвимых версий SAP:
Для исправления SQL injection была выпущена SAP security note 2101079, была исправлена 9-ого февраля 2016-ого года, CVE-2016-2386 (CVSS v3 9.1).
Список уязвимых версий SAP:
Уязвимость с неправильной реализацией хеширования паролей лечится нотой 2191290. Нота выпущена 12 января 2016-ого, CVE-2016-1910 (CVSS v3 5.3).
Список уязвимых версий SAP:
Конечно, кто-то из вас может сказать, мол, установили какой-то «кривой сервер» и нашли баги, но и для этого есть статистика только по разглашению списка пользователей SAP-a. Сканирование проводилось по 7348 SAP-серверам, которые доступны через Интернет.
Статистика по каждому порту
В сумме выходит, что, к уязвимости разглашения информации подвержены около 1013 серверов (~14%)
Ну и еще одна табличка по статистике ДОСТУПНОСТИ сервлета в котором может быть уязвимость SQL injection (если еще не установлен патч)
Суммарно – 2174 серверов, что составляет ~30%
Выходит, что нужно делать?
Нужно патчиться, товарищи администраторы.
Да, и напоследок: хочешь у нас работать и находить еще более крутые баги – присылай свое резюме сюда