XSS扫描器编写思路
文章目錄
- XSS掃描器
- 設計思路
- url解析與參數合并
- 潛在注入點檢測
- 目的
- 實現思路
- 實現代碼
- html解析器
- 潛在注入點搜索
- 測試結果
- 針對性測試
- html回顯實現思路
- html回顯實現代碼
- 其他回顯的針對性測試流程
- 結果處理
- 待優化部分
XSS掃描器
設計思路
數據來源 : 通過開放爬蟲獲得大量get請求url后,存入掃描器目錄下的target.txt。
url解析與參數合并
解析出url,host,param并與內置字典進行危險參數名合并(借鑒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潛在注入點檢測
目的
潛在注入點檢測是判斷輸入的字符能否回顯在頁面,如果輸入的字符不輸出在頁面就沒有必要進行Fuzzing測試。
注入字符:
檢測潛在注入點通常使用一個隨機字符串,不直接用payload判斷回顯的原因是大多數應用都有防火墻和過濾機制,payload會被檢測出惡意行為的特征從而被攔截,因此payload不回顯不代表此次輸入點不存在XSS漏洞,可能僅僅是payload被攔截過濾。
所以采用無害的隨機數字字符就可以避免這種情況產生,先驗證可注入,再調整Payload去繞過過濾;而隨機的目的在于不希望固定字符成為XSS防御黑名單里的關鍵詞。
實現思路
重寫python中的htmlparser類,維護一個棧tree[]和一個結果列表tokenizer[]。
htmlparser中開始標簽,結束標簽,自結束標簽,文本內容和注釋處理函數分別改為以下的作用
開始標簽: 將標簽名 屬性(key value)字典加入到棧中
結束標簽: 如果棧不為空 則彈出樹中的開始標簽 并將開始標簽信息加入分詞器tokenizer中
自結束標簽 如<img/>: 調用開始標簽壓入棧 再調用結束標簽加入分詞器中
文本內容: 將文本內容加入棧中最后一個壓入的標簽content屬性中(默認文本內容在兩個標簽中)
getTokenizer函數將樹中的值壓入tokenizer中
實現代碼
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潛在注入點搜索
將html響應包內容和請求時發送的測試字符傳入注入點搜索函數,遍歷htmlparser解析的結果列表token。
分為三種情況處理,測試字符處于tagname | content | attribute中。通過index來記錄潛在注入點的個數。最終把測試字符回顯的位置種類(html/script/attribute/comment),回顯點index(position)以及所處標簽的詳細信息(tagname,content,attributes)加入occurenes數組中返回。
(如果回顯點在屬性名或屬性值中則詳細信息的content值為key or value)
部分代碼:
針對pentesterLab XSS第一關的返回報文進行潛在注入點測試,測試payload為xsscheck
測試結果
返回報文中回顯位置 <h2> Hello xsscheck<footer> <p>? PentesterLab 2013</p> </h2>潛在注入點搜索結果展示 注入點位置種類:html 注入點index:0 注入點詳細信息:標簽名:h2 標簽屬性:[] 標簽內容:Hello xsscheck針對性測試
針對性測試payload構造借鑒了xray的思路,盡可能不發送敏感字符而采用隨機無害字符串去探測目標是否存在xss漏洞,在掃描器漏報與誤報的天平中中傾向于誤報。實際探測結束后還要根據實際情況人工繞過黑白名單過濾等防御機制。
通過潛在注入點測試得到回顯信息字典數組occurences后,遍歷回顯點數組,根據回顯位置類型分別調用針對性函數進行進一步測試,其中paramsName為當前遍歷的參數名,paramsCopy為當前的掃描url的參數字典。
每種環境的測試payload均不同,這里以回顯點處于html舉例并給出其他環境下的payload構造思路和測試說明。
html回顯實現思路
假如潛在注入點在html文本中,則根據occurence中details得到被包裹的標簽名。假如被style標簽包裹考慮使用特殊的payload:expression(a(odfqkv))進行測試。
在包裹的標簽名不為style的情況下則采用</被包裹標簽名><隨機測試字符串>嘗試閉合前標簽并構造新標簽來測試。此時要考慮一種特殊情況,即回顯點在html文本中不被任何標簽所包裹,潛在注入點搜素會返回包裹標簽為html,此時就不用加上</html>,測試payload僅為<隨機測試字符串>。
這里是借鑒了xray的xss掃描技巧不發送帶有敏感字符如img,svg,而采用隨機測試字符來判斷能否產生新標簽。
確定了測試payload后則將當前測試參數的值改為測試payload,發送后進行回顯點判斷,假如在響應報文中出現了新標簽則代表成功閉合了前標簽可以在當前環境下構造新標簽.
html回顯實現代碼
def html_check(self, occurence, url, params, paramName):_type = occurence['type']details = occurence['details']# 如果在標簽名是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下可執行的表達式 expression(alert(1))'}self.result.append(tmp_res)# 如果被非style標簽包裹 試探payload:</被包裹標簽名><隨機七位字符> 真實payload</被包裹標簽名><svg οnlοad=alert`1`>else:flag = get_random_str(7)# 如果文本未被標簽包裹(tagname==html) 僅僅為html文本中的內容則payload不需要</{tagname}>去閉合上一個標簽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('測試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('發現了flag標簽詳細信息為' + str(location['details']) + '\nxss類型:可在html中構造新標簽')tmp_res = {'host': get_complete_url(url, params),'ParamPosition': 'query','ParamKey': paramName,'Payload': true_payload,'Request': '','Response': '','msg': 'html文本中可構造新標簽'}self.result.append(tmp_res)其他回顯的針對性測試流程
回顯位置在標簽屬性中:
回顯位置在html注釋中:
回顯位置在script中:
結果處理
最后將針對性測試的結果存在結果數組并寫入result.json文件
count = len(result)print('共發現漏洞' + 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)待優化部分
參考文章:
XSS掃描器成長記
跨站的藝術
總結
以上是生活随笔為你收集整理的XSS扫描器编写思路的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: selenium + python环境搭
- 下一篇: 最简单的方法教你装matpower