如果你跟我一樣,看完資安鬼故事開始神經兮兮兼被害妄想,三不五時懷疑「該不會有人從網路偷連我的個人電腦還渾然不知?」,這篇文章分享的小工具應該能幫助你減輕憂慮。

確認有沒有人從網路登入 Windows 的簡單方法是查詢事件檢視器的安全性事件,事件識別碼 4624 是登入成功記錄、4625 是登入失敗,事件詳細資料會包含登入帳號、來源 IP 等資訊,若發現可疑再深入追查:
(提醒:這種檢查方式只能揭露用 Windows 驗證試圖登入主機的行為,像是遠端登入、連上網路磁碟機... 等,但不包含透過木馬、後門程式建立的網路連線)

不過呢,身為大內攻城獅,這種例行檢查還一筆一筆點開來看成何體統?當然要土砲小工具自用才帥氣。

早年我曾寫過 WPF 版,不過 EXE 檔分享轉發較困難,這些年學會新兵器,就來翻寫成 PowerShell 版吧! 使用時只需複製貼上存成 ListLogonEvent.ps1 檔,程式碼直接攤在陽光下(也順便請大家 Code Review),沒有夾帶木馬或病毒的風險。在電腦上開個 【Windows PowerShell (系統管理員)】(如下圖) 或 Cmder (推薦!) 即可執行,想到就健檢一下,有機會提早抓出潛伏的歹咪呀,希望對提升全人類資安能有丁點貢獻。

PowerShell 有個 Get-WinEvent 指令可以查詢 Windows 事件,依實務經驗,將所有事件記錄抓回來用 PowerShell 篩選的效率很差,用 -FilterHashtable-FilterXPath 查詢,FilterHashtable 只支援簡單的比對,XPath 語法較難懂但能實現複雜一點的查詢條件,會是較好的選擇。

Windows Event Log 支援的 XPath 版本只到 1.0,能用的函式少得可憐,別期望它像 SQL WHERE。如果不知從何開始,可以用事件檢視器的篩選工具設好條件,再參考它產生的 XML。參考

我設了幾項條件:

  • EventID = 4624 (登入成功) 或 4625 (登入失敗)
  • 只顯示特定日期之後的記錄,要用 TimeCreated[timediff(@SystemTime) ⇐ 距現在的毫秒數] 這種特殊寫法
  • 由於我要鎖定來自網路的存取,故要排除 EventData 中的 IpAddress 為 -、::1、127.0.0.1 的事件

程式碼很簡單,大約五十行搞定,最後將結果輸出成以 Tab 分隔的 CSV:

Param (
    # 查詢區間之起始時間(預設最近30天)
    [DateTime]$start = (Get-Date).AddDays(-30)
)
$wp = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()
if (-Not $wp.IsInRole([Security.Principal.WindowsBuiltInRole]"Administrator")) {
	Write-Host "*** 請使用系統管理員權限執行 ***" -ForegroundColor Red
	return
}
# 登入類別對照表
$LogonTypeTexts = @(
    'NA','NA',
    'Interactive', #2
    'Network','Batch','Service','NA','Unlock','NetworkClearText',
    'NewCredentials','RemoteInteractive','CachedInteractive'
)
# 計算起始時間距今的毫秒數
$timeDiff = (New-TimeSpan -Start $start -End (Get-Date)).TotalMilliseconds
# 限定 4624(登入成功)、4625(登入失敗)
$xpath = @"
*[  
    System[
        (EventID=4624 or EventID=4625) and
        TimeCreated[timediff(`@SystemTime) <= $timeDiff]
    ] and 
    EventData[
        Data[@Name='IpAddress'] != '-' and
        Data[@Name='IpAddress'] != '::1' and
        Data[@Name='IpAddress'] != '127.0.0.1'
    ]
]
"@
# 加上 SilentlyContinue 防止查無資料時噴錯 No events were found that match the specified selection criteria.
Get-WinEvent -LogName 'Security' -FilterXPath $xpath -ErrorAction SilentlyContinue | ForEach-Object {
    $xml = [xml]$_.ToXml() # 將事件記錄轉成 XML
    $d = @{} # 建立 Hashtable 放 EventData.Data 中的客製屬性
    @($xml.Event.EventData.Data) | ForEach-Object {
        $d[$_.name] = $_.'#text'
    }
    if ($_.ID -eq 4624) { $action = '登入成功'  }
    elseif ($_.ID -eq 4625) { $action = '登入失敗' }
    $logonType = ''
    if ($d.LogonType -gt 1) {
        $logonType = $LogonTypeTexts[$d.LogonType]
    }
    [PSCustomObject]@{
        Action = $action;
        Time = $_.TimeCreated.ToString("yyyy/MM/dd HH:mm:ss");
        Id = $_.ID;
        TargetAccount = "$($d.TargetDomainName)\$($d.TargetUserName)"; # 登入帳號
        Socket = "$($d['IpAddress']):$($d['IpPort'])"; # IP 來源
        LogonType = $logonType; # 登入類別
        LogonProcess = $d.LogonProcessName; # 程序名稱
        AuthPkgName = $d.AuthenticationPackageName; # 驗證模組
    }
} | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation

我模擬了從另一台機器 192.168.50.93 登入整合式驗證 ASP.NET 網站 登入成功、失敗及網路磁碟機失敗的狀況,查詢結果如下:
(提醒:需使用系統管理員權限執行)

純文字不好閱讀,建議用 .\ListLoginEvents.ps1 > D:\Check.csv 存成 CSV 檔再用 Excel 開啟。

22 列紅字是登入 IIS 失敗,21 列綠字是登入 IIS 成功;藍字是試圖連上 \\My-PC\d$ 時 Windows 用當下登入帳號 tony 試圖自動登入失敗的記錄,兩筆紫色是故意打錯帳密產生的失敗記錄。

大家可以用它檢查自己的 Windows,看看是否有人曾試圖登入你的電腦、連上你的網路分享資料夾。

PowerShell example to list logon events on Windows.


Comments

# by Taiyu

版主您好 我有查微軟網頁,可是都是微軟的廣告,遇到沒有數字簽章的時候,要怎麼處理,還請賜教,感謝。 PS V:\__01-00__Task\ListLoginEvents> .\ListLoginEvents.ps1 > Check.csv .\ListLoginEvents.ps1 : File V:\__01-00__Task\ListLoginEvents\ListLoginEvents.ps1 cannot be loaded. The file V:\__01-00 __Task\ListLoginEvents\ListLoginEvents.ps1 is not digitally signed. You cannot run this script on the current system. F or more information about running scripts and setting execution policy, see about_Execution_Policies at https:/go.micro soft.com/fwlink/?LinkID=135170. At line:1 char:1 + .\ListLoginEvents.ps1 > Check.csv + ~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : SecurityError: (:) [], PSSecurityException + FullyQualifiedErrorId : UnauthorizedAccess

# by CMH

回樓上 遇到執行ps1時數位簽章錯誤時可以用以下指令更改Excecution Policy Set-ExecutionPolicy Unrestricted 或是用 powershell.exe 執行時指定 Execution Policy powershell.exe -executionpolicy bypass -file .\Script.ps1

# by

版主你好, 有没有办法使用检查一下公司发的笔记本被IT部门安装了什么样的监控软件吗?

# by Jeffrey

to 翔,假設公司安裝的是名門正派的監控軟體(沒用後門、木馬的技巧讓自己隱形,若是如此已屬駭客層次的議題了),最基本的檢查方式是看Windows的應用程式的安裝清單(控制台用來移除軟體的那個介面),但有些軟體因部署方式不同不一定會出現在清單中,因此更徹底的檢查方式是開工作管理員逐一盤點執行中的程序,看有沒有可疑項目。

# by 小陳

這妄想症我有時也有,不過我是假設是否不小心裝到一些間諜程式來偷資料。 我是用 ProcessTCPSummary by nirsoft 來看的。 主要是觀察是否有哪個process偷連上網。 ProcessTCPSummary - Show the number of TCP connections for every Windows process

# by IG

WFH的現在看到這篇真棒,想請問黑暗大及各位先進,IT管理方面,是否可以用這方式透過AD來查詢每台電腦遠端登入紀錄呢,因為上司要求稽核查員工是否有在上班時間正常登入事件... 在思考該怎麼運用比較好 ><

# by Jeffrey

to IG, 只要具備權限,Get-WinEvent 加 -ComputerName 即可查詢遠端主機,剩下就是從事件中挑選合適的項目以判斷打卡時間。

# by IG

謝謝 Jeffrey 的教學,我來嘗試看看,感謝~

# by BEN

感謝分享! 是否可以加入以下兩個EVENT ID,這兩個通常也是常常需要關心的事件。 4720 已建立使用者帳戶 1102 稽核記錄已清除

# by Jeffrey

to BEN, 視需求調整 $xpath 中的 (EventID=4624 or EventID=4625) 條件即可。

# by kyoiron

LogonType沒有 6,是不是要列個NA?

# by Jeffrey

to kyoiron, 謝謝提醒,還真沒發現不存在 LogType 6,已更正。 https://docs.microsoft.com/zh-tw/windows/security/threat-protection/auditing/event-4624#%E7%99%BB%E5%85%A5%E9%A1%9E%E5%9E%8B%E5%92%8C%E6%8F%8F%E8%BF%B0

# by david

您好,執行另存下來的指令後卻沒有反應 .\ListLoginEvents.ps1 > D:\Check.csv 將這一行指令貼在cmd也不能執行,請問可能大概問題點是什麼呢

# by Jeffrey

to david, 不能執行的錯誤訊息為何?如果要在 cmd 執行,要寫成 powershell .\ListLoginEvents.ps1 (記得要以管理者執行)

# by 查理斯王子

大大您好 請問輸出成csv可以寫在ListLoginEvents.ps1裡面嗎 我有查到powershell裡有個指令out-file是可以輸出成檔案,我試著寫進ListLoginEvents.ps1裡,就是在您的程式碼最後加上|out-file "$date.csv",檔案是有跑出來,但內容是空的,不知道該怎麼改才是對的呢 對了,因為我希望檔名加上今天的日期,所以有加一行$date=Get-Date -Format ‘yyyyMMdd’

# by Jeffrey

to 查理斯王子,我實測 .\ListLoginEvents.ps1 | Out-File "$(Get-Date -Format yyyyMMdd).csv" 是 OK 的。拿掉 | Out-File 有顯示在螢幕嗎? 若有,Out-File 先給成絕對路徑固定檔名試試。

# by 查理斯王子

感謝大大 後來發現,原來我在poweshell ISE裡面執行的話,就只會匯出一個空的EXCEL檔,必須是在powershell的命令列底下執行才行。我後來也換成您的寫法比較簡單。 我也順利用批次檔呼叫系統管理員身分的powershell執行這支程式,並放進排程了。 其實小弟之前完全沒用過這個,只是恰巧需要這個功能又剛好看到您這篇文章,真的受益匪淺,再次感謝大大。

# by Jesen

版主您好 感謝您寫了這個PS SCRIPT。我在使用時,在WIN 10上使用,跑不出東西來。但在WINDOWS 2012 R2上,用同一個SCRIPT,卻有資料SHOW出來。檢查了PS的版本: WIN 10 --->5.1 Windows 2012 R2 --->4.0 不知道是否跟PS的版本有關 ?

# by Jesen

版主您好 感謝您寫了這個PS SCRIPT。我在使用時,在WIN 10上使用,跑不出東西來。但在WINDOWS 2012 R2上,用同一個SCRIPT,卻有資料SHOW出來。檢查了PS的版本: WIN 10 --->5.1 Windows 2012 R2 --->4.0 不知道是否跟PS的版本有關 ?

# by Jeffrey

to Jesen, 我自己在 Win10 / PS5.1 跑是 OK 的,所以是事件檢視器有看到 4624/4625 事件,但 PS 沒出錯也沒資料?

# by Brian

版主你好: 我也遇到跟Jesen一樣的狀況,WIN10有執行但沒有資料。事件檢視器有4624的事件,但是沒出現錯誤也沒有資料,不知道是哪邊有問題?

# by Jeffrey

to Brian, Jesen,,會是因為沒用系統管理員身分執行的關係?(已在程式加入權限檢查與提示)

# by Ted

大大你這個寫得太好,爬statoverflow或者讓chagpt寫都有Bug,你的腳本有登入IP就算對方使用群組登入隱藏也可以用nslookup精準反查,太謝謝了哥

# by CHH

這個真的恰到好處,目前使用上碰到的狀況 : windows 10 的 Powershell version 是 5.1.19041.2673 可以正常執行 windows 11 的 Powershell version 是 5.1.22621.963 空白沒動作

# by Jacky

您好~想問一下我執行好像只有當天資料,請問可以修改看到30天以內的資料嗎

# by Jeffrey

to Jacky, 預設就是抓最近 30 天的資料,你用事件檢視器查安全事件的日期範圍,看看是否因為資料過多(保存資料量有上限,過多時舊資料會被覆寫)舊資料被蓋掉。

# by Jay

你好, 我試著加入-ComputerName $Server -Credential $Cred 來取得遠端server的event log, 但是Run起來都是只抓到localhost 的event log ? Param ( # 查詢區間之起始時間(預設最近30天) [DateTime]$start = (Get-Date).AddDays(-1) ) # $wp = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() # if (-Not $wp.IsInRole([Security.Principal.WindowsBuiltInRole]"Administrator")) { # Write-Host "*** 請使用系統管理員權限執行 ***" -ForegroundColor Red # return # } #config $Config = @{ Username = "user" Password = "password" HostName = "server01" } # 登入類別對照表 $LogonTypeTexts = @( 'System', 'Interactive', 'Network' 'Batch', 'Service', 'Unlock' 'NetworkCleartext', 'NewCredentials', 'RemoteInteractive', 'CachedInteractive', 'CachedRemoteInteractive', 'CachedUnlock' ) # 計算起始時間距今的毫秒數 $timeDiff = (New-TimeSpan -Start $start -End (Get-Date)).TotalMilliseconds # 限定 4624(登入成功)、4625(登入失敗) $xpath = @" *[ System[ (EventID=4624 or EventID=4625) and TimeCreated[timediff(`@SystemTime) <= $timeDiff] ] and EventData[ Data[@Name='IpAddress'] != '-' and Data[@Name='IpAddress'] != '::1' and Data[@Name='IpAddress'] != '127.0.0.1' and Data[@Name='TargetUserName'] = 'system' ] ] "@ $computerName = $Config['HostName'] -split (',') $password = ConvertTo-SecureString -AsPlainText $Config['Password'] -Force $Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Config['Username'], $password # 加上 SilentlyContinue 防止查無資料時噴錯 No events were found that match the specified selection criteria. $numbers = @() foreach ($Server in $computerName) { $numbers += Get-WinEvent -LogName 'Security' -ComputerName $Server -Credential $Cred -FilterXPath $xpath -ErrorAction SilentlyContinue | ForEach-Object { $xml = [xml]$_.ToXml() # 將事件記錄轉成 XML $d = @{} # 建立 Hashtable 放 EventData.Data 中的客製屬性 @($xml.Event.EventData.Data) | ForEach-Object { $d[$_.name] = $_.'#text' Write-Host $_.name Write-Host $_.'#text' } if ($_.ID -eq 4624) { $action = '登入成功' } elseif ($_.ID -eq 4625) { $action = '登入失敗' } $logonType = '' if ($d.LogonType -gt 1) { $logonType = $LogonTypeTexts[$d.LogonType] } [PSCustomObject]@{ Action = $action; Time = $_.TimeCreated.ToString("yyyy/MM/dd HH:mm:ss"); Id = $_.ID; TargetAccount = "$($d.TargetDomainName)\$($d.TargetUserName)"; # 登入帳號 Socket = "$($d['IpAddress']):$($d['IpPort'])"; # IP 來源 LogonType = $logonType; # 登入類別 LogonProcess = $d.LogonProcessName; # 程序名稱 AuthPkgName = $d.AuthenticationPackageName; # 驗證模組 WorkstationName = $d.WorkstationName } } } $numbers | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation

# by Jeffrey

to Jay,單獨跑 Get-WinEvent -LogName 'Security' -ComputerName ... 的結果為何?理論上要嘛成功,要嘛不通,傳回本機事件有點詭異。

# by Ronald

版主您好: 感謝您寫了這篇,但我在PowerShell執行ListLogonEvent.ps1後Action的部分會顯示亂碼,不會顯示登入成功或登入失敗,是哪個部分需要調整呢?

# by Jeffrey

to Ronald,ps1 若有寫中文,要存成 UTF8 with BOM 編碼,這是剛上手常踩到的坑之一,等列入 SOP 後才會避免。參考:https://blog.darkthread.net/blog/ps1-encoding-issue/

# by Pokai

版主好, 有參考您的程式碼, 4625改成4634, 但使用抓不到登出的值, 請問是漏了那段呢? 感謝

# by Jeffrey

to Pokai, 使用事件檢視器有看到 4634 事件嗎?

# by Roy

為什麼我用這個方法, 只生成了沒有任務內容的0kb csv檔 0____0 大大有頭緒嗎? 已經是用系統管理員權限的powershell了

Post a comment