Windows 自動封鎖 IP
自架的對外伺服器常被陌生 IP try 帳密,導致事件檢視器常常卡一堆登入失敗的紀錄而影響判讀,於是決定寫隻簡單的程式來監聽 Event Log 並即時將登入失敗的 IP 加到封鎖清單中。原本以為網路上查查資料,複製貼上改一下應該一小時左右就能搞定,結果各種撞牆卡了我一整天才完成,特此紀錄一下相關注意事項!

 
  
Nuget
| <PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="6.0.0" /> | 
Step
1. 讀取 EventLog/Security 取得 EventId=4625 的相關資訊
- 讀取需使用 - 系統管理員權限
- 範例: - var entries = 
 from EventLogEntry e in eventLog.Entries
 where e.InstanceId == 4625
 select new
 {
 e.ReplacementStrings,
 };
 if (entries.Count() > 0)
 {
 var events = entries.ToList();
 var ips = new Dictionary<string, int>();
 foreach (var d in events)
 {
 if (d.ReplacementStrings.Length >= 19)
 {
 var targetUserName = d.ReplacementStrings[5];
 var ip = d.ReplacementStrings[19];
 }
 }
- ReplacementStrings資料結構參考:- <Data Name="SubjectUserSid">S-1-0-0</Data> 
 <Data Name="SubjectUserName">-</Data>
 <Data Name="SubjectDomainName">-</Data>
 <Data Name="SubjectLogonId">0x0</Data>
 <Data Name="TargetUserSid">S-1-0-0</Data>
 <Data Name="TargetUserName">ѓ®бвм</Data>
 <Data Name="TargetDomainName" />
 <Data Name="Status">0xc000006d</Data>
 <Data Name="FailureReason">%%2313</Data>
 <Data Name="SubStatus">0xc0000064</Data>
 <Data Name="LogonType">3</Data>
 <Data Name="LogonProcessName">NtLmSsp</Data>
 <Data Name="AuthenticationPackageName">NTLM</Data>
 <Data Name="WorkstationName">-</Data>
 <Data Name="TransmittedServices">-</Data>
 <Data Name="LmPackageName">-</Data>
 <Data Name="KeyLength">0</Data>
 <Data Name="ProcessId">0x0</Data>
 <Data Name="ProcessName">-</Data>
 <Data Name="IpAddress">83.69.141.105</Data>
 <Data Name="IpPort">5223</Data>
2. 從防火牆規則取得已封鎖 IP 列表
- 使用 Powershell 語法 - Get-NetFirewallRule取得已封鎖 IP 列表
- 參考 Nuget 套件 - System.Management.Automation
- 需再參考 Nuget 套件 - Microsoft.PowerShell.SDK,不然可能會出現以下錯誤:
 “Cannot load PowerShell snap-in Microsoft.PowerShell.Diagnostics because of the following error: Could not load file or assembly”
- 需注意執行環境的 Powershell 執行原則 是否低於 - RemoteSigned,不然可能會出現以下錯誤:
 Cannot be loaded because running scripts is disabled on this system
- 範例: - using (var ps = PowerShell.Create()) 
 {
 // 設定執行原則
 ps.AddScript("Set-ExecutionPolicy RemoteSigned");
 // 從防火牆規則 firewallRuleName 取出當前已封鎖 IP 列表
 ps.AddScript("Import-Module NetSecurity");
 ps.AddScript($"[string[]](Get-NetFirewallRule -DisplayName '{firewallRuleName}' | Get-NetFirewallAddressFilter).RemoteAddress");
 // 以上語法有特別轉型成 String[],故可用 Invoke<string>() 直接取得
 foreach (string ip in ps.Invoke<string>())
 {
 blockedIps.Add(ip);
 }
 // Powershell 執行異常時不會拋錯,需用以下語法取出
 PSDataCollection<ErrorRecord> errors = ps.Streams.Error;
 if (errors != null && errors.Count > 0)
 {
 foreach (ErrorRecord err in errors)
 {
 Write2EventLog($"Error: {err}", EventLogEntryType.Error);
 }
 }
 }
3. 整併 IP 列表並寫入防火牆規則
- 使用 Powershell 語法 - Set-NetFirewallRule寫回阻擋規則
- 使用 - @("111.222.333.444", "111.222.333.555")格式產生 Powershell 的 String [] 參數
- 範例: - using (var ps = PowerShell.Create()) 
 {
 var sb = new StringBuilder();
 sb.Append("\"");
 sb.Append(string.Join("\",\"", ips));
 sb.Append("\"");
 ps.AddScript("Set-ExecutionPolicy RemoteSigned");
 ps.AddScript("Import-Module NetSecurity");
 ps.AddScript($"Set-NetFirewallRule -DisplayName '{firewallRuleName}' -Direction Inbound -Action Block -RemoteAddress @({sb})");
 ps.Invoke();
 }
4. 透過 4625 事件觸發
- 可直接從 - EventLog/Security事件附加工作排程![從 4625 事件附加工作排程]() 
 ![觸發程序]() 
Example
https://github.com/KuoAnn/AutoBlockIp
結果
放了一個晚上就擋了 14 組 IP,網路的世界真是險惡…
 
  
自啟用排程後有效降低被攻擊的次數

踩雷
- 因套件使用到 Powershell 腳本,須注意不能使用單一檔案發布,不然會出現以下錯誤:
The type initializer for ‘System.Management.Automation.ExperimentalFeature’ threw an exception.
- 發布時需用可攜式,指定 RID 可能會抓不到特定版本的 dll
Could not load file or assembly ‘Microsoft.Management.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35’. 系統找不到指定的檔案。
Ref. https://stackoverflow.com/questions/56352672/running-powershell-from-net-core-could-not-load-file-or-assembly-microsoft-ma

