控RDP爆破攻擊并自動(dòng)封禁惡意IP)
很多做運(yùn)維或自己搭過(guò)Windows服務(wù)器的人應(yīng)該都有過(guò)這種經(jīng)歷裝好一臺(tái)Windows Server開(kāi)了遠(yuǎn)程桌面RDP結(jié)果沒(méi)過(guò)幾天一看安全日志密密麻麻全是4625登錄失敗事件源IP來(lái)自天南海北一分鐘能刷幾十條。這些IP就是全網(wǎng)掃描的爆破機(jī)器人不處理的話輕則占用系統(tǒng)資源、干擾日志排查重則真有弱口令被撞庫(kù)成功服務(wù)器直接變?nèi)怆u。我之前也被這個(gè)問(wèn)題煩了很久后來(lái)寫了一個(gè)“監(jiān)控Windows遠(yuǎn)程登錄失敗事件并自動(dòng)封鎖惡意IP”的PowerShell腳本配合任務(wù)計(jì)劃程序跑起來(lái)基本做到了無(wú)人值守自動(dòng)封禁。這篇文章把這個(gè)腳本的設(shè)計(jì)思路、完整實(shí)現(xiàn)、參數(shù)計(jì)算、排坑經(jīng)驗(yàn)全部拆開(kāi)講內(nèi)容偏實(shí)操每一步都可以直接落到自己的機(jī)器上。如果你是剛接觸Windows安全配置的新手也能按步驟跟下來(lái)如果你已經(jīng)寫過(guò)類似腳本重點(diǎn)看后面的細(xì)節(jié)調(diào)優(yōu)和問(wèn)題排查部分應(yīng)該會(huì)有一些參考價(jià)值。1. 為什么要在Windows層面監(jiān)控遠(yuǎn)程登錄失敗場(chǎng)景與威脅模型1.1 誰(shuí)在攻擊你的遠(yuǎn)程桌面爆破攻擊的現(xiàn)實(shí)場(chǎng)景先說(shuō)一個(gè)很現(xiàn)實(shí)的情況只要你的服務(wù)器有公網(wǎng)IP而且開(kāi)放了3389端口你不需要做什么“值得被攻擊”的事也會(huì)被掃描器盯上。掃描器不會(huì)挑目標(biāo)它按網(wǎng)段順序掃發(fā)現(xiàn)響應(yīng)RDP協(xié)議的端口就掛到字典里開(kāi)始試密碼。這類攻擊是7x24小時(shí)不間斷的哪怕你密碼強(qiáng)度再高攻擊者也會(huì)一直換著字典試直到某一天你自己把密碼改弱了或者某臺(tái)機(jī)器真的用了弱口令服務(wù)器就淪陷了。我見(jiàn)過(guò)不止一臺(tái)被攻陷的Windows機(jī)器事后看日志基本都是一個(gè)套路前幾個(gè)小時(shí)還是普通的4625失敗記錄突然某一條4625后面緊跟著一條4624登錄成功然后系統(tǒng)被創(chuàng)建了隱藏賬戶、加了計(jì)劃任務(wù)、端口反彈一條龍拿走。所以監(jiān)控失敗登錄不是為了“看著熱鬧”而是要在爆破階段就把IP按住讓攻擊者根本沒(méi)有機(jī)會(huì)進(jìn)入下一階段。1.2 事件日志里的“數(shù)字指紋”4625事件解析Windows安全日志里遠(yuǎn)程登錄失敗的核心事件ID是4625。這個(gè)事件記錄了非常多的關(guān)鍵字段對(duì)我們寫腳本有用的主要有幾個(gè)Account Name嘗試登錄的用戶名可以判斷是針對(duì)性攻擊還是廣撒網(wǎng)爆破。Source Network Address發(fā)起登錄請(qǐng)求的源IP這是封禁的關(guān)鍵依據(jù)。Logon Type登錄類型遠(yuǎn)程桌面是10網(wǎng)絡(luò)共享是3本地控制臺(tái)是2。我們監(jiān)控RDP失敗重點(diǎn)看類型10。Process Name發(fā)起登錄的進(jìn)程正常情況下是svchost.exe或系統(tǒng)進(jìn)程如果出現(xiàn)奇怪的進(jìn)程名就要小心了。Failure Reason失敗原因區(qū)分密碼錯(cuò)誤、賬戶不存在、賬戶鎖定等情況。這里有一個(gè)特別容易踩的坑4625事件里如果賬戶不存在Account Name會(huì)顯示不存在的用戶名如果賬戶存在但密碼錯(cuò)誤會(huì)顯示真實(shí)的用戶名。這本身不影響我們按IP封禁但如果你后續(xù)想做用戶維度的告警就必須把這兩個(gè)情況分開(kāi)統(tǒng)計(jì)否則會(huì)被大量隨機(jī)用戶名刷屏誤以為有人在爆破真實(shí)賬戶。1.3 只監(jiān)控4625就夠了嗎事件ID組合判斷坦白說(shuō)只盯4625能覆蓋90%以上的爆破場(chǎng)景但作為一套完整的監(jiān)控方案我建議把下面幾個(gè)事件ID一起納入判斷事件ID含義監(jiān)控價(jià)值4625登錄失敗爆破直接證據(jù)核心事件4624登錄成功結(jié)合4625判斷是否被攻破4776憑據(jù)驗(yàn)證失敗NTLM認(rèn)證失敗覆蓋更多登錄場(chǎng)景4740賬戶被鎖定爆破觸發(fā)賬戶鎖定的信號(hào)4771Kerberos預(yù)認(rèn)證失敗域環(huán)境下的失敗認(rèn)證非域場(chǎng)景較少用對(duì)于一臺(tái)獨(dú)立的Windows Server4625 4624組合就夠了。當(dāng)你看到同一IP在短時(shí)間內(nèi)有大量4625且其中緊跟著一條4624說(shuō)明爆破已經(jīng)成功這時(shí)候光封IP來(lái)不及了還要馬上檢查賬戶、清理后門。我自己的腳本里單獨(dú)維護(hù)了一個(gè)“爆破成功”檢測(cè)邏輯發(fā)現(xiàn)這種情況直接觸發(fā)高優(yōu)先級(jí)告警而不是走普通的IP封禁流程。2. 腳本的整體設(shè)計(jì)思路先想清楚再動(dòng)手2.1 兩種常見(jiàn)的監(jiān)控實(shí)現(xiàn)路線對(duì)比實(shí)現(xiàn)“監(jiān)控失敗登錄并封禁IP”這個(gè)需求主流路線有兩條各有適用場(chǎng)景。第一條是事件訂閱觸發(fā)Event Trigger。Windows支持在事件日志上掛訂閱任務(wù)EventTrigger當(dāng)符合條件的事件被寫入日志時(shí)系統(tǒng)自動(dòng)觸發(fā)任務(wù)執(zhí)行腳本。這種方式響應(yīng)最快幾乎是事件發(fā)生就執(zhí)行但配置復(fù)雜度高訂閱關(guān)系容易斷而且如果日志量太大觸發(fā)任務(wù)本身可能成為瓶頸。第二條是計(jì)劃任務(wù)輪詢Scheduled Task Polling。腳本每隔固定時(shí)間比如1分鐘或5分鐘掃描一次安全日志統(tǒng)計(jì)最近時(shí)間窗口內(nèi)的失敗次數(shù)達(dá)到閾值就執(zhí)行封禁。這種方式實(shí)現(xiàn)簡(jiǎn)單、邏輯可控缺點(diǎn)是響應(yīng)有延遲但對(duì)于爆破封鎖場(chǎng)景1分鐘級(jí)別的延遲完全可以接受——攻擊者要試幾千幾萬(wàn)個(gè)密碼組合不可能在1分鐘內(nèi)完成。對(duì)比下來(lái)我個(gè)人更推薦計(jì)劃任務(wù)輪詢。原因有三個(gè)一是實(shí)現(xiàn)難度低整個(gè)邏輯寫在一個(gè)PowerShell腳本里就行不需要額外配置事件訂閱二是便于調(diào)試腳本可以手動(dòng)執(zhí)行輸出日志一眼就能看出問(wèn)題三是策略調(diào)整方便想改變掃描頻率、閾值改配置參數(shù)就行。事件訂閱路線適合對(duì)實(shí)時(shí)性要求極高的場(chǎng)景比如檢測(cè)登錄成功后立刻觸發(fā)阻斷普通服務(wù)器不需要這個(gè)級(jí)別的響應(yīng)速度。2.2 為什么我選擇了“踩點(diǎn)記錄增量掃描”的設(shè)計(jì)簡(jiǎn)單的輪詢方案有個(gè)問(wèn)題每次掃描都把最近幾分鐘內(nèi)的所有4625事件統(tǒng)計(jì)一遍那么上一輪已經(jīng)處理過(guò)的IP下一輪會(huì)被重復(fù)統(tǒng)計(jì)導(dǎo)致計(jì)數(shù)器虛高。比如一個(gè)IP在10分鐘內(nèi)發(fā)起了30次失敗登錄你把掃描間隔設(shè)為5分鐘第一輪統(tǒng)計(jì)20次觸發(fā)封禁第二輪又把20次統(tǒng)計(jì)一遍沒(méi)被封禁的其他IP也跟著被重復(fù)計(jì)數(shù)閾值判斷就失真了。所以我的腳本采用了一個(gè)很關(guān)鍵的設(shè)計(jì)記錄每一輪掃描到的最后一條事件記錄IDEventRecordId下一輪只掃描比這個(gè)ID更新的日志。這樣每個(gè)事件只會(huì)被統(tǒng)計(jì)一次計(jì)數(shù)是真實(shí)的不會(huì)重復(fù)累加。這個(gè)思路其實(shí)跟增量同步差不多每次只處理新增的部分天然避免了重復(fù)消費(fèi)。另外在封禁狀態(tài)上也要做“冪等”處理。腳本每次封IP之前先查一下防火墻里有沒(méi)有這條規(guī)則如果已存在就跳過(guò)封禁動(dòng)作只更新計(jì)數(shù)信息。冪等設(shè)計(jì)的好處是即使任務(wù)被重復(fù)執(zhí)行或者多臺(tái)機(jī)器同時(shí)跑同一個(gè)腳本也不會(huì)產(chǎn)生重復(fù)的防火墻規(guī)則這個(gè)對(duì)后續(xù)人工清理特別重要。2.3 整體架構(gòu)與任務(wù)執(zhí)行流程整個(gè)系統(tǒng)的結(jié)構(gòu)可以畫(huà)成下面這個(gè)邏輯流不用畫(huà)圖軟件文字描述也足夠清晰任務(wù)計(jì)劃程序每1分鐘調(diào)用一次PowerShell腳本。腳本查詢安全日志中Event ID 4625日志類型為Security過(guò)濾掉本地回環(huán)地址127.0.0.1和::1。只取LogonType 10的遠(yuǎn)程桌面登錄事件提取源IP和用戶名。只處理事件記錄ID大于上次標(biāo)記值的增量事件。按源IP聚合統(tǒng)計(jì)在滾動(dòng)時(shí)間窗口內(nèi)失敗的次數(shù)達(dá)到閾值進(jìn)入封禁流程。封禁前三重白名單檢查內(nèi)網(wǎng)網(wǎng)段、本機(jī)回環(huán)、自定義白名單列表。執(zhí)行netsh advfirewall firewall add rule新增入站阻斷規(guī)則。腳本把處理結(jié)果寫入自定義日志文件供后續(xù)查看。這套流程看起來(lái)不復(fù)雜但每一步都對(duì)應(yīng)著一個(gè)實(shí)際的坑后面我會(huì)在實(shí)現(xiàn)和排查部分展開(kāi)講。3. 從零搭建PowerShell腳本漸進(jìn)式實(shí)現(xiàn)3.1 環(huán)境準(zhǔn)備日志設(shè)置與腳本權(quán)限在寫腳本之前有兩件基礎(chǔ)事情必須確認(rèn)不然腳本跑起來(lái)會(huì)各種莫名其妙。第一件事是確保安全日志足夠大。Windows安全日志默認(rèn)上限可能只有20MB或128MB不同版本不同在高強(qiáng)度爆破下日志很快會(huì)被寫滿而默認(rèn)策略是“覆蓋最舊事件”這意味著你的腳本還沒(méi)來(lái)得及掃描舊事件就被沖掉了直接導(dǎo)致漏統(tǒng)計(jì)。強(qiáng)烈建議把安全日志上限調(diào)高至少512MB甚至1GB。注意調(diào)整日志大小在WinR執(zhí)行eventvwr.msc選擇“Windows日志 安全”右側(cè)“屬性”設(shè)置日志最大大小并勾選“不覆蓋事件手動(dòng)清除日志”。不覆蓋模式也意味著日志滿了之后新事件寫不進(jìn)去所以日志大小要根據(jù)攻擊強(qiáng)度合理設(shè)置。我個(gè)人習(xí)慣設(shè)為1GB按一臺(tái)被重點(diǎn)爆破的機(jī)器一天幾十萬(wàn)條失敗日志來(lái)算大約能存7-15天夠用了。第二件事是PowerShell執(zhí)行策略。默認(rèn)Windows機(jī)器的執(zhí)行策略可能是Restricted腳本根本不讓跑。在管理員PowerShell里執(zhí)行Set-ExecutionPolicy RemoteSigned -Force執(zhí)行策略改為RemoteSigned的意思是本地創(chuàng)建的腳本可以直接運(yùn)行從網(wǎng)絡(luò)下載的腳本需要有簽名。對(duì)絕大多數(shù)場(chǎng)景來(lái)說(shuō)這個(gè)策略夠用且風(fēng)險(xiǎn)可控。3.2 核心腳本一捕獲失敗登錄并記錄追蹤狀態(tài)下面這個(gè)腳本是整個(gè)監(jiān)控系統(tǒng)的主腳本負(fù)責(zé)掃描日志、提取IP、維護(hù)增量狀態(tài)。代碼不長(zhǎng)但每一行背后都有講究。# 監(jiān)控遠(yuǎn)程登錄失敗事件并自動(dòng)封鎖惡意IP # 適用Windows Server 2016/2019/2022PowerShell 5.1 及以上 param( [int]$ScanWindowMinutes 5, # 統(tǒng)計(jì)時(shí)間窗口 [int]$FailThreshold 5, # 閾值窗口內(nèi)失敗次數(shù)達(dá)到該值則封禁 [string]$StateFile C:\Scripts\rdp_monitor_state.json, [string]$LogFile C:\Scripts\rdp_monitor.log ) function Write-Log { param([string]$Message) $time Get-Date -Format yyyy-MM-dd HH:mm:ss Add-Content -Path $LogFile -Value [$time] $Message } # 讀取上次處理的事件記錄ID $lastId if (Test-Path $StateFile) { (Get-Content $StateFile -Raw | ConvertFrom-Json).LastEventRecordId } else { 0 } # 查詢安全日志中4625事件只取增量部分 $filter { LogName Security Id 4625 StartTime (Get-Date).AddMinutes(-$ScanWindowMinutes) } $events Get-WinEvent -FilterHashtable $filter -ErrorAction SilentlyContinue | Where-Object { $_.RecordId -gt $lastId -and $_.Properties[18].Value -eq 10 } # 統(tǒng)計(jì)每個(gè)源IP在窗口內(nèi)的失敗次數(shù) $ipFailCount {} $ipUserMap {} foreach ($evt in $events) { # Properties數(shù)組索引對(duì)應(yīng)不同字段這里用名字解析更穩(wěn) $ip $null $user $null foreach ($prop in $evt.Properties) { if ($prop.Value -match ^\d{1,3}(\.\d{1,3}){3}$) { $ip $prop.Value } if ($prop.Name -match Account Name) { $user $prop.Value } } if (-not $ip) { continue } if (-not $ipFailCount.ContainsKey($ip)) { $ipFailCount[$ip] 0 } $ipFailCount[$ip] if (-not $ipUserMap.ContainsKey($ip)) { $ipUserMap[$ip] () } if ($user -and $ipUserMap[$ip] -notcontains $user) { $ipUserMap[$ip] $user } } # 記錄當(dāng)前最大記錄ID供下一輪掃描用 $maxId if ($events) { ($events | Measure-Object -Property RecordId -Maximum).Maximum } else { $lastId } { LastEventRecordId $maxId } | ConvertTo-Json | Set-Content -Path $StateFile Write-Log 掃描完成增量事件數(shù): $($events.Count)最大記錄ID: $maxId # 輸出統(tǒng)計(jì)結(jié)果供下一步封禁腳本使用 $ipFailCount.GetEnumerator() | ForEach-Object { [PSCustomObject]{ IP $_.Key Count $_.Value AttemptedUsers ($ipUserMap[$_.Key] -join ,) } } | Export-Clixml -Path C:\Scripts\rdp_failed_summary.xml這里重點(diǎn)解釋兩個(gè)細(xì)節(jié)第一LogonType的判斷方式。4625事件的Properties是一個(gè)數(shù)組不同位置的元素代表不同字段。直接按下標(biāo)比如Properties[18]判斷LogonType在Windows版本變化時(shí)容易出錯(cuò)。更穩(wěn)妥的做法是遍歷Properties通過(guò)字段名Logon Type來(lái)定位值。上面的代碼里為了簡(jiǎn)潔沒(méi)有完全展開(kāi)實(shí)際用的時(shí)候建議用名字解析我在后面的完整版腳本里就是這么寫的。第二源IP的提取。4625事件里源IP字段在Properties中的名字叫Source Network Address取值是字符串形式的IP地址。上面這段用了正則匹配的方式從所有屬性值里找類似IP地址的字段這種寫法簡(jiǎn)單粗暴但可靠因?yàn)镮P地址的格式足夠特殊不會(huì)跟其他字段混淆。當(dāng)然如果追求嚴(yán)謹(jǐn)還是按屬性名精確提取更規(guī)范。3.3 核心腳本二自動(dòng)封鎖惡意IP掃描腳本拿到統(tǒng)計(jì)結(jié)果后下一步就是封禁。這里有個(gè)設(shè)計(jì)上的選擇可以把封禁邏輯寫進(jìn)同一個(gè)腳本也可以拆成獨(dú)立腳本。我傾向于拆成兩個(gè)因?yàn)槁氊?zé)分離便于單獨(dú)調(diào)試和維護(hù)。下面是被調(diào)用的封禁腳本核心段# 封禁腳本 block-bad-ip.ps1 param( [string]$StateFile C:\Scripts\rdp_monitor_state.json, [string]$SummaryXml C:\Scripts\rdp_failed_summary.xml, [string]$WhitelistFile C:\Scripts\rdp_whitelist.txt ) $summary Import-Clixml -Path $SummaryXml if (-not $summary) { exit } # 白名單文件里每行一個(gè)IP或網(wǎng)段 $whitelist if (Test-Path $WhitelistFile) { Get-Content $WhitelistFile } else { () } # 遍歷統(tǒng)計(jì)結(jié)果按閾值封禁 foreach ($item in $summary) { $ip $item.IP # 跳過(guò)回環(huán)與內(nèi)網(wǎng)地址避免誤封本機(jī)或內(nèi)網(wǎng)設(shè)備 if ($ip -eq 127.0.0.1 -or $ip -like 10.* -or $ip -like 192.168.* -or $ip -like 172.16.* -or $ip -like 172.31.*) { continue } if ($ip -in $whitelist) { continue } # 閾值判斷 if ($item.Count -ge 5) { # 先檢查是否已有規(guī)則冪等處理 $existing Get-NetFirewallRule -DisplayName RDP_BLOCK_$ip -ErrorAction SilentlyContinue if (-not $existing) { New-NetFirewallRule -DisplayName RDP_BLOCK_$ip -Direction Inbound -Protocol TCP -LocalPort 3389 -RemoteAddress $ip -Action Block | Out-Null Write-Log 已封禁IP: $ip, 失敗次數(shù): $($item.Count), 嘗試用戶: $($item.AttemptedUsers) } else { Write-Log IP已在封禁列表: $ip, 當(dāng)前失敗次數(shù): $($item.Count) } } }這個(gè)封禁方案用的是Windows Defender Firewall的入站規(guī)則。RemoteAddress參數(shù)支持單個(gè)IP地址也支持CIDR格式網(wǎng)段。如果你需要封的IP非常多直接給每個(gè)IP建單條規(guī)則會(huì)造成幾千條規(guī)則堆積查起來(lái)麻煩但阻斷性能沒(méi)有明顯問(wèn)題因?yàn)榉阑饓σ?guī)則是按順序匹配的遍歷幾千條的成本對(duì)現(xiàn)代CPU來(lái)說(shuō)可以忽略。如果想更精細(xì)也可以按網(wǎng)絡(luò)前綴聚合比如把連續(xù)的同C段IP合并成一條規(guī)則但聚合邏輯會(huì)復(fù)雜很多普通場(chǎng)景沒(méi)必要。3.4 注冊(cè)計(jì)劃任務(wù)讓系統(tǒng)自動(dòng)周期性執(zhí)行腳本寫完以后剩下最關(guān)鍵的一步是讓系統(tǒng)自動(dòng)、持續(xù)地運(yùn)行它。這一步做不好腳本寫得再漂亮也是白費(fèi)。用任務(wù)計(jì)劃程序注冊(cè)一個(gè)每分鐘執(zhí)行一次的定時(shí)任務(wù)操作如下WinR 輸入taskschd.msc打開(kāi)任務(wù)計(jì)劃程序。右側(cè)操作欄點(diǎn)“創(chuàng)建任務(wù)”名稱填RDP失敗登錄監(jiān)控與封禁。“常規(guī)”選項(xiàng)卡選擇“使用最高權(quán)限運(yùn)行”否則寫安全日志和防火墻規(guī)則可能權(quán)限不足。“觸發(fā)器”選項(xiàng)卡新建觸發(fā)器“重復(fù)任務(wù)間隔”設(shè)為1分鐘“持續(xù)時(shí)間”選擇“無(wú)限期”勾選“已啟用”。“操作”選項(xiàng)卡新建操作程序填powershell.exe參數(shù)填-ExecutionPolicy Bypass -WindowStyle Hidden -File C:\Scripts\rdp_monitor.ps1。“條件”選項(xiàng)卡取消勾選“只有在計(jì)算機(jī)使用交流電源時(shí)才啟動(dòng)此任務(wù)”避免服務(wù)器臨時(shí)切換到電池場(chǎng)景時(shí)任務(wù)不跑。“設(shè)置”選項(xiàng)卡勾選“如果任務(wù)運(yùn)行失敗按以下頻率重啟每次嘗試重新運(yùn)行”并設(shè)置重試次數(shù)同時(shí)勾選“如果任務(wù)運(yùn)行時(shí)間超過(guò)以下時(shí)間停止任務(wù)”。注意任務(wù)計(jì)劃程序里的“使用最高權(quán)限運(yùn)行”是基于管理員憑據(jù)的。創(chuàng)建任務(wù)時(shí)輸入的密碼如果是普通管理員可能還是會(huì)遇到UAC截?cái)鄦?wèn)題。最穩(wěn)妥的方式是用一個(gè)獨(dú)立的服務(wù)賬戶并賦予“作為批處理作業(yè)登錄”的權(quán)限或者直接用內(nèi)置管理員賬戶創(chuàng)建任務(wù)。生產(chǎn)環(huán)境建議用托管服務(wù)賬戶gMSA之類的方案但小規(guī)模服務(wù)器直接用內(nèi)置管理員也問(wèn)題不大。注冊(cè)好以后建議在計(jì)劃任務(wù)里點(diǎn)“運(yùn)行”手動(dòng)觸發(fā)一次確認(rèn)腳本能正常執(zhí)行并且日志有輸出然后把觸發(fā)器改成每分鐘運(yùn)行也可以反過(guò)來(lái)先手動(dòng)跑排錯(cuò)完再掛任務(wù)。4. 封禁策略與參數(shù)調(diào)優(yōu)這里藏著大量細(xì)節(jié)4.1 封禁閾值怎么定失敗次數(shù)與時(shí)間窗口的計(jì)算閾值和時(shí)間窗口是兩個(gè)緊密相關(guān)的參數(shù)不能拍腦袋定。設(shè)置太激進(jìn)比如窗口2分鐘內(nèi)失敗2次就封禁很可能會(huì)把正常用戶誤封比如別人在遠(yuǎn)程桌面里連了幾次密碼敲錯(cuò)兩次就被封了設(shè)置太寬松比如窗口60分鐘失敗100次才封禁那攻擊者可能已經(jīng)試了幾萬(wàn)個(gè)密碼風(fēng)險(xiǎn)太高。我給的參考經(jīng)驗(yàn)值5分鐘內(nèi)失敗5次即$ScanWindowMinutes 5$FailThreshold 5。這個(gè)數(shù)值怎么推出來(lái)的先看正常用戶行為一個(gè)人手動(dòng)輸錯(cuò)密碼連續(xù)失敗三四次就會(huì)警覺(jué)并且Windows默認(rèn)也有賬戶鎖定策略通常鎖定閾值為5-10次所以正常用戶大概率在5分鐘內(nèi)不會(huì)有5次以上的失敗。再看攻擊行為爆破工具跑字典時(shí)一個(gè)IP一分鐘內(nèi)通常能發(fā)起20-100次登錄嘗試5分鐘內(nèi)失敗次數(shù)至少是幾十次。所以5分鐘5次的閾值可以做到攻擊IP大概率命中正常用戶幾乎不會(huì)觸發(fā)。如果你希望更靈敏可以降低到3分鐘3次如果攻擊非常猛烈可以提閾值到10次。核心原則是閾值必須顯著大于正常用戶的最差輸入同時(shí)顯著小于攻擊者的最低強(qiáng)度。另外有一個(gè)很多人忽略的點(diǎn)賬戶鎖定策略與腳本封禁策略的聯(lián)動(dòng)。如果Windows賬戶鎖定閾值設(shè)得比較低比如3次鎖定賬戶那么攻擊者靠爆破大概率不會(huì)成功但因?yàn)殒i定和解鎖也需要管理員介入可能造成合法用戶被鎖。所以如果你用了自動(dòng)化封禁腳本可以考慮把系統(tǒng)的賬戶鎖定閾值調(diào)高一些比如10次讓腳本封IP成為第一道防線賬戶鎖定策略退居其次。這是一個(gè)策略協(xié)同的權(quán)衡不要盲目參照網(wǎng)上默認(rèn)的“3次鎖定”配置。4.2 白名單機(jī)制千萬(wàn)別把自己鎖在門外自動(dòng)封禁最難處理的風(fēng)險(xiǎn)是誤封尤其是把自己封了。很多時(shí)候你在公司內(nèi)網(wǎng)遠(yuǎn)程到服務(wù)器源IP是固定IP或者動(dòng)態(tài)分配的辦公網(wǎng)出口IP一旦腳本誤判你可能就再也連不上了。更麻煩的是外網(wǎng)管理場(chǎng)景某人出差在酒店連服務(wù)器IP變了就直接被腳本封禁嚴(yán)重影響業(yè)務(wù)。所以在封禁邏輯里白名單必須是第一優(yōu)先級(jí)。我的做法是用一個(gè)獨(dú)立的文本文件rdp_whitelist.txt每行一個(gè)IP或網(wǎng)段腳本先讀白名單再?zèng)Q定是否封禁。白名單不僅包含固定的管理員IP還要把所有內(nèi)網(wǎng)網(wǎng)段都放進(jìn)去10.0.0.0/8 192.168.0.0/16 172.16.0.0/12 118.xx.xx.xx # 公司固定出口IP注意腳本里New-NetFirewallRule的RemoteAddress參數(shù)既接受單個(gè)IP也接受CIDR網(wǎng)段但白名單匹配代碼要單獨(dú)處理CIDR格式不能用簡(jiǎn)單的字符串包含判斷。上面的示例里我用的是-like 10.*這種前綴通配方式這只適合標(biāo)準(zhǔn)網(wǎng)段劃分。更通用、更嚴(yán)謹(jǐn)?shù)淖龇ㄊ墙馕鯥P與掩碼做子網(wǎng)比較完整代碼里可以用IPv4地址加前綴長(zhǎng)度拆成二進(jìn)制位做與運(yùn)算網(wǎng)上有很多工具函數(shù)可以直接抄這里不展開(kāi)。經(jīng)驗(yàn)白名單文件里一定要把本機(jī)網(wǎng)卡所在網(wǎng)段寫進(jìn)去。有些云服務(wù)器有多塊網(wǎng)卡公網(wǎng)IP是NAT映射到內(nèi)網(wǎng)IP的腳本只看到內(nèi)網(wǎng)IP如果不加白名單本機(jī)或者云平臺(tái)健康檢查的IP就可能被誤封導(dǎo)致服務(wù)器被云平臺(tái)判定為不健康。這個(gè)坑我踩過(guò)必須提醒。4.3 防火墻規(guī)則管理封禁后的生命周期問(wèn)題被封禁的IP是永久封還是臨時(shí)封永久封的好處是省心壞處是規(guī)則列表會(huì)越來(lái)越長(zhǎng)而且某些IP是運(yùn)營(yíng)商動(dòng)態(tài)分配的過(guò)一段時(shí)間可能被分配給正常用戶持續(xù)封禁會(huì)傷及無(wú)辜。臨時(shí)封的好處是規(guī)則庫(kù)不會(huì)膨脹誤封能自動(dòng)解除壞處是攻擊者換個(gè)IP就能再來(lái)。我的做法是默認(rèn)永久封禁 定期人工復(fù)核白名單。理由有兩個(gè)一是爆破IP的池子很大很多IP來(lái)自惡意網(wǎng)絡(luò)永久封禁無(wú)傷大雅二是定期看一遍封鎖清單把明顯的誤封IP加進(jìn)白名單并手動(dòng)刪除規(guī)則成本很低。如果你不想人工介入也可以加一個(gè)“過(guò)期解封”邏輯給每條規(guī)則打上時(shí)間戳比如7天后自動(dòng)刪除這個(gè)實(shí)現(xiàn)也不復(fù)雜但腳本要從規(guī)則描述字段里解析創(chuàng)建時(shí)間代碼會(huì)啰嗦一些。綜合來(lái)看小型服務(wù)器人工復(fù)核更劃算大規(guī)模場(chǎng)景才需要自動(dòng)解封。5. 實(shí)際運(yùn)行中的常見(jiàn)問(wèn)題與排查手冊(cè)跑了半年這個(gè)腳本之后我把遇到過(guò)的典型問(wèn)題整理如下每一類都對(duì)應(yīng)一個(gè)真實(shí)的坑。5.1 安全日志被寫滿導(dǎo)致漏統(tǒng)計(jì)之前提到安全日志默認(rèn)大小有限在持續(xù)爆破下很容易被寫滿。如果你設(shè)置的是“覆蓋最舊事件”那腳本還沒(méi)跑到舊的失敗記錄就被新的覆蓋了。尤其當(dāng)腳本按“增量事件ID”方式掃描時(shí)如果日志覆蓋發(fā)生記錄的LastEventRecordId對(duì)應(yīng)的那條事件已經(jīng)不存在但新的日志ID比記錄值大所以還能接著掃只是中間被覆蓋的部分丟了計(jì)數(shù)會(huì)偏低。這種情況下你看到的封禁數(shù)量會(huì)明顯少于實(shí)際攻擊IP數(shù)量。解決思路前面說(shuō)過(guò)把安全日志調(diào)大到1GB并且選擇“不覆蓋事件”。但這里有個(gè)新的問(wèn)題日志寫滿后新事件無(wú)法寫入審計(jì)能力反而斷了。所以更完整的方案是定期導(dǎo)出安全日志歸檔并清空或者用第三方日志收集工具轉(zhuǎn)發(fā)出去。對(duì)單機(jī)小場(chǎng)景1GB 每月手動(dòng)歸檔一次就夠。5.2 任務(wù)計(jì)劃程序運(yùn)行失敗權(quán)限與執(zhí)行策略我最初遇到過(guò)任務(wù)計(jì)劃里腳本運(yùn)行但沒(méi)有任何效果的情況。排查后發(fā)現(xiàn)根本原因是PowerShell執(zhí)行策略在 System 上下文里沒(méi)有生效Set-ExecutionPolicy改的是當(dāng)前用戶級(jí)別的策略而計(jì)劃任務(wù)以管理員賬戶運(yùn)行卻讀取的是本機(jī)策略。最后的解決辦法是在計(jì)劃任務(wù)的啟動(dòng)參數(shù)里顯式加-ExecutionPolicy Bypass這就繞過(guò)了策略限制。另一個(gè)問(wèn)題是腳本寫日志文件時(shí)如果路徑不存在腳本會(huì)報(bào)錯(cuò)退出。解決方法是腳本開(kāi)頭把日志目錄和狀態(tài)文件目錄都補(bǔ)建一遍不要依賴手動(dòng)創(chuàng)建。另外計(jì)劃任務(wù)默認(rèn)的“運(yùn)行用戶”如果不是管理員組訪問(wèn)安全日志和防火墻規(guī)則就會(huì)報(bào)“拒絕訪問(wèn)”。創(chuàng)建任務(wù)時(shí)務(wù)必選擇管理員賬戶并且勾選“使用最高權(quán)限運(yùn)行”這個(gè)在3.4節(jié)提過(guò)但太多人栽在這里還是值得再說(shuō)一次。5.3 誤封和漏封IP提取與判斷邏輯的問(wèn)題有一次我發(fā)現(xiàn)某個(gè)內(nèi)網(wǎng)網(wǎng)段的IP被批量封了查日志發(fā)現(xiàn)那個(gè)IP的失敗次數(shù)很高問(wèn)題就出在它不僅是RDP的源IP也是某臺(tái)內(nèi)網(wǎng)打印機(jī)的IP。很多設(shè)備在局域網(wǎng)里用SMB或RDP短連接嘗試登錄也會(huì)有失敗記錄而我的腳本只按類型10遠(yuǎn)程桌面過(guò)濾理論上不該把內(nèi)網(wǎng)打印機(jī)計(jì)入后來(lái)仔細(xì)看發(fā)現(xiàn)打印機(jī)走的是另一個(gè)端口但同一IP上還有別的服務(wù)發(fā)起的4625記錄類型也是10這就傻了。避免這種誤判的方法是加端口維度。腳本可以只處理Source Network Address對(duì)應(yīng)的連接端口是3389的事件但4625事件里“源端口”屬性在Properties中是沒(méi)有直接暴露的需要配合網(wǎng)絡(luò)跟蹤或更底層的日志。這個(gè)信息在大多數(shù)情況下可用性不高所以我采用了更實(shí)用的策略把所有內(nèi)網(wǎng)網(wǎng)段統(tǒng)統(tǒng)加進(jìn)白名單。不需要精確到端口只按網(wǎng)段排除簡(jiǎn)單粗暴但可靠。漏封的情況則相反某個(gè)攻擊IP走的是IPv6::ffff:xxx.xxx.xxx.xxx這種映射格式腳本的正則只匹配純IPv4導(dǎo)致漏掉。遇到這種情況可以把正則改成同時(shí)匹配IPv4和IPv4-mapped IPv6或者干脆統(tǒng)一轉(zhuǎn)成字符串判斷是否包含IPv4四段數(shù)字。實(shí)戰(zhàn)中純IPv6掃描器很少但映射格式確實(shí)有建議在源IP提取時(shí)做一次標(biāo)準(zhǔn)化處理。5.4 誤封自己之后如何緊急恢復(fù)這是最緊急的場(chǎng)景我也踩過(guò)一次。當(dāng)時(shí)把公司出口IP寫進(jìn)白名單里但腳本判斷邏輯有bug白名單沒(méi)生效結(jié)果整個(gè)公司出口被封連遠(yuǎn)程桌面都進(jìn)不去。這種時(shí)候人有兩種選擇第一去云控制臺(tái)用VNC/Web終端登錄服務(wù)器刪掉對(duì)應(yīng)防火墻規(guī)則第二如果你用的是云主機(jī)且廠商提供了安全組在安全組層面臨時(shí)放行從外部倒過(guò)來(lái)解封本機(jī)防火墻規(guī)則。在腳本設(shè)計(jì)上應(yīng)對(duì)這種風(fēng)險(xiǎn)最優(yōu)的辦法是給腳本加一個(gè)“最大封禁數(shù)量”上限。比如單次運(yùn)行最多封10個(gè)IP超過(guò)就不再封了這樣即使白名單有bug也不會(huì)一瞬間封光整個(gè)網(wǎng)段。另一個(gè)辦法是加“封禁通知”每次執(zhí)行封禁時(shí)往運(yùn)維郵箱發(fā)一封郵件知道封了什么IP出了問(wèn)題也能快速定位。6. 擴(kuò)展與優(yōu)化從“能用”到“好用”基礎(chǔ)版本的腳本已經(jīng)能完成“監(jiān)控-統(tǒng)計(jì)-封禁”的閉環(huán)但實(shí)際使用中還值得再做幾個(gè)增強(qiáng)按投入產(chǎn)出比排序第一個(gè)增強(qiáng)是告警通知。被封IP是不需要人盯著的但“爆破成功”必須有人響應(yīng)。我在腳本里加了4624登錄成功的檢測(cè)一旦發(fā)現(xiàn)同一IP在連續(xù)幾次4625后出現(xiàn)4624立即發(fā)告警郵件。把郵件通知用SMTP協(xié)議發(fā)出去PowerShell自帶的Send-MailMessage就能實(shí)現(xiàn)但需要注意新版PowerShell移除了這個(gè)cmdlet建議直接用System.Net.Mail命名空間。第二個(gè)增強(qiáng)是IP信譽(yù)聯(lián)動(dòng)。單純按失敗次數(shù)封禁是自學(xué)習(xí)策略還可以引入外部威脅情報(bào)庫(kù)。比如把封禁的IP拿到公共威脅情報(bào)接口查詢?nèi)绻且阎膼阂鈷呙柙淳吞岣叻饨麅?yōu)先級(jí)如果是正常的IDC出口可以考慮加長(zhǎng)觀察期。這個(gè)對(duì)小型服務(wù)器性價(jià)比不高但對(duì)防護(hù)要求較高的生產(chǎn)環(huán)境很有價(jià)值。第三個(gè)增強(qiáng)是規(guī)則聚合和清理。運(yùn)行幾個(gè)月后防火墻里可能積累了幾萬(wàn)條封鎖規(guī)則手動(dòng)清理不現(xiàn)實(shí)??梢詫懸粋€(gè)定期任務(wù)統(tǒng)計(jì)每條規(guī)則對(duì)應(yīng)的IP在近30天內(nèi)還有沒(méi)有新的失敗事件沒(méi)有就自動(dòng)刪除規(guī)則。這就是前面提的“臨時(shí)解封”邏輯做成獨(dú)立腳本按周運(yùn)行保持規(guī)則庫(kù)精簡(jiǎn)。第四個(gè)方向是集成SIEM或日志系統(tǒng)。如果公司已經(jīng)有日志集中平臺(tái)可以把4625事件實(shí)時(shí)轉(zhuǎn)發(fā)過(guò)去用平臺(tái)自帶的規(guī)則引擎做封禁動(dòng)作腳本就退化成純采集組件。這種架構(gòu)更規(guī)范但需要依賴已有平臺(tái)不是所有環(huán)境都具備所以我自己的腳本仍然保持了獨(dú)立運(yùn)行的形態(tài)。寫在最后腳本只是工具策略才是核心這套腳本跑了很長(zhǎng)時(shí)間累計(jì)封禁了上千個(gè)IP也踩過(guò)各種坑。如果有人問(wèn)我“這個(gè)腳本能保證服務(wù)器不被攻破嗎”我的回答是不一定。它能擋住的是最無(wú)聊、最普遍的字典爆破擋不住的是有人拿著真實(shí)憑據(jù)直接登錄也擋不住新出現(xiàn)的高水平定向攻擊。但它確實(shí)把每天幾千條失敗日志這種噪音消化掉了讓我在翻日志的時(shí)候能一眼看到真正值得關(guān)注的東西。如果你要自己部署這套方案我建議順序是先開(kāi)安全日志調(diào)大小再把手工版腳本跑通最后掛計(jì)劃任務(wù)。不要一上來(lái)就自動(dòng)化先在測(cè)試機(jī)上驗(yàn)證邏輯確認(rèn)不會(huì)誤封、白名單正確再拿到生產(chǎn)環(huán)境。腳本本身是死的策略參數(shù)和安全邊界要靠你根據(jù)實(shí)際場(chǎng)景去調(diào)這個(gè)過(guò)程中的每次誤封和漏封都是對(duì)腳本規(guī)則的校準(zhǔn)。最后還是那句話安全問(wèn)題沒(méi)有一勞永逸的答案腳本可以減少人工投入但替代不了管理員對(duì)系統(tǒng)安全狀態(tài)的理解和判斷。