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 而現在動態引數已經很普遍了,比如百度貼吧的帖子
https://tieba.baidu.com/p/1000000001
最後的十位數字不位於引數,而是位於路徑中,後端讀取這段路徑的值來作為變數,不管數字怎麼變都是同一個介面處理。
c 甚至於子域名也可能對應同一個介面
https://bj.meituan.com/meishi/
https://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有換行沒換行交替,實在是看了有些尷尬
再是人工稽核,稽核時需要稽核員(可以是固定資深人員、也可以是自選另外一個同事),迭代更新的規則展示更新的部分。稽核主要是依靠經驗判斷有無不可控的風險。