某些網站為了實現友好的用戶交互,提供了一種自定義的錯誤頁面,而不是顯示一個大大的404 ,比如CSDN上的404提示頁面如下:
這樣雖然提高了用戶體驗,但是在編寫對應POC進行檢測的時候如果只根據返回的HTTP頭部信息判斷,則很可能造成誤報,為了能准確檢測到404頁面,
需要從狀態碼和頁面內容兩個方面來進行判斷。
從狀態碼來判斷比較簡單。可以直接使用requests庫發送http請求,得到響應碼即可。
從頁面內容上進行判斷的話,采用的思路是訪問web站點上明顯不存在的頁面,獲取頁面內容進行保存,然后訪問目標頁面,將二者進行比較,如果相似度達到某一閾值,則該頁面為404頁面,否則為正常頁面。
為了判斷兩個頁面的相似度,采用Python的simhash庫,這個庫具體實現的算法我不太懂,但是Python的好處就是:不懂無所謂,直接拿來用就行。這里也只是簡單的拿來用一下:
#-*- encoding:utf-8 -*-
# 404 頁面識別
from hashes.simhash import simhash
import requests
class page_404:
def __init__(self, domain): #檢測站點
self._404_page = [] # 404頁面
self._404_url = [] #404 url
self._404_path = ["test_404.html", "404_test.html", "helloworld.html", "test.asp?action=modify&newsid=122%20and%201=2%20union%20select%201,2,admin%2bpassword,4,5,6,7%20from%20shopxp_admin"] #404頁面路徑,用於生成一部分404頁面
self._404_code = [200, 301, 302] #當前可能是404頁面的http請求的返回值
#自己構造404url,以便收集一些404頁面的信息
for path in self._404_path:
for path in self._404_path:
if domain[-1] == "/":
url = domain + path
else:
url = domain + "/" + path
response = requests.get(url)
if response.status_code in self._404_code:
self.kb_appent(response.content, url)
def kb_appent(self, _404_page, _404_url):
if _404_page not in self._404_page:
self._404_page.append(_404_page)
if _404_url not in self._404_url:
self._404_url.append(_404_url)
def is_similar_page(self, page1, page2):
hash1 = simhash(page1)
hash2 = simhash(page2)
similar = hash1.similarity(hash2)
if similar > 0.85: #當前閾值定義為0.85
return True
else:
return False
def is_404(self, url):
if url in self._404_url:
return True
response = requests.get(url)
if response.status_code == 404:
return True
if response.status_code in self._404_code:
for page in self._404_page:
if self.is_similar_page(response.content, page):
self.kb_appent(url, response.content) #如果是404頁面,則保存當前的url和頁面信息
return True
else:
return False
return False
上面的代碼中,檢測類中主要保存了這樣幾個信息:
_404_page:404頁面,用於與其他請求的頁面進行相似度判斷,以便識別404頁面,這里用列表主要為了防止一個站點有多種404頁面,這段代碼運行時間越長它的准確度越高
_404_url:404 頁面的url,保存之前判斷出頁面是404的url,已經判斷出來的就不再判斷,為了提升效率
_404_path:構建不存在頁面的url,最后一個是一個sql注入的代碼,這里為了識別出那些被防火牆攔截而顯示的錯誤頁面
_404_code:可能返回404頁面的響應碼,如果響應碼為這些,則需要對頁面進行判斷
類在初始化時需要傳入一個域名,根據這個域名來拼接幾個不存在的或者會被防火牆攔截的請求並提交這些請求,得到返回信息,將這些信息作為判斷的信息進行保存。
在判斷時首先根據之前保存的404 url信息進行判斷,如果當前url是404頁面則直接返回,提高效率。然后提交正常的http請求並獲取響應信息,
如果響應碼為404則返回True,否則再狀態碼是否在_404_code列表中,最后再與之前保存的404頁面信息進行比較得到結果。
這段代碼的測試代碼如下:
from page_404 import page_404
if __name__ == '__main__':
domain = "http://xzylrd.gov.cn"
check_404 = page_404(domain)
dest_url = "http://xzylrd.gov.cn/TEXTBOX2.ASP?action=modify&newsid=122%20and%201=2%20union%20select%201,2,admin%2bpassword,4,5,6,7%20from%20shopxp_admin"
print (check_404.is_404(dest_url))