SЯP wrong эncяyptioи или как скомпрометировать всех пользователей в SAP JAVA

Всем привет, меня зовут Ваагн Варданян (тут нет опечатки, как многие думают :)), работаю я в DSec исследователем безопасности SAP-систем, и в этой небольшой статье расскажу о связке уязвимостей в SAP, использование которых может привести к компрометации системы и как результат — доступу к критичной бизнес информации.

С каждой новой версией SAP NW, приложения становятся все более защищёнными, и уязвимости не дают скомпрометировать систему полностью. Но бывают ситуации, когда несколько проблем безопасности, используемых вместе, все же позволяют атакующим добиться своих целей. Сегодня мы расскажем о том, как скомпрометировать SAP NW с помощью связки уязвимостей.

В статье сначала мы поговорим о возможности получения информации из системы, об эксплуатации уязвимости, основанной на утечке информации, далее — об эскалации привилегий. Все уязвимости были найдены в последних (на момент исследования) версиях SAP (SAP NW AS JAVA 7.4). Ну что ж, понеслось.


17c5a2a601e64fe7abbd3e4c5c6df218.jpeg

Введение
На сервере SAP AS JAVA есть множество стандартных приложений, которые хранятся в папке C:\usr\sap\%SID%\J00\j2ee\cluster\apps\sap.com, где %SID% — это SID SAP-системы. Так, наша тестовая система имеет SID DM0.
Из приведенного ниже скриншота можно понять, что в стандартной сборке SAP NW присутствует более 1400 компонентов (если установить полную сборку, то выйдет и более 2000), которые могут быть вызваны пользователями SAP’a с различными правами.


2729fc9ec6554f008bc1b97cb864d2fd.png

Каждый компонент, который можно вызвать, имеет некий уровень доступа, описанный в файлах web.xml, portalapp.xml. Всего существует 4 вида прав доступа к компонентам:


fe64dcf454864ab6bc479c619e6fbb19.png

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

Уязвимость разглашения информации
После поиска файлов конфигураций файл дескрипторов, в которых есть описание прав доступа


или оно полностью отсутствует. Был найден webdynpro компонент tc~rtc~coll.appl.rtc~wd_chat. Вот его конфигурационный файл:


f6a42a8254134b4bafc0f60548c09f0f.png

К данному сервису можно обратится по следующему адресу:
http:/SAP_IP: SAP_PORT/webdynpro/resources/sap.com/tc~rtc~coll.appl.rtc~wd_chat/Chat#

Oткрываем страницу и видим некоторый функционал по отправке сообщений. Среди прочего, в нем есть возможность добавления адресатов (пользователей):


db5c40bd2fd44896ab40030c7d4885bb.png

Нажав на запись add participant, можем увидеть окно, которое предлагает поискать пользователя без авторизации.


525a809d615d4bdc8ed98aad51a8fdf7.png

Очень интересно, что в списке был найден пользователь с именем 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\
Как видно из конфигурационного файла, сервис также доступен анонимно:


dcdb04bf37704638832ce541641e5781.png

Тег servlet-class показывает, что доступ к данному сервлету можно получить с помощью SOAP-запросов. Теперь перед нами стоит задача найти структуру SOAP-запроса. Известно, что структуру запросов можно узнать, найдя wsdl-файл, в котором описан данный сервис. Получается, что нам надо найти wsdl файл, содержащий запись UDDISecurityImplBean. Используя total commander, запустим поиск.


f96867e233874949aab3b5a74b1728cb.png

На сервере нашелся файл, содержащий нужную нам информацию. Поскольку он имеет wsdl-структуру, преобразовать его в SOAP-запрос можно, используя специальную утилиту. Мы выяснили, что в данном файле описано 2 метода: applyPermission и deletePermissionById.


b60e5b715cdc49609cf6d031c6f2e80f.png

Выберем второй метод (deletePermissionById), сгенерируем SOAP-запрос и отправим его на сервер SAP.


aada39bfb24945b8bf014241aa79df87.png

В ответе получим:


5a081bae4bd949e3b5cb0c796510f951.png

Ответ вернул 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

После декомпиляции файла можем увидеть следующие классы:


60191a095fb2408d9110e6abe1b41a66.png

Сама обработка запроса происходит в классе UDDISecurityBean.


04b8d803f0eb4628aa119d1c916c1516.png

Когда мы отправляем запрос deletePermissionById, видно, как появляется конструктор PermissionsDao (), который вызывает функцию deletePermision. Переходим в класс PermissionsDao.


c4d78391971a45f89c338fe8d85b2f4e.png

Данные, передаваемые через SOAP-запрос, используются для обращения к базе данных сервера SAP без фильтрации. Можно предположить, что тут есть SQL injection. Чтобы наверняка убедиться в этом, необходимо отправить специальный запрос для SQL injection и посмотреть логи базы данных сервера SAP. Файлы логов базы данных находятся по умолчанию в папке C:\usr\sap\%SID%\J00\j2ee\cluster\server0\log\system\ и называются по умолчанию database_NN.N. log, где N — число от 0 до 9 включительно.
Отправим следующий запрос:


1a6698b6876d42efb6b7fad2af04366f.png

В ответе также получим 200-ый код:


09f8d26f362e45c3ad7ace47aef89171.png

Но в логах базы данных можно заметить следующее:


cdd6e70dcd37442f8ddf49e57230049d.png

Теперь точно можно говорить о том, что у нас есть анонимная SQL-инъекция в базу данных SAP. Удалим логи с сервера и отправим следующий запрос. Если на сервере в логах не возникнут ошибки, то структура SQL-запроса правильная.


2826522ca7274d2cb8c15d93e16cd343.png

Ошибок нет.


bb7a74568f05426aa0126d9cee29f89c.png

Теперь новая задача — найти запрос, который поможет получить критичные данные из базы данных SAP, например, хеш пароля Jon Snow. Из документации SAP NW AS JAVA известно, что данные о пользователях (логин, именa, хеши пароля) хранятся в таблице UME_STRINGS.
Запрос для получения всех данных из UME_STRINGS имеет следующий вид:


56eeea8021c14dfc96ca8165f7e01d23.png
373c5c0d6e1648d088deb32a3f62daba.png

Как видно, данная SQL injection vulnerability не error based, а адаптер, который используется в этом сервлете и не поддерживает sleep () функцию. Будем использовать метод умножения таблиц для увеличения времени обработки запроса и превратим данную уязвимость в time-based SQL injection. Для этого надо найти таблицу, в которой всегда во всех SAP-серверах будут содержаться данные. Такая таблица — J2EE_CONFIG, в которой хранится информация о конфигурации компонентов.

Oправим следующий запрос:


0e44997da2bc4de596c14f3262a08ea9.png

Сервер, получая запрос, пробует извлечь данные из БД, предварительно умножив 2 таблицы — UME_STRINGS и J2EE_CONFIG. Поскольку в таблицах хранятся данные большого объёма, произойдет временная нагрузка на сервер.


690fd3e89969429894e1dfaa3fc102b5.png

А ответ мы получаем спустя 32 секунды. И — готово: time-based SQL injection.


321510a9f49e445d8740045bcb1e2b4d.png

Получение хеша администратора
Как уже говорилось выше, захешированные пароли хранятся в таблице UME_STRINGS, которая имеет следующую структуру:


7d3e6195380c4b8e87e3616c9b3e6a55.png

В таблице 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}

Будем перебирать их, и, если символ, который мы выбрали, совпадает с символом хеша из пароля, у нас будет задержка запроса.


702677c5091146408e502dedbe95069a.png

Ответ будет задержан на 1 секунду.


eec05a549c394875badcde743fd74b43.png

Автоматизируя этот процесс, получим хеш от пользователя Jon Snow.

{SHA-512, 10000, 24}YXNkUVdFMTIzzbAIcuqnw5RzpmdgZ38PWjhBeaGzHkV6XINN7ZDqxqgr0nYxfCaE5ncdK7kzzkzryJAn42qv9YlY034Llr4b8Rv1534chnIf1i8jZE6ylzTV5XuzvUlaXQ== 


8462e5a5a51d470ba1d307225ef3efa2.png

Как видно, пароль захеширован алгоритмом SHA-512. На этом исследование бы закончилось, если бы не третья уязвимость.

Криптографическая ошибка, эскалации привилегий
Эта уязвимость была найдена случайно :-)
С помощью SQL injection получили хеш пароля пользователя Jon Snow:

{SHA-512, 10000, 24}YXNkUVdFMTIzzbAIcuqnw5RzpmdgZ38PWjhBeaGzHkV6XINN7ZDqxqgr0nYxfCaE5ncdK7kzzkzryJAn42qv9YlY034Llr4b8Rv1534chnIf1i8jZE6ylzTV5XuzvUlaXQ== 

Хммм. Как видно, в конце хеша есть символы ==. А что будет, если мы сделаем base64decode?


8fa4c012747749e6bba2cab6a5be6b45.png
3d37cfd6290b4495b499f7920f26957e.jpg

Что? Как? Пароль? Есть единственный способ проверить, действительно ли это пароль от Jon’a — зайти на портал, используя логин J.Snow и пароль asdQWE123.


8b8597d421874165968cbb0ed8ea080b.png

Ура! Я администратор! Но как? Надо разобраться, с чем связан тот факт, что пароль находится в формате 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. Рассмотрим этот класс подробнее.


068133b9cea846c1990025fffc488912.png

Он имеет 2 конструктора, один из которых

  public PasswordHash(String user, String password) 
  { 
    this._user = user; 
    this._password = password; 
    this._extra = null; 
  } 

Как мы видим, он инициализирует внутренние переменные user и password.
Чтобы из пароля получить хеш, используется другая функция getHash класса PasswordHash.


3d5cc808deb546f880cb12b36ec0cb81.png

Как видно из строки 114, эта функция вызывает createHashWithIterations функцию, которая принимает случайное значение на вход в качестве соли для генерации хеша. Перейдем в функцию createHashWithIterations (salt)


3bc87607881442b3b7c25fb797a31525.png

На строке 184 инициализируется переменная output, хранящая байты введенного пароля пользователя.
В строке 185 создается новая переменная pass_n_salt, состоящая из байтов пароля и случайной соли, сгенерированной в строке 112.
Далее в строке 191 вызывается функция hashWithIterations, принимающая на вход 2 параметра — output и pass_n_salt. Заметим, что в output хранится пароль asdQWE123 в байтах.


e6ff3b72e1c6443abe568ea516492f30.png

Так, интересно. Рассмотрим каждую строку по отдельности:
Строка 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 будет иметь следующую структуру:


5530c7804bfe4b869daa3305ea5576ff.png

Вся эта логика будет исполнена 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


2a9584cd6ab94250ace474fc878540bc.png

Как мы видим, SAP поменял порядок шагов, теперь сначала происходит инициализация переменной оutput, а затем хеширование данных переменной.

Заключение

Итак, давайте посмотрим, какие ноты безопасности выпустил SAP для исправления данных уязвимостей.

Разглашение пользователей, логинов — SAP nota 2255990, уязвимость была исправлена 8-ого мая 2016-ого года. Имеет уже CVE-шку CVE-2016–3973 (CVSS v3 7.5).
Список уязвимых версий SAP:


eb0f8b012eeb4ed8b712aa9f626c630a.png

Для исправления SQL injection была выпущена SAP security note 2101079, была исправлена 9-ого февраля 2016-ого года, CVE-2016–2386 (CVSS v3 9.1).
Список уязвимых версий SAP:


6177c9277aab49afa5ed637e5fa08b78.png

Уязвимость с неправильной реализацией хеширования паролей лечится нотой 2191290. Нота выпущена 12 января 2016-ого, CVE-2016–1910 (CVSS v3 5.3).
Список уязвимых версий SAP:


d7824204fec04737b84f8f1ee0f92ab5.png

Конечно, кто-то из вас может сказать, мол, установили какой-то «кривой сервер» и нашли баги, но и для этого есть статистика только по разглашению списка пользователей SAP-a. Сканирование проводилось по 7348 SAP-серверам, которые доступны через Интернет.


5bcdf43bd7d44d639de349548d35a6cf.png

Статистика по каждому порту


6859ab7bf42f492e83b5bb9566371774.png

В сумме выходит, что, к уязвимости разглашения информации подвержены около 1013 серверов (~14%)


0f8e6cbc62274a40a078ea6878ee57b4.png

Ну и еще одна табличка по статистике ДОСТУПНОСТИ сервлета в котором может быть уязвимость SQL injection (если еще не установлен патч)


fb2b782366894110853d988018abd41f.png

Суммарно — 2174 серверов, что составляет ~30%

Выходит, что нужно делать?
Нужно патчиться, товарищи администраторы.
Да, и напоследок: хочешь у нас работать и находить еще более крутые баги — присылай свое резюме сюда

© Habrahabr.ru