DAST 黑盒漏洞掃描器 第三篇:無害化

0X01 前言

甲方掃描器其中一個很重要的功能重點,就是無害化,目的是盡量降低業務影響到可接受程度。
做過甲方掃描器,基本上對於反饋都有所熟悉。
「我們的服務有大量報錯,請問和你們有關么」
「我們的接口昨晚請求量飆升,這是掃描器造成的么」
掃描,應該盡量無感知。
無害化,是掃描器不同於SAST/IAST的一個難點(當然IAST有部署上的性能問題,偶爾的OOM、過高的資源佔用也是大問題),是生存下去的必須考慮的一個點。掃描要有產出、要有效率,而業務側更注重安全、有無業務影響,尤為重要,畢竟誰也不想業務一出問題先來問問是不是安全部門的掃描器。
業務影響的大體場景分類,個人還是分成針對web接口掃描和主機端口掃描造成的業務影響。

0X02 web接口掃描無害化

2.1 QPS

2.1.1 為什麼要控制qps

QPS(Queries-per-second),指掃描器每秒針對業務發出的請求量。

QPS不控制好,很容易隨時導致業務反饋,業務經過一番辛苦排查,發現是掃描器,自然會帶着情緒進行反饋;更嚴重者甚至會導致佔用過多服務資源,導致影響正常業務(可能升級到事務報告,雖然沒收到過)

常見的反饋場景分為幾種:
a. 在nginx層的公司級監控中,掃描器發送的請求超過了某個接口設置的QPS閾值
b. 在web框架的監控中,請求QPS超過閾值
c. 業務自行設置的監控中,請求超過閾值
d. 業務接口本身沒有超過閾值,但是後端的二次調用或多次調用(RPC或其他http服務)超過了閾值

怎麼解決?

2.1.2 聚合方式控制qps的關鍵點

QPS控制需要確定兩個點:
1 聚合方式:通過什麼方式把不同的URL聚合到一個key中
為什麼需要聚合?我們要控制的是對業務的接口的請求量,而不是我們看到的URL的訪問量。

a 同一個path,但是參數不同,大多數情況下還是同一個接口同一個函數在處理;

b 而現在動態參數已經很普遍了,比如百度貼吧的帖子
//tieba.baidu.com/p/1000000001
最後的十位數字不位於參數,而是位於路徑中,後端讀取這段路徑的值來作為變量,不管數字怎麼變都是同一個接口處理。

c 甚至於子域名也可能對應同一個接口
//bj.meituan.com/meishi/
//sh.meituan.com/meishi/
長這樣多數情況下,不同的城市 北京bj、上海sh、廣州gz等,都是對應到同一個美食列表頁服務

如果不聚合,針對每個長得一模一樣的url設置最低的1的qps,當同時對帖子進行掃描時,即使每個url每秒只發送一個請求,在同一段時間內url數量多時,對這個處理的接口發起的訪問量也會變得極大。特別是在業務高峰期(早8點到晚10點業務請求量較大),接口壓力本來就挺大,基於流量鏡像模式的,接收到的url也很多,再做一次掃描,容易把業務打的叫苦不迭。
所以聚合理論上是將表面不一的url收束在屬於它們自己的範圍內,當然越接近接口越精準。

2 QPS值:對應的key能使用多少QPS、每秒能發送多少請求
QPS值,取決於這個接口的承接能力、設置報警的閾值。
值設置的高了,容易打爆業務接口;值設置的低了,掃描速率會較慢,流量大時可能消費不完。
最適合的值上限,是業務正常情況下的請求量均值或上限。
有的業務會按照一分鐘請求總量來設置告警,一分鐘 100-1000,也就對應着 1-17的QPS。平時訪問量較低的接口,QPS為1就足以打報警。

2.1.3 不同的實現方式

a 域名聚合
同一個域名或子域名的請求不超過N(5-10)。
一刀切方式,優點是成本低、效果也挺好。
缺點是絕對平等,不管流量大小都使用同一個QPS,流量大時可能掃描不完。
和任務綁定起來適用於掃描任務多但每個任務流量不多的情況,比如多用戶多任務的情況。

b 集群聚合
每一個url,根據nginx解析結果,獲取url最終歸屬的業務集群。
結合內部監控數據,獲取每個集群實時訪問量,計算出每個集群的訪問峰值;
使用峰值減去實時訪問量(前n分鐘),得到空餘的可用訪問量
缺點:
1 依賴於內部基建的資源
2 同一個集群下的接口,可用的訪問量是基於整個集群的。比如A可用300、B可用300、C可用300,集群整體可用900,但A本身承受量只有500。這樣就會出現QPS傾斜,訪問都打到A,會把A打爆。

c 去重聚合
鏡像模式下基本都會有一套去重程序、流量清洗服務,在去重的時候記錄下每個key的實時訪問量。
用去重的key作為聚合的值,QPS計算的方式和之前一樣,計算每個key的峰值,再減去前n分鐘(在可控範圍內儘可能少)的qps,得到這個key在這一分鐘的可用量。
結合路徑相似度與頁面相似度的去重,去重key接近於接口,效果比較理想。

2.1.4 QPS超限後的限制方式

如果插件最小任務單位已經拆分到請求級別,那麼可以將請求任務丟到隊列里,根據可用量來讀取請求任務運行。
但插件有很多情況需要上下文關聯,比如需要第一個請求獲取登錄憑證token、第二個請求使用該token進行後端RCE等。
這時當掃描規則發送請求時,該請求已經沒有可用QPS了,怎麼做限制

方法1
直接sleep,等待可用了再發送,但會導致任務hang住、佔用節點。資源多無所謂。

方法2
超限後扔回重試隊列中
但是下次掃描並不從頭開始,而是從中斷處開始繼續掃描

不進行斷點重掃,會有部分插件一定掃描不完,比如有的規則掃描時沒有限制,1秒10個請求,無法成功;而QPS控制業務邊緣歸屬於引擎,不應該影響規則本身的檢測效果

記錄每個掃描任務的每個請求hash (url + header + postBody),給插件分類(有無上下文關鍵);
對於沒有上下文的規則,下次任務已經請求過的hash直接跳過;對於有上下文關聯的規則,存儲每個hash的返回,重試時已經請求過的hash,直接讀取上一次的返回。

2.2 業務錯誤碼報錯

業務線會監控服務穩定性可用性,最簡單的就是404/504等錯誤碼的每分鐘佔比。
可用性有的會直接影響到業務的KPI與指標。
這裡應該收集每一次的反饋,和內部監控平台溝通談需求,識別到內部掃描時把這部分請求摘掉;如果是單個業務自身做的報錯收集,只能靠他們自己去解決這些問題了。
這裡注意掃描時要讓業務溯源簡單,自定義的header / ua / 出口IP等,否則溯源老半天才發現是內部掃描,業務會更生氣。

2.3 臟數據

臟數據是一個無法避開不談的問題。

鏡像模式下,只過濾掉header與post,只掃描get,仍然可能會存在臟數據問題。
get理論上應該是做查詢,select操作,但是業務想用來做增刪改操作也無可厚非。

get中可能存在用戶憑證,業務不把token放在cookie中了,而是放在get參數中、平時保存在localstorage中。這種情況帶着get參數值去掃描,等於拿着用戶的身份去掃描,有個信息增刪改操作,就會導致用戶臟數據。
再比如,業務寫接口,登錄界面獲取手機驗證碼,getCode?phone=xxx&type=1,拿到這條流量進行掃描,在對type進行掃描的時候,重複發送給用戶驗證碼,用戶還以為賬號是被黑了。
又或者,沒有增刪改等,只是單純的查詢,但因為帶着用戶信息,頻繁訪問,被反扒處置了,用戶被封禁了。

方法1 清洗與打標

在2.1中,說過接口;在我們有了所有接口數據後,借用其他安全產品的能力(SAST/IAST),給接口打上標籤,判斷是增刪改或者是查;增刪改操作就小心跳過,可避免掉大部分臟數據業務影響情況。再設置參數名黑名單,過濾掉形如phone/token/tele等明顯帶有用戶特性的參數名的值。
這種做法相對安全,覆蓋率取決於SAST/IAST的能力,但也會導致這些接口的漏報

方法2 爬蟲

通過爬蟲,第一篇1.2.4,採用自身測試賬號抓取的流量,通常臟數據問題會比較少,因為這些請求都是正常用戶可以訪問到發出的,也是外部掃描器會收集到的。
用戶能訪問到的接口,造成臟數據,說明外部的用戶也可以造成,特別是外部的白帽子/黑客也會進行掃描。並不是不掃描問題就不存在,只是沒暴露。

方法3 影子環境

直接將現有的DB環境、服務環境複製一份,通過nginx層轉發掃描請求/壓測請求到影子環境,風險極低,但成本極大(成本*2)。
或者單純的複製DB環境,在數據庫連接函數中作hook,把掃描請求的數據庫鏈接改為影子庫。但是操作起來很麻煩,開發維護成本較大,多層調用最後再到DB,和最初的請求關聯起來判斷是不是掃描的就很難。
而且除了常規DB mysql/mongo/redis/MQ/kafka等,還有其他的數據庫甚至是本地log等,轉了一部分但另一些操作沒轉,業務數據層方面就不一致了,只能通過反饋不斷維護,比較麻煩,不太推薦。

方法4 測試環境

第一篇 1.2.6中,測試環境進行掃描。
把掃描作為一個能力開放給業務線,在業務可控的情況下,可以帶cookie掃描、也不需要過濾各種參數,QA在明白風險的情況下進行掃描。
甚至可以進行流量打標,讓業務自行選擇是否對涉及增刪改的接口進行掃描。

PS:
有的收購業務一掃系統就出問題,沒有統一測試環境,沒有統一上線流程可以獲取代碼進行接口打標,脆弱至到處都是未授權,這種情況無能為力。

2.4 掃描時間段

有的業務允許掃描,但希望掃描的時間段放在半夜,因為半夜業務訪問量較低,出了臟數據或者QPS等問題,影響會比白天高峰期時小,到業務 “開門” 也有一定的緩衝時間。

2.5 限制帶寬

雖然都是過濾就可以的,但是和靜態文件影響掃描性能不同,這個會造成成本問題。

掃描時可能會遇到下載文件的接口,請求一次後,接口會把幾十上百兆的文件內容返回給客戶端。這QPS一上來,帶寬蹭蹭蹭就起飛了。私有雲場景可能是和雲服務買帶寬套餐,可能是階梯式收費,也可能是按使用量收費。一旦超量,可能就要多花費個幾百萬,足以發個嚴重事故了。

所以流量過濾時,也要過濾掉content-length過長的、content-type屬於下載類型的。

0X03 主機端口掃描無害化

3.1 內網HTTP服務報錯

內網中有很多HTTP服務,對內網進行框架漏洞掃描時,可能會引起報錯,影響與處理方法同2.2

3.2 指紋匹配過濾不符合端口的數據

內網有很多端口服務,端口服務有自己的報錯姿勢。
有業務自己寫的socket服務,但是沒有做數據校驗,當接收到不符合格式的數據時,拋異常導致進程中斷。
這樣的業務,其實也算存在DOS漏洞,可以提交安全工單敦促業務修復。

也存在部分常規業務,打個比方,redis協議掃描mysql導致mysql處理時間過長hang住(可能不是這兩個服務,時間長忘記了)。
這種可以通過端口指紋方式避免掉,通過給每個端口打上所屬服務/框架的標籤,規則設置一級指紋(URL/HOST等數據類型)、二級指紋(URL數據的框架、HOST端口的服務/框架),在任務處過濾掉不匹配不對應的,可避免數據不匹配導致的服務報錯或者掛掉

3.3 業務方給定指紋

內網可能有RPC框架,接收到不符合端口的數據會報錯,但是框架層面無法作數據校驗、來直接過濾掉不符合的數據,因為使用方需要這些報錯來判斷服務是否有異常。也無法在框架層面直接判斷來源IP和集群過濾掉掃描請求,這不符合框架的設計。

可以由框架提供探測指紋,端口連接後返回特定數據,標識本身服務。

3.4 限制服務同一時間的鏈接數

內網掃描出現的比較多的情況,是一個IP端口的服務被鏈接過多,導致服務異常。

一個要做的是限制同一個 IP+端口 單位的鏈接數,同一時間只能鏈接N個。
另一個,可能存在規則socket.connect後,沒有close,即使任務關閉,但遠程連接方沒有收到關閉請求,還在等待接收數據。即使本身已經限制了鏈接數,還是會打爆。所以規則檢測,需要判斷connect後是否有close。

PS:
當然還可能會有層出不窮的報錯,比如掃描弱口令 比如ssh/mysql/mssql/redis時,引起的大量報錯信息與不停告警。遇到過業務方寫個了mssql代理,掃描時導致DBA mssql各種報錯,但是DBA不知道是哪個機器發送的請求,業務方也不知道自己的代理啟動在哪裡,只知道有掃描器弱口令規則的paylaod,也無法加白名單。最後把IP寫進username里才定位出來。

也可能有內部業務與其他公司有服務對接,在自家內網開了個代理服務器,可映射到其他內網。掃描時檢測到各種端口,其實是其他公司的內網各IP與端口。這種情況十分危險,有反饋最好先加白過濾。代理服務器可能鏈接到其他傳統的、脆弱老舊內網,一掃過去可能就掛了,事故報告就來了。

0X04 規則無害化

4.1 場景

規則無害化由上述方式並不能很好控制。
有的友商掃描SMB (MS17-010)等漏洞的時候,沒有把控好規則的危害程度…直接上了把業務打藍屏的規則;
log4J常規payload,鏈接到dns,會導致log4J等待、hang住,qps過高可能會打掛服務。

4.2 靶場驗證

規則掃描線上服務之前,需要設置靶場鏈接,經過驗證後才能上線,不僅是驗證規則是否有效,也是驗證規則是否會對環境造成影響

4.3 插件審核機制

自動化檢測一些常規規範,比如調用socket.connect後是否有close()等

對個人而言,代碼得或多或少符合PEP8規範吧,雜亂無章的縮進、不明所以的換行、奇怪的dict有換行沒換行交替,實在是看了有些尷尬

再是人工審核,審核時需要審核員(可以是固定資深人員、也可以是自選另外一個同事),迭代更新的規則展示更新的部分。審核主要是依靠經驗判斷有無不可控的風險。