
ActiveMQ es un broker que incluye clientes compatibles con JMS y permite la comunicación entre sistemas que utilizan diversos lenguajes, no solo Java. También es un servidor de mensajería de código abierto y patrones de integración que mantiene la consistencia y persistencia entre cada sistema mediante funciones de clúster, bases de datos y FileSystem.
OpenWire es un protocolo de mensajería binaria utilizado en Apache ActiveMQ. Está diseñado para la transmisión eficiente de datos entre el broker (ActiveMQ) y los clientes.
CVE-2023-46604 es una vulnerabilidad en el protocolo OpenWire (en el proceso de marshalling) de ActiveMQ. Un atacante remoto con acceso de red al broker o cliente OpenWire basado en Java puede manipular los tipos de clase serializados en el protocolo OpenWire para ejecutar comandos de shell arbitrarios y hacer que el cliente o el broker (respectivamente) instancien cualquier clase presente en el classpath. Es una de las técnicas de ataque representativas utilizadas por el grupo APT Andariel.
CVE-2023-46604 se lleva a cabo manipulando llamadas XML o insertando datos maliciosos en el proceso de intercambio de datos a través del protocolo OpenWire para ejecutar comandos remotos. El protocolo OpenWire se encarga básicamente de la serialización y transmisión de datos; la vulnerabilidad explota el hecho de que en este proceso de serialización se permiten tipos de clase maliciosos.
[Figura] POC
[Figura] XML
Vamos a examinarlo mediante el análisis de los paquetes enviados al protocolo OpenWire en ActiveMQ 5.17.3. Primero, antes del análisis de paquetes, hay que conocer e interiorizar la forma del paquete.패킷 Header
+----------------------------------------------------------------------------------+
| Packet Length | Command | Command Id | Command response required | CorrelationId |
|---------------|---------|------------|---------------------------|---------------|
| 00000066 | 1f | 00000000 | 00 | 00000000 |
+----------------------------------------------------------------------------------+
패킷 Body
+--------------------------------------------------------------------------------------+
| not-null | not-null | classname-size | classname | not-null | message-size | message |
|----------|----------|----------------|-----------|----------|--------------|---------|
| 01 | 01 | 0043 | ..... | 01 | 0012 | ..... |
+--------------------------------------------------------------------------------------+
OpenWire 패킷 Body 형식
[=If not-null is 1===========]
+----------+ [ +-------+----------------+ ]
| not-null | [ | size | encoded-string | ]
+----------+ [ +-------+----------------+ ]
| byte | [ | short | size octects | ]
+----------+ [ +-------+----------------+ ]
[============================]
[Figura 1] Comprobación del paquete OpenWire en Wireshark
En [Figura 1] se muestra el payload real que contiene el formato de Header y Body del paquete descrito anteriormente (por conveniencia, se utiliza una invocación a través de FileSystem en lugar de la invocación mediante ClassPath del método POC existente). Veamos más detalladamente en el código qué efecto producen estos contenidos del paquete y por qué deben enviarse así.
[Figura 2] Significado del Header del paquete
패킷 Header 끊어서 파악
+----------------------------------------------------------------------------------+
| Packet Length | Command | Command Id | Command response required | CorrelationId |
|---------------|---------|------------|---------------------------|---------------|
| 00000066 | 1f | 00000000 | 00 | 00000000 |
+----------------------------------------------------------------------------------+
Packet Length : 00000066 / OpenWire 같은 프로토콜은 기본적으로 패킷을 길이를 명시.
Command : 1f / 는 ExecptionResponse(31)을 명시 하기 위한 설정 31은 16진수(hex)로 1f.
Command Id : 00000000 / 는 int 형태 4바이트 형태 임으로 16진수 표현시 00 00 00 00.
Command response required : 00 / 는 boolean 타입으로 True(01), False(00) 에서 False.
CorrelationId : Command Id와 같은 유형
[Figura 2] añade una explicación sobre la forma de cada elemento del encabezado y el código asignado para ayudar a comprender el Header del paquete anterior. Ahora que hemos visto el Header, veamos cómo atraviesa el código el contenido del Body.
[Figura 3] Significado del Body del paquete
패킷 Body 내용 끊어서 파악
+--------------------------------------------------------------------------------------+
| not-null | not-null | classname-size | classname | not-null | message-size | message |
|----------|----------|----------------|-----------|----------|--------------|---------|
| 01 | 01 | 0043 | ..... | 01 | 0012 | ..... |
+--------------------------------------------------------------------------------------+
not-null (01) | / [그림 3]의 첫번째 함수에서 not-null pass
not-null (01) | classname-size (0043) | classname / [그림 3]의 두번째 함수에서 not-null pass, 세번째 함수에서 사이즈 체크 위한
not-null (01) | message-size (0012) | message / [그림 3]의 두번째 함수에서 not-null pass, 세번째 함수에서 사이즈 체크 위한
[Figura 3] muestra cómo actúan los valores establecidos en el Body del paquete. Finalmente, a través de [Figura 4], se carga la clase y se instancia mediante los datos serializados.
[Figura 4] Ejecución de RCE mediante createThrowable
[Figura 5] Ejemplo de ejecución de la calculadora RCE
[Figura 5] Git Diff (5.17.2 -> 5.17.6)
[Figura 5] Función validate
A diferencia de otros análisis, la razón por la que se analizó el paquete de ataque en esta vulnerabilidad fue para comprender y estudiar cómo se ejecuta realmente el ataque.
Al examinar detenidamente la estructura y el flujo del paquete, se pudo identificar claramente el método concreto que utiliza el atacante para comprometer el sistema y su mecanismo de funcionamiento.
(nist) https://nvd.nist.gov/vuln/detail/cve-2023-46604
(POC) https://github.com/X1r0z/ActiveMQ-RCE/tree/main
(blog) https://attackerkb.com/topics/IHsgZDE3tS/cve-2023-46604/rapid7-analysis