reflected_xss檢測工具的介紹
1.反射型xss的介紹
xss代碼出現(xiàn)在URL中,瀏覽器訪問這個url后,服務(wù)端響應(yīng)的內(nèi)容中包含有這段xss代碼,這樣的xss被稱作反射型xss.
2.工具的介紹
reflected_xss工具目前可以用來檢測網(wǎng)站中是否可能存在反射型xss,程序是用python寫的.
我用的python版本是2.7.3,系統(tǒng)上需要有python的requests模塊.
工具有四個文件
spider.py用來爬取網(wǎng)頁中的符合規(guī)則的url,可以指定爬取深度,然后生成一個urllist文件
reflect_xss.py檢測urllist文件中的每一個url是否可能存在反射型xss漏洞
filter.py中的函數(shù)會被spider.py調(diào)用,過濾掉一些不帶參數(shù)的url和重復(fù)的url.
config文件中,目前只有兩個選項:timeout和sleep.
timeout是請求最多等待響應(yīng)的時間.發(fā)送響應(yīng)時,超過timout秒后就當作請求超時了.
sleep是每次請求url的間隔時間,因為有的網(wǎng)站會限制ip訪問速度.
3.工具工作的流程
spider.py接受一個url參數(shù),根據(jù)這個url發(fā)送請求,接收到響應(yīng)后,把響應(yīng)中符合規(guī)則的url提取出來,保存到urllist文件中.如果有指定爬取深度的話,會遞歸的爬取規(guī)則的url.這個規(guī)則是用正則寫的,目前的規(guī)則是爬取到的url都應(yīng)該是一個網(wǎng)站下的,并且url必須是帶參數(shù)了的.
比如,spider.py “http://127.0.0.1/test.php?x=1″ 能夠保存的url都是以http://127.0.0.1開頭的,并且url中有一個?號.
reflect_xss.py會檢查urllist文件中的每一個url是否存在反射型xss.檢測流程是:分析每一個url的參數(shù),將”參數(shù)”挨個替換成”參數(shù)+xss payload”,然后發(fā)送出去,分析響應(yīng)的內(nèi)容中是否有xss payload字符串.其中的xss payload可以自定義,keywords文件中的每一行都會被當作一個xss payload.可以向keywords文件添加或刪除xss payload.
4.工具用法演示
先來大致看下當前目錄下的文件吧
再來看看我用來在本機測試的兩個文件吧.
只有http://127.0.0.1/rxss.php?a=1&x=2這個url是符合規(guī)則的,所以一會兒爬蟲爬完了,urllist中應(yīng)該只有這一條url.
spider.py就是我們的小爬蟲.
直接運行spider.py可以看到用法.
爬蟲爬完了,生成了一個urllist文件,文件中只有一條url,一會兒就來檢測這條url是否可能存在反射型xss缺陷
這個url參數(shù)最好還是要帶上雙引號,就跟用sqlmap的時候一樣.
reflect_xss.py就是用來檢測的.
直接python reflect_xss.py 運行程序就可以檢測了.
如果有發(fā)現(xiàn)可能存在反射型xss,會在當前目錄下生成一個Found文件,文件記錄了有缺陷的url.
檢測的時候,如果發(fā)生了異常,會被記錄到當前目錄下的error.log日志文件.這里沒什么異常,所以沒有記錄.這個日志文件記錄的比較簡潔明了,我就不多做介紹了.
我的郵箱是:happy7513159@qq.com 歡迎交流
下載地址:http://vdisk.weibo.com/lc/3GawHF1w1aTxKu2BJjr 密碼:CMB6
核心代碼:
spider.py:
- #!/usr/bin/env python
- #coding=utf-8
- import requests
- import re
- import string
- import sys
- import filter
- headers={'Connection':'keep-alive',"User-Agent":"Mozilla/5.0 (X11; Linux i686) AppleWebkit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36","Origin":"http://www.oschina.net",'Accept-Encoding':'gzip,deflate,sdch','Accept-Language':'zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4','X-Requested-With':'XMLHttpRequest','Accept':'application/json,text/javascript,*/*;q=0.01'}
- def saveurl(urlset):
- try:
- f=open("urllist","a")
- for url in urlset:
- #unicode字符碼寫入文件,添加換行
- f.write(url.encode('UTF-8')+"n")
- #except:
- # print "Failed to write urllist to file!"
- finally:
- f.close()
- def main(requestsurl,depth):
- try:
- #print "%d"%depth
- depth=depth+1
- urlset=parseContent(requests.get(requestsurl,timeout=2,headers=headers).text)
- saveurl(urlset)
- if depth==string.atoi(sys.argv[2]):
- pass
- else:
- for u in urlset:
- main(u,depth)
- except:
- pass
- def parseContent(content):
- strlist = re.split('"',content)
- urlset = set([])
- for strstr in strlist:
- #python正則匹配時,需要\\表示
- #if re.match('http://.*com(/|w)+', str):
- #這個正則有點簡單,只匹配了當前網(wǎng)站
- #if re.match('http://'+domain, str):
- rules="http://"+domain+"[^,^ ^ ^']*"
- #strstr是unicode對象
- result=re.compile(rules).findall(strstr.encode("utf-8"))
- #result是一個數(shù)組
- if len(result)==0:
- pass
- else:
- for i in result:
- urlset.add(i)
- return list(urlset)
- if __name__=="__main__":
- if len(sys.argv)!=3:
- print "usage:"+sys.argv[0]+" http://test.com/"+" depth"
- print "example:"+sys.argv[0]+' "http://127.0.0.1/a.php?c=1"'+" 3"
- else:
- domain=sys.argv[1].split('/')[2]
- #保存最開始的url
- tmp=[]
- tmp.insert(0,sys.argv[1]);
- saveurl(tmp)
- #開始抓取
- main(sys.argv[1],0)
- filter.filter()
reflect_xss.py
- #!/usr/bin/env python
- #coding=utf-8
- import requests
- import sys
- import time
- import re
- #global payloads
- #payloads=['"><sCript>alert(1)</sCript>','<img src=@ onerror=x>']
- jindu=0
- def readconf(keyname):
- isFound=0
- try:
- f=open("config","r")
- lines=f.readlines()
- for line in lines:
- if line.startswith(keyname):
- isFound=1
- return line.split('=')[1]
- if isFound==0:
- errorlog("Warn:Can not to read key "+keyname+" from configure file")
- return False
- except:
- errorlog("Warn:can not to read configure file ")
- return False
- def errorlog(str):
- str=str+"n"
- t=time.strftime('%m-%d %H.%M.%S--->',time.localtime(time.time()))
- f=open("error.log","a")
- f.write(t+str)
- f.close()
- def findlog(url):
- try:
- f=open("Found","a")
- f.write(url+"n")
- except:
- errorlog("Fail:open 'Found' file")
- finally:
- f.close()
- def main(payload,canshu,checkurl):
- global jindu
- url=checkurl.replace(canshu,canshu+payload)
- #print "checking: "+url
- #TODO timeout,防止ip被屏蔽應(yīng)該有
- if readconf("sleep"):
- time.sleep(float(readconf("sleep")))
- #可能有Timeout異常
- try:
- a=requests.get(url,timeout=1)
- if a.text.find(payload)!=-1:
- #print "Find!!!!"
- #print url
- #print "-----------------------"
- findlog(url)
- else:
- if jindu%10==0:
- print time.strftime('%H:%M.%S->',time.localtime(time.time()))+"checking the "+str(jindu)+"th"+" url:"+url
- except:
- errorlog("Fail:request.get "+url)
- jindu=jindu+1
- def parse(url):
- #url=http://test.com/test.php?a=1&b=2&c=3
- #canshus=["a=1","c=3"]
- #有可能url是http://test.com這種,沒有參數(shù)
- #這種情況,返回一個空對象
- try:
- canshus=url.split("?")[1].split("&")
- return canshus
- except:
- kong=[]
- return kong
- pass
- def readfile(filename):
- #這個global的位置放在上面效果就不一樣,不懂為什么
- #global payloads
- try:
- aa=open(filename,"r")
- f=aa.readlines();
- for i in range(0,len(f)):
- #過濾掉n
- f[i]=f[i].rstrip("n")
- return f
- except:
- print "Failed to access "+'"'+filename+'" '"file!"
- finally:
- aa.close()
- if __name__=="__main__":
- if len(sys.argv)!=1:
- print 'usage:'+sys.argv[0]+" url depth"
- print 'example:'+sys.argv[0]+'"http://url/test.php?x=1&y=2" 3'
- else:
- #global payloads
- payloads=readfile("keywords.txt")
- urls=readfile("urllist")
- for checkurl in urls:
- for payload in payloads:
- for canshu in parse(checkurl):
- if len(canshu)!=0:
- main(payload,canshu,checkurl)