当前位置: 首页 > news >正文

免费网站模板 怎么用广州最新消息今天

免费网站模板 怎么用,广州最新消息今天,南宁做网站外包,网站维护主要做哪些声明 本人菜鸟一枚#xff0c;为了完成作业#xff0c;发现网上所有的关于wafw00f的源码解析都是这抄那那抄这的#xff0c;没有新东西#xff0c;所以这里给出一个详细的源码解析#xff0c;可能有错误#xff0c;如果有大佬发现错误#xff0c;可以在评论区平和的指出…声明 本人菜鸟一枚为了完成作业发现网上所有的关于wafw00f的源码解析都是这抄那那抄这的没有新东西所以这里给出一个详细的源码解析可能有错误如果有大佬发现错误可以在评论区平和的指出如果觉得这篇文章对你有帮助请点点赞和收藏^_^ 这里不展示wafw00f的安装使用这些可以去github上看wafw00f的readme或者网上有挺多教程的只解析其源码 前言 昨晚写了关于IdentYwaf的的源码解析IdentYwaf源码详细解析 在这篇文章中我说到wafw00f在识别waf的能力上远远强于IdentYwaf所以我就在写一篇wafw00f来证明为什么wafw00f比IdentYwaf要强的多 wafw00f工具可以去github下载wafw00f 概述 至于什么是waf以及wafw00f是做什么的这里大概总结一下 waf就是网络程序防火墙可以识别出一些非法语句例如XSS和SQL注入语句然后拦截这些语句 wafw00f以及IdentYwaf这些工具就是识别网站用了市面上哪些waf的是渗透测试前期收集信息必不可少的一部分 wafw00f源码分析 wafw00f工作流程 和网上当然是不一样的啦 这里可以看出wafw00f的工作流程是比较简单的它的核心就一个identwaf函数 wafw00f文件目录 └─wafw00f│ .gitignore│ CODE_OF_CONDUCT.md│ CREDITS.txt│ Dockerfile│ LICENSE│ Makefile│ MANIFEST.in│ README.md│ setup.py ├─docs│ conf.py│ index.rst│ Makefile│ wafw00f.8│ └─wafw00f│ main.py★★★│ manager.py│ wafprio.py│ __init__.py│ ├─bin│ wafw00f│ ├─lib│ asciiarts.py│ evillib.py★★★│ __init__.py│ └─plugins★★★★aesecure.pyairee.pyairlock.pyalertlogic.pyaliyundun.py...__init__.py wafw00f核心流程分析 我分析wafw00f的过程主要分成两个部分因为这个工具最重要的就两个文件main.py和evillib.py我就分成两个文件来解析这里先解析evillib.py再解析main.py至于用到的其它文件我就把里面的函数当成集成在main.py和evillib.py文件里面了 最后我们还会详细解析plugins目录里面的一些东西这个插件目录是wafw00f的超级核心 evillib.py 整个文件内容 #!/usr/bin/env pythonCopyright (C) 2022, WAFW00F Developers. See the LICENSE file for copying permission. import time import logging from copy import copyimport requests import urllib3 try:from urlparse import urlparse, urlunparse except ImportError:from urllib.parse import urlparse, urlunparse# For requests 2.16, this should be used. # requests.packages.urllib3.disable_warnings(InsecureRequestWarning) # For requests 2.16, this is the convention urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)def_headers {Accept : text/html,application/xhtmlxml,application/xml;q0.9,image/webp,image/apng,*/*;q0.8,application/signed-exchange;vb3,Accept-Encoding: gzip, deflate,Accept-Language: en-US,en;q0.9,DNT : 1, # Do Not Track request headerUser-Agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3770.100 Safari/537.36,Upgrade-Insecure-Requests: 1 #} proxies {}def urlParser(target):log logging.getLogger(urlparser)ssl Falseo urlparse(target)if o[0] not in [http, https, ]:log.error(scheme %s not supported % o[0])returnif o[0] https:ssl Trueif len(o[2]) 0:path o[2]else:path /tmp o[1].split(:)if len(tmp) 1:port tmp[1]else:port Nonehostname tmp[0]query o[4]return (hostname, port, path, query, ssl)class waftoolsengine:def __init__(self, targethttps://example.com, debuglevel0, path/, proxiesNone,redirTrue, headNone):self.target targetself.debuglevel debuglevelself.requestnumber 0self.path pathself.redirectno 0self.allowredir redirself.proxies proxiesself.log logging.getLogger(wafw00f)if head:self.headers headelse:self.headers copy(def_headers) #copy object by value not reference. Fix issue #90def Request(self, headersNone, pathNone, params{}, delay0, timeout7):try:time.sleep(delay)if not headers:h self.headerselse: h headersreq requests.get(self.target, proxiesself.proxies, headersh, timeouttimeout,allow_redirectsself.allowredir, paramsparams, verifyFalse)self.log.info(Request Succeeded)self.log.debug(Headers: %s\n % req.headers)self.log.debug(Content: %s\n % req.content)self.requestnumber 1return reqexcept requests.exceptions.RequestException as e:self.log.error(Something went wrong %s % (e.__str__())) 主要有三个东西urlParser()函数、waftoolsengine类、默认的Headers 默认Headers def_headers {Accept : text/html,application/xhtmlxml,application/xml;q0.9,image/webp,image/apng,*/*;q0.8,application/signed-exchange;vb3,Accept-Encoding: gzip, deflate,Accept-Language: en-US,en;q0.9,DNT : 1, # Do Not Track request headerUser-Agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3770.100 Safari/537.36,Upgrade-Insecure-Requests: 1 #}没什么好讲的跳 urlParser(target)函数 def urlParser(target):log logging.getLogger(urlparser)ssl Falseo urlparse(target)if o[0] not in [http, https, ]:log.error(scheme %s not supported % o[0])returnif o[0] https:ssl Trueif len(o[2]) 0:path o[2]else:path /tmp o[1].split(:)if len(tmp) 1:port tmp[1]else:port Nonehostname tmp[0]query o[4]return (hostname, port, path, query, ssl)wafw00f如果不提供完整的url和端口号会出现如下错误 ERROR:wafw00f:Something went wrong HTTPSConnectionPool(host‘117.39.30.114’, port443): Max retries exceeded with url: / (Caused by SSLError(SSLError(1, ‘[SSL: UNSUPPORTED_PROTOCOL] unsupported protocol (_ssl.c:1129)’))) ERROR:wafw00f:Site 117.39.30.114 appears to be down 其中错误是在urlparse(target)发出来的 https:// 默认端口号为443http://默认端口号为80若遇到仅开放8080等其他情况的网站需要指定网站端口号才能顺利扫描识别waf且务必告知是https访问还是http访问。否则urlparse均会抛出错误。 如果urlparse顺利过关则会返回如下内容 scheme://netloc/path;params?query#fragment urlParser则返回 hostname netloc.split(“:”)[0] prot netloc.split(“:”)[1] path path query query ssl True if scheme “https” else False 这个函数就是关于url解析的一个函数 waftoolsengine类 class waftoolsengine:def __init__(self, targethttps://example.com, debuglevel0, path/, proxiesNone,redirTrue, headNone):self.target targetself.debuglevel debuglevelself.requestnumber 0self.path pathself.redirectno 0self.allowredir redirself.proxies proxiesself.log logging.getLogger(wafw00f)if head:self.headers headelse:self.headers copy(def_headers) #copy object by value not reference. Fix issue #90def Request(self, headersNone, pathNone, params{}, delay0, timeout7):try:time.sleep(delay)if not headers:h self.headerselse: h headersreq requests.get(self.target, proxiesself.proxies, headersh, timeouttimeout,allow_redirectsself.allowredir, paramsparams, verifyFalse)self.log.info(Request Succeeded)self.log.debug(Headers: %s\n % req.headers)self.log.debug(Content: %s\n % req.content)self.requestnumber 1return reqexcept requests.exceptions.RequestException as e:self.log.error(Something went wrong %s % (e.__str__()))这个类主要有两个部分初始化和请求不难看懂后面的main.py会实例化一个这个类并不断用实例化对象进行操作所以我们后面再来详细解析这里面都做了什么操作 main.py 先来看看WAFW00F类继承自waftoolsengine类 class WAFW00F(waftoolsengine):xsstring scriptalert(XSS);/scriptsqlistring UNION SELECT ALL FROM information_schema AND or SLEEP(5) or lfistring ../../../../etc/passwdrcestring /bin/cat /etc/passwd; ping 127.0.0.1; curl google.comxxestring !ENTITY xxe SYSTEM file:///etc/shadow]pwnhack;/pwndef __init__(self, targetwww.example.com, debuglevel0, path/,followredirectTrue, extraheaders{}, proxiesNone):self.log logging.getLogger(wafw00f)self.attackres Nonewaftoolsengine.__init__(self, target, debuglevel, path, proxies, followredirect, extraheaders)self.knowledge dict(genericdict(foundFalse, reason), wafnamelist())self.rq self.normalRequest()def normalRequest(self):return self.Request()def customRequest(self, headersNone):return self.Request(headersheaders)def nonExistent(self):return self.Request(pathself.path str(random.randrange(100, 999)) .html)def xssAttack(self):return self.Request(pathself.path, params {s: self.xsstring})def xxeAttack(self):return self.Request(pathself.path, params {s: self.xxestring})def lfiAttack(self):return self.Request(pathself.path self.lfistring)def centralAttack(self):return self.Request(pathself.path, params{a: self.xsstring, b: self.sqlistring, c: self.lfistring})def sqliAttack(self):return self.Request(pathself.path, params {s: self.sqlistring})def oscAttack(self):return self.Request(pathself.path, params {s: self.rcestring})def performCheck(self, request_method):r request_method()if r is None:raise RequestBlocked()return r# Most common attacks used to detect WAFsattcom [xssAttack, sqliAttack, lfiAttack]attacks [xssAttack, xxeAttack, lfiAttack, sqliAttack, oscAttack]def genericdetect(self):reason reasons [Blocking is being done at connection/packet level.,The server header is different when an attack is detected.,The server returns a different response code when an attack string is used.,It closed the connection for a normal request.,The response was different when the request wasn\t made from a browser.]try:# Testing for no user-agent response. Detects almost all WAFs out there.resp1 self.performCheck(self.normalRequest)if User-Agent in self.headers:self.headers.pop(User-Agent) # Deleting the user-agent key from object not dict.resp3 self.customRequest(headersself.headers)if resp3 is not None and resp1 is not None:if resp1.status_code ! resp3.status_code:self.log.info(Server returned a different response when request didn\t contain the User-Agent header.)reason reasons[4]reason \r\nreason Normal response code is %s, % resp1.status_codereason while the response code to a modified request is %s % resp3.status_codeself.knowledge[generic][reason] reasonself.knowledge[generic][found] Truereturn True# Testing the status code upon sending a xss attackresp2 self.performCheck(self.xssAttack)if resp1.status_code ! resp2.status_code:self.log.info(Server returned a different response when a XSS attack vector was tried.)reason reasons[2]reason \r\nreason Normal response code is %s, % resp1.status_codereason while the response code to cross-site scripting attack is %s % resp2.status_codeself.knowledge[generic][reason] reasonself.knowledge[generic][found] Truereturn True# Testing the status code upon sending a lfi attackresp2 self.performCheck(self.lfiAttack)if resp1.status_code ! resp2.status_code:self.log.info(Server returned a different response when a directory traversal was attempted.)reason reasons[2]reason \r\nreason Normal response code is %s, % resp1.status_codereason while the response code to a file inclusion attack is %s % resp2.status_codeself.knowledge[generic][reason] reasonself.knowledge[generic][found] Truereturn True# Testing the status code upon sending a sqli attackresp2 self.performCheck(self.sqliAttack)if resp1.status_code ! resp2.status_code:self.log.info(Server returned a different response when a SQLi was attempted.)reason reasons[2]reason \r\nreason Normal response code is %s, % resp1.status_codereason while the response code to a SQL injection attack is %s % resp2.status_codeself.knowledge[generic][reason] reasonself.knowledge[generic][found] Truereturn True# Checking for the Server header after sending malicious requestsnormalserver, attackresponse_server , response self.attackresif server in resp1.headers:normalserver resp1.headers.get(Server)if response is not None and server in response.headers:attackresponse_server response.headers.get(Server)if attackresponse_server ! normalserver:self.log.info(Server header changed, WAF possibly detected)self.log.debug(Attack response: %s % attackresponse_server)self.log.debug(Normal response: %s % normalserver)reason reasons[1]reason \r\nThe server header for a normal response is %s, % normalserverreason while the server header a response to an attack is %s, % attackresponse_serverself.knowledge[generic][reason] reasonself.knowledge[generic][found] Truereturn True# If at all request doesnt go, press Fexcept RequestBlocked:self.knowledge[generic][reason] reasons[0]self.knowledge[generic][found] Truereturn Truereturn Falsedef matchHeader(self, headermatch, attackFalse):if attack:r self.attackreselse:r self.rqif r is None:returnheader, match headermatchheaderval r.headers.get(header)if headerval:# set-cookie can have multiple headers, python gives it to us# concatinated with a commaif header Set-Cookie:headervals headerval.split(, )else:headervals [headerval]for headerval in headervals:if re.search(match, headerval, re.I):return Truereturn Falsedef matchStatus(self, statuscode, attackTrue):if attack:r self.attackreselse:r self.rqif r is None:returnif r.status_code statuscode:return Truereturn Falsedef matchCookie(self, match, attackFalse):return self.matchHeader((Set-Cookie, match), attackattack)def matchReason(self, reasoncode, attackTrue):if attack:r self.attackreselse:r self.rqif r is None:return# We may need to match multiline context in response bodyif str(r.reason) reasoncode:return Truereturn Falsedef matchContent(self, regex, attackTrue):if attack:r self.attackreselse:r self.rqif r is None:return# We may need to match multiline context in response bodyif re.search(regex, r.text, re.I):return Truereturn Falsewafdetections dict()plugin_dict load_plugins()result_dict {}for plugin_module in plugin_dict.values():wafdetections[plugin_module.NAME] plugin_module.is_waf# Check for prioritized ones first, then check those added externallychecklist wafdetectionspriochecklist list(set(wafdetections.keys()) - set(checklist))def identwaf(self, findallFalse):detected list()try:self.attackres self.performCheck(self.centralAttack)except RequestBlocked:return detectedfor wafvendor in self.checklist:self.log.info(Checking for %s % wafvendor)if self.wafdetections[wafvendor](self):detected.append(wafvendor)if not findall:breakself.knowledge[wafname] detectedreturn detected我们在后续分析中再来一点一点拆解这个类直接分析没啥大用 主流程 def main():parser OptionParser(usage%prog url1 [url2 [url3 ... ]]\r\nexample: %prog http://www.victim.org/)parser.add_option(-v, --verbose, actioncount, destverbose, default0,helpEnable verbosity, multiple -v options increase verbosity)parser.add_option(-a, --findall, actionstore_true, destfindall, defaultFalse,helpFind all WAFs which match the signatures, do not stop testing on the first one)parser.add_option(-r, --noredirect, actionstore_false, destfollowredirect,defaultTrue, helpDo not follow redirections given by 3xx responses)parser.add_option(-t, --test, desttest, helpTest for one specific WAF)parser.add_option(-o, --output, destoutput, helpWrite output to csv, json or text file depending on file extension. For stdout, specify - as filename.,defaultNone)parser.add_option(-f, --format, destformat, helpForce output format to csv, json or text.,defaultNone)parser.add_option(-i, --input-file, destinput, helpRead targets from a file. Input format can be csv, json or text. For csv and json, a url column name or element is required.,defaultNone)parser.add_option(-l, --list, destlist, actionstore_true,defaultFalse, helpList all WAFs that WAFW00F is able to detect)parser.add_option(-p, --proxy, destproxy, defaultNone,helpUse an HTTP proxy to perform requests, examples: http://hostname:8080, socks5://hostname:1080, http://user:passhostname:8080)parser.add_option(--version, -V, destversion, actionstore_true,defaultFalse, helpPrint out the current version of WafW00f and exit.)parser.add_option(--headers, -H, destheaders, actionstore, defaultNone,helpPass custom headers via a text file to overwrite the default header set.)options, args parser.parse_args()logging.basicConfig(levelcalclogginglevel(options.verbose))log logging.getLogger(wafw00f)if options.output -:disableStdOut()print(randomArt())if options.list:print([] Can test for these WAFs:\r\n)try:m [i.replace(), ).split( () for i in wafdetectionsprio]print(R WAF Name *24Manufacturer\n -*8 *24-*12\n)max_len max(len(str(x)) for k in m for x in k)for inner in m:first Truefor elem in inner:if first:text Y {:{}} .format(elem, max_len2)first Falseelse:text W{:{}} .format(elem, max_len2)print(text, E, end)print()sys.exit(0)except Exception:returnif options.version:print([] The version of WAFW00F you have is %sv%s%s % (B, __version__, E))print([] WAFW00F is provided under the %s%s%s license. % (C, __license__, E))returnextraheaders {}if options.headers:log.info(Getting extra headers from %s % options.headers)extraheaders getheaders(options.headers)if extraheaders is None:parser.error(Please provide a headers file with colon delimited header names and values)# arg1# if len(args) 0 and not options.input:# parser.error(No test target specified.)#check if input file is presentif options.input:log.debug(Loading file %s % options.input)try:if options.input.endswith(.json):with open(options.input) as f:try:urls json.loads(f.read())except json.decoder.JSONDecodeError:log.critical(JSON file %s did not contain well-formed JSON, options.input)sys.exit(1)log.info(Found: %s urls to check. %(len(urls)))targets [ item[url] for item in urls ]elif options.input.endswith(.csv):columns defaultdict(list)with open(options.input) as f:reader csv.DictReader(f)for row in reader:for (k,v) in row.items():columns[k].append(v)targets columns[url]else:with open(options.input) as f:targets [x for x in f.read().splitlines()]except FileNotFoundError:log.error(File %s could not be read. No targets loaded., options.input)sys.exit(1)else:targets argsresults []# arg2targets [117.39.30.114]for target in targets:if not target.startswith(http):log.info(The url %s should start with http:// or https:// .. fixing (might make this unusable) % target)target https:// targetprint([*] Checking %s % target)pret urlParser(target)if pret is None:log.critical(The url %s is not well formed % target)sys.exit(1)(hostname, _, path, _, _) pretlog.info(starting wafw00f on %s % target)proxies dict()if options.proxy:proxies {http: options.proxy,https: options.proxy,}# arg3attacker WAFW00F(target, debugleveloptions.verbose, pathpath,followredirectoptions.followredirect, extraheadersextraheaders,proxiesproxies)if attacker.rq is None:log.error(Site %s appears to be down % hostname)continue# arg4# 测试指定的wafif options.test:if options.test in attacker.wafdetections:waf attacker.wafdetections[options.test](attacker)if waf:print([] The site %s%s%s is behind %s%s%s WAF. % (B, target, E, C, options.test, E))else:print([-] WAF %s was not detected on %s % (options.test, target))else:print([-] WAF %s was not found in our list\r\nUse the --list option to see what is available % options.test)returnwaf attacker.identwaf(options.findall)log.info(Identified WAF: %s % waf)if len(waf) 0:for i in waf:results.append(buildResultRecord(target, i))print([] The site %s%s%s is behind %s%s%s WAF. % (B, target, E, C, (E and/or C).join(waf), E))if (options.findall) or len(waf) 0:print([] Generic Detection results:)if attacker.genericdetect():log.info(Generic Detection: %s % attacker.knowledge[generic][reason])print([*] The site %s seems to be behind a WAF or some sort of security solution % target)print([~] Reason: %s % attacker.knowledge[generic][reason])results.append(buildResultRecord(target, generic))else:print([-] No WAF detected by the generic detection)results.append(buildResultRecord(target, None))print([~] Number of requests: %s % attacker.requestnumber)#print table of resultsif len(results) 0:log.info(Found: %s matches. % (len(results)))if options.output:if options.output -:enableStdOut()if options.format json:json.dump(results, sys.stdout, indent2)elif options.format csv:csvwriter csv.writer(sys.stdout, delimiter,, quotechar,quotingcsv.QUOTE_MINIMAL)count 0for result in results:if count 0:header result.keys()csvwriter.writerow(header)count 1csvwriter.writerow(result.values())else:print(os.linesep.join(getTextResults(results)))elif options.output.endswith(.json):log.debug(Exporting data in json format to file: %s % (options.output))with open(options.output, w) as outfile:json.dump(results, outfile, indent2)elif options.output.endswith(.csv):log.debug(Exporting data in csv format to file: %s % (options.output))with open(options.output, w) as outfile:csvwriter csv.writer(outfile, delimiter,, quotechar,quotingcsv.QUOTE_MINIMAL)count 0for result in results:if count 0:header result.keys()csvwriter.writerow(header)count 1csvwriter.writerow(result.values())else:log.debug(Exporting data in text format to file: %s % (options.output))if options.format json:with open(options.output, w) as outfile:json.dump(results, outfile, indent2)elif options.format csv:with open(options.output, w) as outfile:csvwriter csv.writer(outfile, delimiter,, quotechar,quotingcsv.QUOTE_MINIMAL)count 0for result in results:if count 0:header result.keys()csvwriter.writerow(header)count 1csvwriter.writerow(result.values())else:with open(options.output, w) as outfile:outfile.write(os.linesep.join(getTextResults(results)))if __name__ __main__:if sys.hexversion 0x2060000:sys.stderr.write(Your version of python is way too old... please update to 2.6 or later\r\n)main()wafw00f上来就开大一个main()函数集成了一大堆代码这个确实不太好个人认为应该还是把一些部分像IdentYwaf那样封装成一个参数这样处理起来就不会出现大改而是把改代码局限在小区域内 读入参数部分 parser OptionParser(usage%prog url1 [url2 [url3 ... ]]\r\nexample: %prog http://www.victim.org/)parser.add_option(-v, --verbose, actioncount, destverbose, default0,helpEnable verbosity, multiple -v options increase verbosity)parser.add_option(-a, --findall, actionstore_true, destfindall, defaultFalse,helpFind all WAFs which match the signatures, do not stop testing on the first one)parser.add_option(-r, --noredirect, actionstore_false, destfollowredirect,defaultTrue, helpDo not follow redirections given by 3xx responses)parser.add_option(-t, --test, desttest, helpTest for one specific WAF)parser.add_option(-o, --output, destoutput, helpWrite output to csv, json or text file depending on file extension. For stdout, specify - as filename.,defaultNone)parser.add_option(-f, --format, destformat, helpForce output format to csv, json or text.,defaultNone)parser.add_option(-i, --input-file, destinput, helpRead targets from a file. Input format can be csv, json or text. For csv and json, a url column name or element is required.,defaultNone)parser.add_option(-l, --list, destlist, actionstore_true,defaultFalse, helpList all WAFs that WAFW00F is able to detect)parser.add_option(-p, --proxy, destproxy, defaultNone,helpUse an HTTP proxy to perform requests, examples: http://hostname:8080, socks5://hostname:1080, http://user:passhostname:8080)parser.add_option(--version, -V, destversion, actionstore_true,defaultFalse, helpPrint out the current version of WafW00f and exit.)parser.add_option(--headers, -H, destheaders, actionstore, defaultNone,helpPass custom headers via a text file to overwrite the default header set.)options, args parser.parse_args()logging.basicConfig(levelcalclogginglevel(options.verbose))log logging.getLogger(wafw00f)这样看有点太难看了直接看-h吧 proxy、output、headers、findall是比较有可能用到的参数但是我都不用我只解析源码哈哈 核心流程 咱们还是跳过大部分的无用代码部分吧确实没啥好分析的直接关注一下下面这部分代码 if options.input:log.debug(Loading file %s % options.input)try:if options.input.endswith(.json):with open(options.input) as f:try:urls json.loads(f.read())except json.decoder.JSONDecodeError:log.critical(JSON file %s did not contain well-formed JSON, options.input)sys.exit(1)log.info(Found: %s urls to check. %(len(urls)))targets [ item[url] for item in urls ]elif options.input.endswith(.csv):columns defaultdict(list)with open(options.input) as f:reader csv.DictReader(f)for row in reader:for (k,v) in row.items():columns[k].append(v)targets columns[url]else:with open(options.input) as f:targets [x for x in f.read().splitlines()]except FileNotFoundError:log.error(File %s could not be read. No targets loaded., options.input)sys.exit(1)else:targets argsresults []for target in targets:if not target.startswith(http):log.info(The url %s should start with http:// or https:// .. fixing (might make this unusable) % target)target https:// targetprint([*] Checking %s % target)pret urlParser(target)if pret is None:log.critical(The url %s is not well formed % target)sys.exit(1)(hostname, _, path, _, _) pretlog.info(starting wafw00f on %s % target)proxies dict()if options.proxy:proxies {http: options.proxy,https: options.proxy,}attacker WAFW00F(target, debugleveloptions.verbose, pathpath,followredirectoptions.followredirect, extraheadersextraheaders,proxiesproxies)if attacker.rq is None:log.error(Site %s appears to be down % hostname)continueif options.test:if options.test in attacker.wafdetections:waf attacker.wafdetections[options.test](attacker)if waf:print([] The site %s%s%s is behind %s%s%s WAF. % (B, target, E, C, options.test, E))else:print([-] WAF %s was not detected on %s % (options.test, target))else:print([-] WAF %s was not found in our list\r\nUse the --list option to see what is available % options.test)returnwaf attacker.identwaf(options.findall)log.info(Identified WAF: %s % waf)if len(waf) 0:for i in waf:results.append(buildResultRecord(target, i))print([] The site %s%s%s is behind %s%s%s WAF. % (B, target, E, C, (E and/or C).join(waf), E))if (options.findall) or len(waf) 0:print([] Generic Detection results:)if attacker.genericdetect():log.info(Generic Detection: %s % attacker.knowledge[generic][reason])print([*] The site %s seems to be behind a WAF or some sort of security solution % target)print([~] Reason: %s % attacker.knowledge[generic][reason])results.append(buildResultRecord(target, generic))else:print([-] No WAF detected by the generic detection)results.append(buildResultRecord(target, None))print([~] Number of requests: %s % attacker.requestnumber)哇塞还是好长来跟着小弟我继续分解这段代码 if options.input:log.debug(Loading file %s % options.input)try:if options.input.endswith(.json):with open(options.input) as f:try:urls json.loads(f.read())except json.decoder.JSONDecodeError:log.critical(JSON file %s did not contain well-formed JSON, options.input)sys.exit(1)log.info(Found: %s urls to check. %(len(urls)))targets [ item[url] for item in urls ]elif options.input.endswith(.csv):columns defaultdict(list)with open(options.input) as f:reader csv.DictReader(f)for row in reader:for (k,v) in row.items():columns[k].append(v)targets columns[url]else:with open(options.input) as f:targets [x for x in f.read().splitlines()]except FileNotFoundError:log.error(File %s could not be read. No targets loaded., options.input)sys.exit(1)else:targets args这部分就是说我们要检测的网站url读入如果指定了一个多个网站检测的文件它就从文件中读取url到targets中否则直接从控制台的args里存储到targets中 for target in targets:if not target.startswith(http):log.info(The url %s should start with http:// or https:// .. fixing (might make this unusable) % target)target https:// targetprint([*] Checking %s % target)pret urlParser(target)if pret is None:log.critical(The url %s is not well formed % target)sys.exit(1)(hostname, _, path, _, _) pretlog.info(starting wafw00f on %s % target)proxies dict()if options.proxy:proxies {http: options.proxy,https: options.proxy,}attacker WAFW00F(target, debugleveloptions.verbose, pathpath,followredirectoptions.followredirect, extraheadersextraheaders,proxiesproxies)if attacker.rq is None:log.error(Site %s appears to be down % hostname)continueif options.test:if options.test in attacker.wafdetections:waf attacker.wafdetections[options.test](attacker)if waf:print([] The site %s%s%s is behind %s%s%s WAF. % (B, target, E, C, options.test, E))else:print([-] WAF %s was not detected on %s % (options.test, target))else:print([-] WAF %s was not found in our list\r\nUse the --list option to see what is available % options.test)return这个循环首先是从targets从获取每一个target 然后看看target是否有http开头没有就默认给一个https://这里其实是有问题的IdentYwaf也是默认给https://但是如果https://有问题会试一下http://而wafw00f则没有这个处理所以如果有网站的ssl证书过期了且我们给的target只有hostname而没有指定协议的话wafw00f就没法检测了这里可以处理一下兼容http://的情况 接着就用了urlParser函数来解析target使用pret存储返回的内容 hostname netloc.split(“:”)[0] prot netloc.split(“:”)[1] path path query query ssl True if scheme “https” else False pret如果不存在说明url不合法直接退出即可 存在则从pret中读取hostname和path信息 pret urlParser(target)if pret is None:log.critical(The url %s is not well formed % target)sys.exit(1)(hostname, _, path, _, _) pret配置代理 proxies dict()if options.proxy:proxies {http: options.proxy,https: options.proxy,}重点来了WAFW00F类是继承waftoolsengine类开发的一个类 attacker WAFW00F(target, debugleveloptions.verbose, pathpath,followredirectoptions.followredirect, extraheadersextraheaders,proxiesproxies)我们我们看看WAFW00F类的初始化看看这个实例化对象有什么稀奇的 class WAFW00F(waftoolsengine):def __init__(self, targetwww.example.com, debuglevel0, path/,followredirectTrue, extraheaders{}, proxiesNone):self.log logging.getLogger(wafw00f)self.attackres Nonewaftoolsengine.__init__(self, target, debuglevel, path, proxies, followredirect, extraheaders)self.knowledge dict(genericdict(foundFalse, reason), wafnamelist())self.rq self.normalRequest()首先就是target指定测试的对象 然后重点关注一下path、followredirect 这个path就是我们解析url整出来的path followredirect应该就是允许处理重定向包的情况这一个选择使得wafw00f在处理response上就比IdentYwaf要强上不少。 接着往下看waftoolsengine此时也被初始化了一次这个是直接对整个waftoolsengine初始化也就是说在这个target处理过程关于引擎的使用都受这次初始化影响直到下次初始化遍历到下一个target self.knowledge dict(genericdict(foundFalse, reason‘’), wafnamelist()) 是为WAFW00F类的实例化对象attacker整了一个知识属性这个属性是一个类似json的格式存储的第一个键是generic第二个键是wafnamegeneric的值是一个字典有两个键一个是found一个是reason这个知识属性具体什么用我们后续再来看主要是应用在输出上面的与我们测试识别关系不大不太重要 重点来了 接下来的内容重点挺多的大家这里最好自己去调试跑一跑看看什么个情况。 self.rq self.normalRequest() 实例化对象接下来就会直接调用这个rqattacker.rq就是attacker.normalRequest() 我们来看看normalRequest def normalRequest(self):return self.Request()接着往下找这个Request就是waftoolsengine的那个Request我们什么都不传直接按照默认的来 def Request(self, headersNone, pathNone, params{}, delay0, timeout7):try:time.sleep(delay)if not headers:h self.headerselse: h headersreq requests.get(self.target, proxiesself.proxies, headersh, timeouttimeout,allow_redirectsself.allowredir, paramsparams, verifyFalse)self.log.info(Request Succeeded)self.log.debug(Headers: %s\n % req.headers)self.log.debug(Content: %s\n % req.content)self.requestnumber 1return reqexcept requests.exceptions.RequestException as e:self.log.error(Something went wrong %s % (e.__str__()))这里重点看这一段 req requests.get(self.target, proxiesself.proxies, headersh, timeouttimeout,allow_redirectsself.allowredir, paramsparams, verifyFalse)因为我们已经初始化过一次全局的引擎了这里request的参数引擎都是知道的所以我们是不需要传参的。 显然wafw00f很高明它把请求和处理分开了所以我们后面再看看它的处理是如何实现的比IdentYwaf要好上不少 回到我们的主流程中接下来我们就要调用rq了 if attacker.rq is None:log.error(Site %s appears to be down % hostname)continue现在我们可以看出来rq是做什么的了就是测试网站是否还存活的IdentYwaf看是否存活是看返回包的情况而wafw00f是看是否有返回包我个人认为wafw00f的方法更加好因为很多网站是需要加上特定的path才能正常访问的wafw00f这样可以避免访问结果为400等状况外的情况而不处理了的情况。如果网站不存活了那么是什么包都不会返回的对404都没有哈哈 而且wafw00f使用的是request可以处理重定向的网站而IdentYwaf用的是urllib3处理不了重定向不能处理重定向就不能处理返回包状态为300的情况所以IdentYwaf被wafw00f簿杀不是没有原因的。 如果网站还存活那么我就可以去看看它是否有WAF了 当然接下来还有一段是测试指定waf 这段我调试时没怎么管 waf attacker.wafdetections[options.test](attacker)这段代码里关键就是这个玩意那么关键就是attacker这个WAFW00F实例化对象里的wafdetections是什么咯这里还是先不分析了后面我们讲到identwaf的时候会详细分析这个东西的。 所以这一长段循环总结下来就是测试target网站还是否存活 接下来我们将来到整个main中最最最最最重要的一段代码 waf attacker.identwaf(options.findall)log.info(Identified WAF: %s % waf)这段也是大部分现在网上那些wafw00f什么分析的重点部分当然我肯定不会像网上那样讲的了那么跟着我接着看看attacker要干什么吧 核心是WAFW00F类重点identwaf方法 WAFW00F.identwaf() 前置有些内容我先提一下再来分析这个函数 首先是WAFW00F的使用的攻击payload xsstring scriptalert(XSS);/scriptsqlistring UNION SELECT ALL FROM information_schema AND or SLEEP(5) or lfistring ../../../../etc/passwdrcestring /bin/cat /etc/passwd; ping 127.0.0.1; curl google.comxxestring !ENTITY xxe SYSTEM file:///etc/shadow]pwnhack;/pwn其次是WAFW00F的攻击手段 def nonExistent(self):return self.Request(pathself.path str(random.randrange(100, 999)) .html)def xssAttack(self):return self.Request(pathself.path, params {s: self.xsstring})def xxeAttack(self):return self.Request(pathself.path, params {s: self.xxestring})def lfiAttack(self):return self.Request(pathself.path self.lfistring)def centralAttack(self):return self.Request(pathself.path, params{a: self.xsstring, b: self.sqlistring, c: self.lfistring})def sqliAttack(self):return self.Request(pathself.path, params {s: self.sqlistring})def oscAttack(self):return self.Request(pathself.path, params {s: self.rcestring})接下来开始详细讲解identwaf是怎么实现的 def identwaf(self, findallFalse):detected list()try:self.attackres self.performCheck(self.centralAttack)except RequestBlocked:return detectedfor wafvendor in self.checklist:self.log.info(Checking for %s % wafvendor)if self.wafdetections[wafvendor](self):detected.append(wafvendor)if not findall:breakself.knowledge[wafname] detectedreturn detected首先identwaf初始化了一个detected为空列表用于存储该网站探查到的WAF保护情况 尝试寻找所有的WAF(如果findall为True) def performCheck(self, request_method):r request_method()if r is None:raise RequestBlocked()return r首先尝试centralAttack def centralAttack(self):return self.Request(pathself.path, params{a: self.xsstring, b: self.sqlistring, c: self.lfistring})如果有返回值return这个返回值如果没有报错 不报错情况 self.attackres就等于这个返回值 一些必要参数 我们接着关注一下另外一些场外因素类内全局变量 wafdetections dict()plugin_dict load_plugins()result_dict {}for plugin_module in plugin_dict.values():wafdetections[plugin_module.NAME] plugin_module.is_waf# Check for prioritized ones first, then check those added externallychecklist wafdetectionspriochecklist list(set(wafdetections.keys()) - set(checklist))load_plugins() def load_plugins():here os.path.abspath(os.path.dirname(__file__))get_path partial(os.path.join, here)plugin_dir get_path(plugins)plugin_base PluginBase(packagewafw00f.plugins, searchpath[plugin_dir])plugin_source plugin_base.make_plugin_source(searchpath[plugin_dir], persistTrue)plugin_dict {}for plugin_name in plugin_source.list_plugins():plugin_dict[plugin_name] plugin_source.load_plugin(plugin_name)return plugin_dicthere相当于wafw00f库的绝对路径 get_path 实际上是 os.path.join(here, *args)即here必是os.path.join的一个参数至于*args则是后续新添加的内容可以学习这里wafw00f的partial的应用 下面plugin_dir 就是指插件的地址 这段代码的主要目的是动态加载指定目录这里是 ‘plugins’ 目录下的插件并将这些插件存储在一个字典中返回。它使用了 pluginbase 库来实现插件的动态加载。下面是对代码各部分的详细解释 导入需要的包pluginbase.PluginBase、os、functiontools.partial确定插件目录 ○ here os.path.abspath(os.path.dirname(file))获取当前脚本的绝对路径。 ○ get_path partial(os.path.join, here)使用 functools.partial 创建一个新的函数 get_path这个函数将 here 作为第一个参数传递给 os.path.join允许你轻松地添加相对于当前脚本的路径。 ○ plugin_dir get_path(‘plugins’)使用 get_path 函数获取 ‘plugins’ 目录的绝对路径。创建插件基础 ○ plugin_base PluginBase(package‘wafw00f.plugins’, searchpath[plugin_dir])创建一个 PluginBase 实例。package 参数指定了插件包的名称这里可能是用于插件导入的命名空间searchpath 指定了插件的搜索路径即前面获取的 plugin_dir。创建插件源 ○ plugin_source plugin_base.make_plugin_source(searchpath[plugin_dir], persistTrue)通过 plugin_base 实例创建一个插件源。searchpath 同样指定了插件的搜索路径persist 参数设置为 True 表示插件源将尝试持久化已加载的插件信息以优化后续加载。加载插件 ○ 初始化一个空字典 plugin_dict 用于存储加载的插件。 ○ 使用 for 循环遍历 plugin_source.list_plugins() 返回的插件名称列表。 ○ 对于每个插件名称使用 plugin_source.load_plugin(plugin_name) 加载插件并将其存储在 plugin_dict 中键为插件名称值为插件对象。返回插件字典最后函数返回包含所有加载插件的字典。 总结来说这段代码通过 pluginbase 库动态加载了一个指定目录‘plugins’下的所有插件并将它们以名称到对象的映射形式存储在一个字典中返回。这种机制允许应用程序在不重启的情况下动态扩展功能只需添加新的插件到 ‘plugins’ 目录即可。 wafdetections for plugin_module in plugin_dict.values():wafdetections[plugin_module.NAME] plugin_module.is_waf因为我们返回的plugin_dict是通过pluginbase和PluginBase.make_plugin_source来生成的里面key值为每个plugin的文件名而value是一共module类module.NAME是每个plugin对WAF的命名module.is_waf指的就是每个waf插件的识别函数所以这里module相当于一个plugin文件 NAME Armor Defense (Armor)def is_waf(self):schemes [self.matchContent(rblocked by website protection from armor),self.matchContent(rplease create an armor support ticket)]if any(i for i in schemes):return Truereturn Falsewafdetectionsprio 就是一个列表里面包含了各个waf的命名 checklist checklist很简单就是把所有需要检查的waf整成列表因为plugin是可扩展的所以这里有可扩展兼容 checklist list(set(wafdetections.keys()) - set(checklist)) 后续检测 for wafvendor in self.checklist:self.log.info(Checking for %s % wafvendor)if self.wafdetections[wafvendor](self):detected.append(wafvendor)if not findall:breakchecklist前面已说就是waf检测列表有插件的waf才能被检测 wafdetections也已说是waf检测插件集合举例 def is_waf(self):schemes [self.matchContent(rblocked by website protection from armor),self.matchContent(rplease create an armor support ticket)]if any(i for i in schemes):return Truereturn False所以其实就是调用了WAFW00F自身的matchContent等match方法来检测。 matchContent def matchContent(self, regex, attackTrue):if attack:r self.attackreselse:r self.rqif r is None:return# We may need to match multiline context in response bodyif re.search(regex, r.text, re.I):return Truereturn FalsematchHeader def matchHeader(self, headermatch, attackFalse):if attack:r self.attackreselse:r self.rqif r is None:returnheader, match headermatchheaderval r.headers.get(header)if headerval:# set-cookie can have multiple headers, python gives it to us# concatinated with a commaif header Set-Cookie:headervals headerval.split(, )else:headervals [headerval]for headerval in headervals:if re.search(match, headerval, re.I):return Truereturn False显然wafw00f在检测上面更加灵活对于有些waf它可能现在变成了5s盾那种类型identYwaf直接向网站url发包检测盲猜很容易被5s盾拦截但是identYwaf却不会处理5s盾因为5s盾返回的不是HTML而是js等其它的包而wafw00f则通过matchHeaders方法巧妙的避开了identYwaf僵硬匹配HTML的弊端 identwaf函数总结 有两种模式一般默认是指匹配一个waf可以通过设置findall来选择是否尝试匹配全部的waf这里self.attackres不清楚是做什么用的总之它会测试centralAttack如果centralAttack测试失败了就会抛出一个错误成功了则返回get到的值基于centralAttack测试成功的情况对每个waf进行测试如果findall是False那么找到一个就退出否则全部找一遍用detected暂存所有找到的waf找完后再存到类实例的knowledge中 回到主流程中后面我就不再分析了剩下都是wafw00f的一些输出。 总结 首先wafw00f中心攻击识别效果奇好无比而且很神奇可以处理5s盾这种情况因为它为每种waf设置了一种检测插件检测插件是个性化的对应waf的插件可以灵活处理不同waf的差异使得识别效果远远好于IdentYwaf 例如cloudflare #!/usr/bin/env pythonCopyright (C) 2022, WAFW00F Developers. See the LICENSE file for copying permission. NAME Cloudflare (Cloudflare Inc.)def is_waf(self):schemes [self.matchHeader((server, cloudflare)),self.matchHeader((server, rcloudflare[-_]nginx)),self.matchHeader((cf-ray, r.?)),self.matchCookie(__cfduid)]if any(i for i in schemes):return Truereturn False基本所有的cloudflare都是5s盾了现在世界出名IdentYwaf还是基于http请求的正则匹配来实现cloudflare识别显然是不可行的。而wafw00f专门匹配cloudflare的cookiecloudflare会设置指定的cookie名__cfduid专门匹配返回包的headers等。 其次是wafw00f使用到了request来实现发包抓包这显然比使用urllib3库的IdentYwaf要强上不少至少可以处理重定向这种情况且wafw00f如果想要实现cookie维护的话也可以使用request的session类来实现。总之到了今天使用对urllib3封装的request显然比直接使用urllib3要好得多方便得多。 其实我在语雀上的笔记还做了很多内容一篇文章好像塞不下这么多内容了所以我就不管了反正主体该说的都说了剩下的大家自己理一理也差不多了。
http://www.hkea.cn/news/14306246/

相关文章:

  • 重庆网站推广运营公司精灵网站建设
  • 做网站 什么语言深圳龙华是低风险区吗
  • 大学网站群建设方案网站建设实战教程
  • 什么做书籍的网站教做面包的网站
  • 男女做的羞羞事的网站深圳设计招聘网
  • xsl做书店网站公司介绍模板范文
  • 大淘客做的网站可以吗厦门电信网站备案
  • 后台网站开发文档电商营销策略
  • 大良营销网站建设资讯网站建立的意义
  • 江苏省建设局网站首页wordpress 子菜单顺序
  • 粉丝帮女流做的网站镇江网页设计公司
  • 做网站的怎么学网站建设效果
  • 网站建设兆金手指花总购物网站开发会遇到的的问题
  • 如何做一个移动网站个人主页网页设计模板图片
  • 怀化组织部网站网页制作方案策划
  • 外汇直播网站建设开发怎样做网站结构优化
  • 宝安中心客运站中国最新军事新闻最新消息2023
  • 福州营销型网站建设公司wordpress 主机迁移
  • 怎么做网站美工企业展厅设计图
  • 苏州晶体公司网站建设建设网站租用空间
  • 自己怎么做网站卖东西网页制作怎么添加视频
  • 淄博网站开发网泰好企业营销策划方案范文
  • 免费商用图片的网站wordpress建立扁平化
  • 合肥网站定制检测网站是否正常
  • 网站建设内容的重点做网站构架
  • asp网站怎么验证到百度站长网页版抖音入口
  • 桂林北站是哪个区如何关闭网站 备案
  • 中国智慧团建网站传媒大气的网站
  • 南宁市网站开发建设建筑网站免费
  • 中国网站建设公司 排名最旺的公司名称大全