diff --git a/README.md b/README.md index a21c6ad..2903f41 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,51 @@ -# httpscan -httpscan是一个扫描指定CIDR网段的Web主机的小工具。和端口扫描器不一样,httpscan是以爬虫的方式进行Web主机发现,因此相对来说不容易被防火墙拦截。 +# httpscan V1.1 -httpscan会返回IP http状态码 Web容器版本 以及网站标题。 -![demo][1] +### Update log -**Usage**:`./httpscan IP/CIDR –t threads` +1. 增加支持指定端口范围扫描。 -Example:`./httpscan.py 10.20.30.0/24 –t 10` +2. 增加指定文件扫描。文件格式为: + ``` + {"ipaddr":"192.168.62.201","ports":[80,8080]} + {"ipaddr":"192.168.62.202","ports":[80,8080]} + ... + ``` + + 每行是一个`json`字符串,包含`ipaddr`和`ports`字段。其中`ports`的值为数组。 + +## Usage + +`python httpscan.py IP/CIDR –t threads -p portrange` + +或 + + `python httpscan.py -f xxx.txt` + +Example: + +`python httpscan.py 10.20.30.0/24 –t 10 -p 80,443` + +`python httpscan.py 10.20.30.0/24 –t 10 -p 8000-8080` + +`python .\httpscan.py -f .\conf\ip.txt` + +``` +httpscan> python .\httpscan.py 192.168.62.201 -p 80,8080 ++---------------------+------+--------------------+------------------------------+ +| IP |Status| Server | Title | ++---------------------+------+--------------------+------------------------------+ +|192.168.62.201:8080 |401 |Oracle XML DB/Oracle|None | ++---------------------+------+--------------------+------------------------------+ +|192.168.62.201:80 |200 |Apache/2.4.39 (Win64|None | ++---------------------+------+--------------------+------------------------------+ +httpscan> python .\httpscan.py -f .\conf\ip.txt ++---------------------+------+--------------------+------------------------------+ +| IP |Status| Server | Title | ++---------------------+------+--------------------+------------------------------+ +|192.168.62.201:8080 |401 |Oracle XML DB/Oracle|None | ++---------------------+------+--------------------+------------------------------+ +|192.168.62.201:80 |200 |Apache/2.4.39 (Win64|None | ++---------------------+------+--------------------+------------------------------+ +``` - [1]: https://raw.githubusercontent.com/zer0h/httpscan/master/log/demo.png diff --git a/httpscan.py b/httpscan.py index d0276a6..3ec51fc 100644 --- a/httpscan.py +++ b/httpscan.py @@ -1,31 +1,68 @@ #!/usr/bin/env python #coding:utf-8 # Author: Zeroh - +import os import re import sys +import json import Queue +import traceback import threading import optparse import requests from IPy import IP +reload(sys) +sys.setdefaultencoding('utf-8') printLock = threading.Semaphore(1) #lock Screen print -TimeOut = 5 #request timeout +TimeOut = 3 #request timeout #User-Agent header = {'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.125 Safari/537.36','Connection':'close'} class scan(): - def __init__(self,cidr,threads_num): + def __init__(self,cidr,threads_num, ports, ipfile): self.threads_num = threads_num - self.cidr = IP(cidr) - #build ip queue + self.ipfile = ipfile + #build ip queue self.IPs = Queue.Queue() - for ip in self.cidr: - ip = str(ip) - self.IPs.put(ip) + + if self.ipfile != "": + assets = self.readFile() + for ip, ports in assets: + for port in ports: + self.IPs.put('%s:%d' % (ip, port)) + else: + for ip in IP(cidr): + ip = str(ip) + for port in ports: + self.IPs.put('%s:%d' % (ip, port)) + + def readFile(self): + assets = [] + if os.path.exists(self.ipfile): + with open(self.ipfile) as f: + contents = f.read() + if ':' in contents: # ip:port格式 + for line in contents.split('\n'): + if line : + ip_port = line.split(':') + ip = ip_port[0] + port = [int(ip_port[1])] + assets.append((ip, port)) + else: # json格式 + for line in contents.split('\n'): + try: + jsline = json.loads(line) + except: + continue + assets.append((jsline['ipaddr'], jsline['ports'])) + + else: + print 'file not exists' + return assets + def request(self): with threading.Lock(): @@ -34,7 +71,7 @@ def request(self): try: r = requests.Session().get('http://'+str(ip),headers=header,timeout=TimeOut) status = r.status_code - title = re.search(r'(.*)', r.text) #get the title + title = re.search(r'(.*)', r.content) #get the title if title: title = title.group(1).strip().strip("\r").strip("\n")[:30] else: @@ -44,14 +81,16 @@ def request(self): banner += r.headers['Server'][:20] #get the server banner except:pass printLock.acquire() - print "|%-16s|%-6s|%-20s|%-30s|" % (ip,status,banner,title) - print "+----------------+------+--------------------+------------------------------+" - - #Save log - with open("./log/"+self.cidr.strNormal(3)+".log",'a') as f: - f.write(ip+"\n") - - except Exception,e: + try: + print "|%-21s|%-6s|%-20s|%-30s|" % (ip,status,banner,title.decode('utf-8')) + print "+---------------------+------+--------------------+------------------------------+" + #Save log + with open("./log/result.log",'a') as f: + f.write("%s %s %s %s\n" % (ip,status,banner,title)) + except: + traceback.print_exc() + pass + except: printLock.acquire() finally: printLock.release() @@ -62,19 +101,45 @@ def run(self): t = threading.Thread(target=self.request) t.start() + if __name__ == "__main__": parser = optparse.OptionParser("Usage: %prog [options] target") parser.add_option("-t", "--thread", dest = "threads_num", default = 10, type = "int", help = "[optional]number of theads,default=10") + parser.add_option("-p", "--port", dest = "ports", + default = "80", type = "str", + help = "[optional]e.g. 80,443 or 80-443, default 80") + parser.add_option("-f", "--file", dest = "file", + default = "", type = "str", + help = "[optional]e.g. conf/ip.txt") (options, args) = parser.parse_args() - if len(args) < 1: + + # options.file = ".\conf\ip.txt" + if len(args) < 1 and options.file == "": parser.print_help() sys.exit(0) + elif len(args) == 1: + try: + IP(args[0]) + except: + parser.print_help() + sys.exit(0) + + cidr = '' if options.file != "" else args[0] + + print "+---------------------+------+--------------------+------------------------------+" + print "| IP |Status| Server | Title |" + print "+---------------------+------+--------------------+------------------------------+" - print "+----------------+------+--------------------+------------------------------+" - print "| IP |Status| Server | Title |" - print "+----------------+------+--------------------+------------------------------+" + ports = [] + if ',' in options.ports: + ports = [int(port) for port in options.ports.split(',')] + elif '-' in options.ports: + start, end = options.ports.split('-') + ports = [port for port in range(int(start), int(end)+1)] + else: + ports.append(int(options.ports)) - s = scan(cidr=args[0],threads_num=options.threads_num) + s = scan(cidr=cidr,threads_num=options.threads_num, ports=ports, ipfile=options.file) s.run()