參考
- 大佬的文章
我的Web應用安全模糊測試之路
WebFuzzing方法和漏洞案例總結
實戰筆記之服務端邏輯重構漏洞 - 大佬的字典
fuzzDicts
前言
加一個引數就是一個洞。在挖洞的時候,我注意到一些特殊的請求引數,比如說 output
、retype
、callback
、fun
、width
、height
等等,更改其中一些數值,返回包中會出現明顯變化。看了大師傅們的文章,才逐漸瞭解到這些引數以及一些特殊的 http 頭的妙用。此文作為一個筆記梳理,並自己嘗試寫了一個簡單指令碼(經測試,BUG 巨多?),以免去使用 BURP 測試那麼複雜。
各種型別
CORS
眾所周知,更改 HTTP 的 Origin
頭便可以測試 CORS 漏洞。所以在碰到包含敏感資訊的頁面的時候,便可以重新發包,加一個 Origin
頭。
但是怎麼可能這麼容易就挖到呢?很多網站在 Origin
頭處做了限制,沒有匹配其後臺規則便不會返回 Access-Control-Allow-Origin
等欄位。
常見限制如下:
Origin: https://test.com # 白名單,只允許固定的域名
Origin: http://whatever.com # 未做限制
Origin: http://test.com.evil.vom # 域名字首或必須包含特定域名字串
Origin: http://sub.test.com # 允許子域名
如果沒有匹配上述中的某些規則,網站便不會返回 Access-Control-Allow-Origin
欄位,也就無法判斷是否存在 CORS
當只測試了一種規則但其沒有匹配網站規則,看到沒有返回該欄位是否就應該放棄?
其實不然,我們可以一個個新增/修改,每一個可能的規則都試一遍。
Content-Type
不知道師傅們碰到過可修改返回資料型別的引數沒有,常見的有 retype
、output
、format
、datatype
等。
比如說 output=html、output=json、output=script
或者 retype=1、retype=2、retype=3
,1234分別代表不同資料型別
看到下面一個介面的請求包:
GET /api.php?kw=<img/src/onerror=alert(!)> HTTP/1.1
Host: test.cn
···
返回包為:
HTTP/1.1 200 OK
Content-Type: application/json
···
{"wd":"<img/src/onerror=alert(!)>"}
由於嚴格限定了 Content-Type
頭,所以瀏覽器只會將其作為 json 格式解析。
這個時候是不是沒辦法了,其實可以嘗試新增一些引數
比如說加一個 ?retype=html
引數,返回包可能就會變成 Content-Type: text/html;charset=utf-8
,
此時瀏覽器便會將其解析為 html 格式,於是一個反射型 xss 便有了。
當然得承認這個情況很難利用(引數跟值都無法確定),並且也十分少見,但是在 jsonp 劫持的時候就不見得了。
jsonp 劫持
在請求 json 格式的資料時,請求中常見 callback=test
、call=test
、cb=test
、func=test
、jsoncallback=test
等引數
且此時返回包資料為 test({"paramer":"value"})
,
例如:
可以看到,burp 識別其為 script 格式,當一個個在 burp 中尋找可能的 jsonp 劫持漏洞時,單找 script 格式的資料就會讓人瘋。因為很多 script 資料內容都是 js 函式
我們再次看到上圖,有沒有發現在 script 那一欄有兩個 JSON 格式的資料?其實很多網站返回資訊都為 json 格式。當其中的資料為敏感資料時,由於沒有看到回撥函式,如 callback 時,是不是這個點就無法利用?
其實此時可以測試能不能利用這些 json 格式的資料造一個 jsonp 劫持呢?此時重點來了:
當請求為:
GET /getUser.php HTTP/1.1
Host: test.cn
Cookie: xxxxx
···
網站返回一個這樣的 json 資料包
HTTP/1.1 200 OK
Content-Type: application/json
···
{"name":"R0oKi3","phone":"13888888888","addr":"湖南省長沙市···"}
是不是就不能繼續測試下一步呢?其實不然:
此時可以修改請求包為:
GET /getUser.php?callback=test HTTP/1.1
Host: test.cn
Cookie: xxxxx
···
網站便可能會返回:
HTTP/1.1 200 OK
Content-Type: application/json
···
test({"name":"R0oKi3","phone":"13888888888","addr":"湖南省長沙市···"});
當其他限制(referer、token)不嚴格時,一個 jsonp 劫持漏洞便有了。
當然,大多 json 格式的資料內容是不包含敏感資訊的,那麼是不是就沒用了?其實也不然。
當使用者可控 Content-Type
(也就是上一個點)時,並且也能構成不含敏感資訊的 jsonp 劫持時,此時便可以考慮構成反射型 xss
如 http://test.cn/getUser.php?callback=<img/src/onerror=alerrt(1)>&retype=html
便可以形成反射型 xss,因為其返回的資料包中含:Content-Type: text/html;charset=utf-8
,瀏覽器將該資料包解析成了 html 格式
驗證碼 Dos
右鍵開啟驗證碼/二維碼等圖片的連結,如果看到引數中存在某些鍵值對,如 width=20&heigth=15
等,並且改變其數值大小時,驗證碼圖片大小也跟著改變時,便可能造成驗證碼 Dos
當沒有這些引數時,便可以考慮自己新增,如 https://test.com/captcha.php?width=2000&height=20000
,fuzz 出這些引數,來造成 Dos
並且,當 fuzz 出一個可以控制驗證碼位數的引數時,如 n=4
,也可以將其改為 0 等數字,嘗試是否能使驗證碼失效,或者改為很大的數值,如果圖片大小跟驗證碼位數相關的話,也能造成 Dos
cookie 隱藏引數導致未授權訪問
在 cookie 中新增一些欄位,可能會導致未授權訪問
admin=true
access=1
debug=true
具體可見:我的Web應用安全模糊測試之路
小指令碼 EasyFuzz.py
"""
python3 EasyFuzz.py -u https://test.com -m jsonp
-u 或者 -url 指定 url
-m 或者 -method 指定要探測的可能漏洞,暫時有的選項有 cors jsonp img 三種漏洞的探測
注意,在傳輸 url 引數時,url中有 & 符號與要用雙引號包裹起來,否則會報錯
由於沒有采用多執行緒,所以會丶慢
"""
import requests
import argparse
import re
from tld import get_fld
jsonps = ["callback", "_callback", "func", "cb", "_cb", "jsonp", "jsonpcallback", "jsonpcb", "json", "jsoncallback", "jbc", "jsonp_cb", "call", "callBack", "jsonCallback", "jsonpCb", "ca", "jsonp_Cb"]
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36"
}
def send(url):
rep = requests.get(url, headers=headers)
return rep
def CorsFuzz(url):
# 獲取本域名
subdomin = re.findall(r'(http.*://[^/]*)', url, re.DOTALL)[0]
protocol = re.findall(r'(http.*://)', url, re.DOTALL)[0]
# 獲取主域名
domain = get_fld(subdomin)
# print(subdomin,domain)
# 建立 cors 字典 本身 主域名 任意域名 以其開頭 其他子域名
cors = [subdomin, protocol + domain, protocol + "test.com", protocol + domain + ".test.com", protocol + "test." + domain]
# print(cors)
flag = 0
for cor in cors:
headers["Origin"] = cor
print("------Origin: " + cor)
try:
# 獲取響應頭
# print(url)
reph = send(url).headers
# print(reph)
if "Access-Control-Allow-Origin" in str(reph):
print(url + "存在 CORS,其 Origin 頭為" + cor + "\n返回值為 Access-Control-Allow-Origin:" + reph["Access-Control-Allow-Origin"])
flag = 1
except:
print(url + " 請求錯誤")
if flag == 0:
print(url+" 不存在 CORS 漏洞")
def JsonpFuzz(url):
flag = 0
for jsonp in jsonps:
jsonp = jsonp + "=myJsonpFunc"
try:
if "?" in url:
newurl = url + "&" + jsonp
print("------" + newurl)
rep = send(newurl).content.decode("utf-8")
else:
newurl = url + "?" + jsonp
print("------" + newurl)
rep = send(newurl).content.decode("utf-8")
if "myJsonpFunc" in rep:
flag = 1
print(url + "可能存在 jsonp 劫持,回撥函式為:" + jsonp)
break
except:
print(url + " 請求錯誤")
if flag == 0:
print(url+" 不存在 jsonp 回撥函式")
def ImgFuzz(url):
# 獲取原資料大致大小
repl = len(send(url).content)
par = "height=250&width=250&w=250&h=250&size=250&margin=250&font_size&=250length=250"
try:
if "?" in url:
print(url + "&" + par)
newl = len(send(url + "&" + par).content)
else:
print(url + "&" + par)
newl = len(send(url + "?" + par).content)
except:
print(url + " 請求錯誤")
if abs(newl - repl) >= 1000:
# 當資料大小相差 1000 時
print(url + "可能存在驗證碼 Dos\n測試引數為:" + par)
def main(url, method):
if method=="cors":
CorsFuzz(url)
elif method=="jsonp":
JsonpFuzz(url)
else:
ImgFuzz(url)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Easy Fuzz')
parser.add_argument("-u", "--url", help="指定URL", default="img")
parser.add_argument("-m", "--method", help="指定操作")
args = parser.parse_args()
url = args.url
method = args.method
# url = "https://baidu.com"
# method = "cors"
main(url, method)