
Reproducer for CVE-2026-43865 — Apache Camel camel-hazelcast default-configured instance unsafe Java deserialization (RCE)
Этот проект демонстрирует уязвимость десериализации Java в компоненте camel-hazelcast Apache Camel, отслеживаемую как CVE-2026-43865. Когда Camel сам создает Config Hazelcast — т.е. когда не предоставлены пользовательские HazelcastInstance, hazelcastConfigUri или ссылка на bean Config — он не применяет фильтр десериализации Java (ни JavaSerializationFilterConfig от Hazelcast, ни ObjectInputFilter со стороны Camel). Таким образом, объекты, поступающие по протоколу кластера Hazelcast, десериализуются внутри слоя сериализации Hazelcast (ObjectInputStream.readObject) без каких-либо ограничений на классы. Злоумышленник, который может присоединиться к кластеру или иным образом получить к нему доступ, может опубликовать специально созданный сериализованный объект, который десериализуется на каждом узле Camel — удаленное выполнение кода, присутствующее по умолчанию и не требующее явной настройки конечной точки.
Совет: https://camel.apache.org/security/CVE-2026-43865.html
Это отличается от советов библиотеки Hazelcast CVE-2016-10750 / CVE-2022-36418 (которые касаются собственного кода Hazelcast). Здесь недостаток в том, что Camel не применяет защиту десериализации, предоставляемую Hazelcast.
Когда экземпляр/конфигурация не предоставлены, HazelcastDefaultComponent строит экземпляр из стандартной конфигурации и не устанавливает фильтр сериализации:
// HazelcastDefaultComponent - affected 4.18.2
if (hazelcastInstance == null && config == null) {
config = new XmlConfigBuilder().build();
config.getProperties().setProperty("hazelcast.version.check.enabled", "false");
config.getProperties().setProperty("hazelcast.phone.home.enabled", "false");
hzInstance = Hazelcast.newHazelcastInstance(config); // no JavaSerializationFilterConfig
}
Исправление вставляет фильтр по умолчанию (белый список java./javax./org.apache.camel., черный список java.net.) на экземплярах, которые Camel создает сам, оставляя предоставленные пользователем Config/HazelcastInstance нетронутыми:
// fixed 4.18.3 / 4.21.0
config.getProperties().setProperty("hazelcast.phone.home.enabled", "false");
HazelcastSerializationFilterHelper.applyDefault(config); // <-- added
hzInstance = Hazelcast.newHazelcastInstance(config);
Любая структура, передающая десериализованный объект Camel, запускает уязвимость. Этот PoC использует потребитель очереди в режиме Poll, где IQueue.poll() десериализует первый элемент до того, как Camel его маршрутизирует:
// HazelcastQueueConsumer (Poll mode) - affected
final Object body = queue.poll(config.getPollingTimeout(), TimeUnit.MILLISECONDS); // readObject, no filter
exchange.getIn().setBody(body);
from("hazelcast-queue:cve?queueConsumerMode=Poll") // Camel builds the default (unfiltered) instance
.log("Consumed: ${body.class.name}");
Жертвой является узел Camel (член кластера Hazelcast). Атакующий — любая сторона, которая может получить доступ к кластеру. Этот самодостаточный PoC запускает клиент Hazelcast (атакующий), который присоединяется к тому же одному члену кластера (dev на 127.0.0.1:5701) и помещает гаджет в очередь, которую потребляет жертва.
CVE-2026-43865/
├── pom.xml # camel-hazelcast 4.18.2 + commons-collections 3.2.1 (gadget)
├── Dockerfile # runs the app (--add-opens for gadget build; loopback cluster address)
├── docker-compose.yml
├── README.md
└── src/main/
├── java/com/example/
│ ├── Application.java
│ ├── VictimRoute.java # victim: hazelcast-queue consumer (default instance)
│ ├── Gadget.java # CommonsCollections6 gadget, fires during Hazelcast deserialization
│ └── ExploitController.java # attacker: Hazelcast client offers the gadget to the queue
└── resources/
└── application.properties
В реальной атаке сериализованные байты создаются собственным клиентом/членом Hazelcast атакующего; только жертве нужна цепочка гаджетов в своем classpath. Этот PoC создает гаджет внутри процесса, поэтому JVM запускается с
--add-opens java.base/java.util=ALL-UNNAMED— деталь построения гаджета, не связанная с уязвимостью.
mvn clean package -DskipTests
docker compose up -d --build
curl -s http://localhost:8080/exploit/attack
# -> Offered gadget to hazelcast queue 'cve' as a cluster client.
# The Camel node's queue.poll() deserialized it via Hazelcast (no filter).
#
# >>> RCE proof — /tmp/pwned exists: true
docker exec cve-2026-43865 ls -la /tmp/pwned
docker compose down
Любой маршрут Camel, использующий потребитель hazelcast (hazelcast-topic, hazelcast-queue, hazelcast-seda, hazelcast-map, hazelcast-multimap, hazelcast-replicatedmap, hazelcast-list, hazelcast-set), или HazelcastAggregationRepository / HazelcastIdempotentRepository, когда управляемый экземпляр создается из конфигурации по умолчанию Camel и атакующий может достичь кластера.
HazelcastInstance / hazelcastConfigUri / bean Config).commons-collections:3.2.1).Обновитесь до 4.14.8 / 4.18.3 / 4.21.0 (CAMEL-23414), которые применяют фильтр JavaSerializationFilterConfig Hazelcast по умолчанию к экземплярам, которые Camel создает из своей собственной конфигурации по умолчанию.
До обновления:
JavaSerializationFilterConfig или общесистемный JVM -Djdk.serialFilter=!java.net.**;java.**;javax.**;org.apache.camel.**;!*).Это воспроизведение предоставлено только для исследования безопасности и авторизованного тестирования, для публично раскрытой и исправленной уязвимости. Не используйте его против систем без явного разрешения.
| Свойство | Значение |
|---|
| Компонент | camel-hazelcast (любой потребитель + HazelcastAggregationRepository / HazelcastIdempotentRepository) |
| Уязвимый класс | org.apache.camel.component.hazelcast.HazelcastDefaultComponent (создание экземпляра по умолчанию) |
| CWE | CWE-502: Десериализация недоверенных данных |
| Воздействие | Удаленное выполнение кода (RCE) на каждом узле Camel в кластере |
| Триггер | Потребитель/репозиторий hazelcast, управляемый экземпляр которого создается из конфигурации по умолчанию Camel |
| Уязвимые версии | С 4.0.0 до 4.14.8, с 4.15.0 до 4.18.3, с 4.19.0 до 4.21.0 |
| Исправленные версии | 4.14.8, 4.18.3, 4.21.0 |
| JIRA | CAMEL-23414 |
| Автор отчета | gaorenyusi |