XSS扫描器编写思路
文章目錄
- XSS掃描器
- 設(shè)計(jì)思路
- url解析與參數(shù)合并
- 潛在注入點(diǎn)檢測(cè)
- 目的
- 實(shí)現(xiàn)思路
- 實(shí)現(xiàn)代碼
- html解析器
- 潛在注入點(diǎn)搜索
- 測(cè)試結(jié)果
- 針對(duì)性測(cè)試
- html回顯實(shí)現(xiàn)思路
- html回顯實(shí)現(xiàn)代碼
- 其他回顯的針對(duì)性測(cè)試流程
- 結(jié)果處理
- 待優(yōu)化部分
XSS掃描器
設(shè)計(jì)思路
數(shù)據(jù)來(lái)源 : 通過(guò)開放爬蟲獲得大量get請(qǐng)求url后,存入掃描器目錄下的target.txt。
url解析與參數(shù)合并
解析出url,host,param并與內(nèi)置字典進(jìn)行危險(xiǎn)參數(shù)名合并(借鑒xsstrike)
host = urlparse(target).netloc url = getUrl(target) params = getParams(target) params = add_extra_params(copy.deepcopy(params))TOP_RISK_GET_PARAMS = {"id", 'action', 'type', 'm', 'callback', 'cb'} def add_extra_params(params):risk_params = TOP_RISK_GET_PARAMSfor p in risk_params:params[p] = ''return params潛在注入點(diǎn)檢測(cè)
目的
潛在注入點(diǎn)檢測(cè)是判斷輸入的字符能否回顯在頁(yè)面,如果輸入的字符不輸出在頁(yè)面就沒(méi)有必要進(jìn)行Fuzzing測(cè)試。
注入字符:
檢測(cè)潛在注入點(diǎn)通常使用一個(gè)隨機(jī)字符串,不直接用payload判斷回顯的原因是大多數(shù)應(yīng)用都有防火墻和過(guò)濾機(jī)制,payload會(huì)被檢測(cè)出惡意行為的特征從而被攔截,因此payload不回顯不代表此次輸入點(diǎn)不存在XSS漏洞,可能僅僅是payload被攔截過(guò)濾。
所以采用無(wú)害的隨機(jī)數(shù)字字符就可以避免這種情況產(chǎn)生,先驗(yàn)證可注入,再調(diào)整Payload去繞過(guò)過(guò)濾;而隨機(jī)的目的在于不希望固定字符成為XSS防御黑名單里的關(guān)鍵詞。
實(shí)現(xiàn)思路
重寫python中的htmlparser類,維護(hù)一個(gè)棧tree[]和一個(gè)結(jié)果列表tokenizer[]。
htmlparser中開始標(biāo)簽,結(jié)束標(biāo)簽,自結(jié)束標(biāo)簽,文本內(nèi)容和注釋處理函數(shù)分別改為以下的作用
開始標(biāo)簽: 將標(biāo)簽名 屬性(key value)字典加入到棧中
結(jié)束標(biāo)簽: 如果棧不為空 則彈出樹中的開始標(biāo)簽 并將開始標(biāo)簽信息加入分詞器tokenizer中
自結(jié)束標(biāo)簽 如<img/>: 調(diào)用開始標(biāo)簽壓入棧 再調(diào)用結(jié)束標(biāo)簽加入分詞器中
文本內(nèi)容: 將文本內(nèi)容加入棧中最后一個(gè)壓入的標(biāo)簽content屬性中(默認(rèn)文本內(nèi)容在兩個(gè)標(biāo)簽中)
getTokenizer函數(shù)將樹中的值壓入tokenizer中
實(shí)現(xiàn)代碼
html解析器
class MyHTMLParser(HTMLParser):def __init__(self):super().__init__()self.tree = []self.tokenizer = []self.root = Nonetemp = {"tagname": "","content": "","attributes": []}def handle_starttag(self, tag, attrs):if len(self.tree) == 0:self.root = tagself.tree.append({"tagname": tag,"content": "","attributes": attrs})def handle_endtag(self, tag):if len(self.tree) > 0:r = self.tree.pop()self.tokenizer.append(r)def handle_startendtag(self, tag, attrs):self.handle_starttag(tag, attrs)self.handle_endtag(tag)def handle_data(self, data):if self.tree:self.tree[-1]["content"] += datadef handle_comment(self, data):self.tokenizer.append({"tagname": "#comment","content": data,"attributes": []})def getTokenizer(self):while len(self.tree):r = self.tree.pop()self.tokenizer.append(r)return self.tokenizer潛在注入點(diǎn)搜索
將html響應(yīng)包內(nèi)容和請(qǐng)求時(shí)發(fā)送的測(cè)試字符傳入注入點(diǎn)搜索函數(shù),遍歷htmlparser解析的結(jié)果列表token。
分為三種情況處理,測(cè)試字符處于tagname | content | attribute中。通過(guò)index來(lái)記錄潛在注入點(diǎn)的個(gè)數(shù)。最終把測(cè)試字符回顯的位置種類(html/script/attribute/comment),回顯點(diǎn)index(position)以及所處標(biāo)簽的詳細(xì)信息(tagname,content,attributes)加入occurenes數(shù)組中返回。
(如果回顯點(diǎn)在屬性名或?qū)傩灾抵袆t詳細(xì)信息的content值為key or value)
部分代碼:
針對(duì)pentesterLab XSS第一關(guān)的返回報(bào)文進(jìn)行潛在注入點(diǎn)測(cè)試,測(cè)試payload為xsscheck
測(cè)試結(jié)果
返回報(bào)文中回顯位置 <h2> Hello xsscheck<footer> <p>? PentesterLab 2013</p> </h2>潛在注入點(diǎn)搜索結(jié)果展示 注入點(diǎn)位置種類:html 注入點(diǎn)index:0 注入點(diǎn)詳細(xì)信息:標(biāo)簽名:h2 標(biāo)簽屬性:[] 標(biāo)簽內(nèi)容:Hello xsscheck針對(duì)性測(cè)試
針對(duì)性測(cè)試payload構(gòu)造借鑒了xray的思路,盡可能不發(fā)送敏感字符而采用隨機(jī)無(wú)害字符串去探測(cè)目標(biāo)是否存在xss漏洞,在掃描器漏報(bào)與誤報(bào)的天平中中傾向于誤報(bào)。實(shí)際探測(cè)結(jié)束后還要根據(jù)實(shí)際情況人工繞過(guò)黑白名單過(guò)濾等防御機(jī)制。
通過(guò)潛在注入點(diǎn)測(cè)試得到回顯信息字典數(shù)組occurences后,遍歷回顯點(diǎn)數(shù)組,根據(jù)回顯位置類型分別調(diào)用針對(duì)性函數(shù)進(jìn)行進(jìn)一步測(cè)試,其中paramsName為當(dāng)前遍歷的參數(shù)名,paramsCopy為當(dāng)前的掃描url的參數(shù)字典。
每種環(huán)境的測(cè)試payload均不同,這里以回顯點(diǎn)處于html舉例并給出其他環(huán)境下的payload構(gòu)造思路和測(cè)試說(shuō)明。
html回顯實(shí)現(xiàn)思路
假如潛在注入點(diǎn)在html文本中,則根據(jù)occurence中details得到被包裹的標(biāo)簽名。假如被style標(biāo)簽包裹考慮使用特殊的payload:expression(a(odfqkv))進(jìn)行測(cè)試。
在包裹的標(biāo)簽名不為style的情況下則采用</被包裹標(biāo)簽名><隨機(jī)測(cè)試字符串>嘗試閉合前標(biāo)簽并構(gòu)造新標(biāo)簽來(lái)測(cè)試。此時(shí)要考慮一種特殊情況,即回顯點(diǎn)在html文本中不被任何標(biāo)簽所包裹,潛在注入點(diǎn)搜素會(huì)返回包裹標(biāo)簽為html,此時(shí)就不用加上</html>,測(cè)試payload僅為<隨機(jī)測(cè)試字符串>。
這里是借鑒了xray的xss掃描技巧不發(fā)送帶有敏感字符如img,svg,而采用隨機(jī)測(cè)試字符來(lái)判斷能否產(chǎn)生新標(biāo)簽。
確定了測(cè)試payload后則將當(dāng)前測(cè)試參數(shù)的值改為測(cè)試payload,發(fā)送后進(jìn)行回顯點(diǎn)判斷,假如在響應(yīng)報(bào)文中出現(xiàn)了新標(biāo)簽則代表成功閉合了前標(biāo)簽可以在當(dāng)前環(huán)境下構(gòu)造新標(biāo)簽.
html回顯實(shí)現(xiàn)代碼
def html_check(self, occurence, url, params, paramName):_type = occurence['type']details = occurence['details']# 如果在標(biāo)簽名是style 采用形如expression(a(odfqkv))的payload --> IE6及以下的瀏覽器if details['tagname'] == 'style':payload = "expression(a({}))".format(get_random_str(6))true_payload = 'expression(alert(1))'params[paramName] = payloadres = requests.get(url=url, params=params, headers=getHeader())_locations = searchInputInResponse(html_doc=res.text, xsscheck=payload)for location in _locations:if payload in location['details']['content'] and location['details']['tagname'] == 'style':tmp_res = {'host': get_complete_url(url, params),'ParamPosition': 'query','ParamKey': paramName,'Payload': true_payload,'Request': '','Response': '','msg': 'IE下可執(zhí)行的表達(dá)式 expression(alert(1))'}self.result.append(tmp_res)# 如果被非style標(biāo)簽包裹 試探payload:</被包裹標(biāo)簽名><隨機(jī)七位字符> 真實(shí)payload</被包裹標(biāo)簽名><svg οnlοad=alert`1`>else:flag = get_random_str(7)# 如果文本未被標(biāo)簽包裹(tagname==html) 僅僅為html文本中的內(nèi)容則payload不需要</{tagname}>去閉合上一個(gè)標(biāo)簽if details['tagname'] == 'html':payload = "<{}>".format(flag)true_payload = "{}".format("<svg οnlοad=alert`1`>")else:payload = "</{}><{}>".format(random_upper(details["tagname"]), flag)true_payload = "</{}>{}".format(random_upper(details["tagname"]), "<svg οnlοad=alert`1`>")params[paramName] = payload# print('測(cè)試payload是' + payload)res = requests.get(url=url, params=params, headers=getHeader())_locations = searchInputInResponse(html_doc=res.text, xsscheck=flag)for location in _locations:if location['details']['tagname'] == flag:# print('發(fā)現(xiàn)了flag標(biāo)簽詳細(xì)信息為' + str(location['details']) + '\nxss類型:可在html中構(gòu)造新標(biāo)簽')tmp_res = {'host': get_complete_url(url, params),'ParamPosition': 'query','ParamKey': paramName,'Payload': true_payload,'Request': '','Response': '','msg': 'html文本中可構(gòu)造新標(biāo)簽'}self.result.append(tmp_res)其他回顯的針對(duì)性測(cè)試流程
回顯位置在標(biāo)簽屬性中:
回顯位置在html注釋中:
回顯位置在script中:
結(jié)果處理
最后將針對(duì)性測(cè)試的結(jié)果存在結(jié)果數(shù)組并寫入result.json文件
count = len(result)print('共發(fā)現(xiàn)漏洞' + str(count) + '處')json_data = json.dumps(result, indent=4, ensure_ascii=False)root_path = os.getcwd()result_path = root_path + '\\' + args.outputwith open(result_path, 'w', encoding='utf-8') as f:f.write(json_data)待優(yōu)化部分
參考文章:
XSS掃描器成長(zhǎng)記
跨站的藝術(shù)
總結(jié)
以上是生活随笔為你收集整理的XSS扫描器编写思路的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: selenium + python环境搭
- 下一篇: 最简单的方法教你装matpower