반응형

이전에 ADFS Audit Log에 대해서 다룬 적이 있습니다.

2020.09.06 - [Windows Server] - ADFS Events and Logging

 

ADFS 주요 로그인 이벤트는 1200~1207입니다. 아래의 항목들은 감사로그가 설정되면 확인할 있습니다.

Event ID

이벤트 로그 상에서는 Security 항목으로 다음과 같이 기록됩니다.

Security log

상세로그는 XML 형식으로 되어 있습니다.

Event Properties

이번 글에서는 각각의 XML 의 속성 값들을 CSV로 내보내는 Script 제작에 대해서 다뤄보겠습니다.

우선 아래의 글에 작성된 스크립트를 참고하였습니다.

Report basic authentication Signins in ADFS | Exchange, Sharepoint, Office 365 (wordpress.com)

 

Sample Script 다음과 같습니다. (Export-CSV 부분을 조금 수정하였습니다.)

$evts = Get-WinEvent -FilterHashtable @{Logname='security';Id=1200}
 
$report = @()
$evts |% {
    $activityid = $_.properties.value[0]
    [XML]$XML = $_.properties.value[1]
 
$myObject = [PSCustomObject]@{
    'Eventid' = $_.id
    'AuditResult' = $xml.auditbase.AuditResult
    'FailureType' =$xml.auditbase.FailureType
    'ErrorCode' = $xml.auditbase.ErrorCode
    'timecreated' = $_.timecreated
    'ActivityId' = $activityID
    'User' = $xml.auditbase.contextComponents.component[0].userid
    'RelyingParty' = $xml.auditbase.contextComponents.component[0].RelyingParty
    'IpAddress' = $xml.auditbase.contextComponents.component[3].IpAddress
    'NetworkLocation' = $xml.auditbase.contextComponents.component[3].NetworkLocation
    'MfaMethod' = $xml.auditbase.contextComponents.component[2].MfaMethod
    'AuthProtocol' = $xml.auditbase.contextComponents.component[3].AuthProtocol
    'UserAgentString' = $xml.auditbase.contextComponents.component[3].UserAgentString
    'EndPoint' = $xml.auditbase.contextComponents.component[3].endpoint
}
$report += $myObject
}
 
$date = get-date -Format ("yyyy-MM-dd-")
$path = ".\" + $date  + $env:computername + ".log"
 
$report |sort timecreated  -Descending:$false |export-csv -path $path -notypeinformation -Encoding UTF8

Sample Script

아래의 명령어는 LogName Security 이고 Id 1200 이벤트 로그를 추출합니다.

Get-WinEvent -FilterHashtable @{Logname='security';Id=1200}

Get-WinEvent

스크립트에서 가장 중요한 [XML]$XML 입니다. ChatGPT에게 확인해 보았습니다.

From ChatGPT
In PowerShell, [XML]$XML is used to cast or convert a string or variable into an XML object. The syntax [XML] is a type accelerator, which is a shorthand way to specify a type in PowerShell, in this case, the System.Xml.XmlDocument type. By casting a string or variable to XML, you can easily navigate and manipulate the XML data using PowerShell's built-in XML features.

PowerShell에서 [XML]$XML은 문자열이나 변수를 XML 개체로 캐스팅하거나 변환하는 데 사용됩니다. [XML] 구문은 PowerShell에서 형식(이 경우 System.Xml.XmlDocument 형식)을 지정하는 속기 방법인 형식 가속기입니다. 문자열이나 변수를 XML로 캐스팅하면 PowerShell의 기본 제공 XML 기능을 사용하여 XML 데이터를 쉽게 탐색하고 조작할 수 있습니다.

-> XML Data에서 각각의 속성 값을 추출할 활용할 있습니다.

 

아래는 ChatGPT 생성해준 XML Script 예시입니다.

# Define a string containing XML data
$xmlData = @"
<root>
  <child name="John">
    <age>30</age>
  </child>
  <child name="Jane">
    <age>25</age>
  </child>
</root>
"@

# Cast the string to an XML object
[XML]$XML = $xmlData

# Access and manipulate the XML data
$XML.root.child | Where-Object { $_.age -gt 28 }

스크립트를 실행하면 다음과 같이 결과 값이 확인됩니다.

XML Script 실행 결과

 

단순 결과 값보다는 XML 구조를 이해하는 것이 중요합니다.

상단의 Raw Data XML Data 구조를 정의합니다.

XML Data 정의

 

$XML 입력시 최상위 Root 라는 속성을 조회 합니다.

XML 계층구조

 

$XML.Root 같이 입력하면 다음 계층의 속성 값을 조회할 있습니다. 같은 방식으로 하위 속성을 조회할 있는 구조 입니다.

Xml 속성 조회

 

 

아래는 Event ID 1200 로그인 로그 샘플입니다.

Sample Event ID 1200

 

Sample Script에서 아래의 부분이 이벤트로그 상에서 어떠한 속성에 해당하는지 확인해 보겠습니다.

우선 Sample Script 실행하면 다음과 같이 CSV 결과를 확인할 있습니다.

Export-CSV

 

Excel상에서 항목별로 정리된 것을 확인할 있습니다.

Log 항목

 

다만 실제 Event 상의 모든 항목을 기록하지는 않습니다.

이벤트 로그의 항목이 스크립트 상에서 어느 부분에 해당하는지 확인해 보겠습니다.

 

스크립트를 실행한 , $XML 확인해보면 속성은 xml AuditBase 확인됩니다.

$XML

 

아래와 같이 최상위 계층의 속성은 xml, AuditBase 라는 것은 확인할 있습니다. Xml 단일 계층이기 때문에 AuditBase 확인합니다.

최상위 계층

 

$xml.AuditBase

$xml.AuditBase

 

2번째 계층은 다음과 같이 확인됩니다.

2번째 계층

 

스크립트 상에서 아래의 영역에 해당합니다.

$xml.auditbase

 

AuditType 속성은 스크립트 상에서는 없는 것을 확인할 있습니다.

해당 속성을 넣고 싶으면 다음과 같이 추가할 있습니다.

'AuditType' = $xml.AuditBase.AuditType

$xml.auditbase.AuditType

 

대부분의 속성이 위치하고 있는 ContextComponents $XML.AuditBase.ContextComponents 조회해보면, 각각의 4개의 Component 조회됩니다.

$XML.Auditbase.ContextComponents

 

로그 상에서 다음과 같이 구분됩니다.

Component 영역

 

부분은 $xml.auditbase.contextComponents.component[1~4] 구분하여 조회할 있습니다.

각각의 Type으로 구분한 것을 있습니다.

Componen 영역 2

 

스크립트 상에서 아래의 부분에 해당하며, Sample에서는 일부 항목만 포함되었다는 것을 있습니다.

Script 상에서 Component 영

 

Event ID 기준을 1200~1207까지 포함, CSV파일로 추, 모든항목을 포함하도록 다음과 같이 스크립트를 수정하였습니다

$evts = Get-WinEvent -FilterHashtable @{Logname='security';Id=1200,1201,1202,1203,1204,1205,1206,1207}

$report = @()
$evts |% {
    $activityid = $_.properties.value[0]
    [XML]$XML = $_.properties.value[1]
 
$myObject = [PSCustomObject]@{
    'timecreated' = $_.timecreated
    'Eventid' = $_.id
    'ActivityId' = $activityID

    #Auditbase
    'Audittype' = $xml.auditbase.AuditType
    'AuditResult' = $xml.auditbase.AuditResult
    'FailureType' =$xml.auditbase.FailureType
    'ErrorCode' = $xml.auditbase.ErrorCode
    
    #ResourceAuditComponent
    'RelyingParty' = $xml.auditbase.contextComponents.component[0].RelyingParty
    'ClaimsProvider' = $xml.auditbase.contextComponents.component[0].ClaimsProvider
    'User' = $xml.auditbase.contextComponents.component[0].userid
    
    #ResourceAuditComponent
    'PrimaryAuth' = $xml.auditbase.contextComponents.component[1].PrimaryAuth
    'DeviceAuth' = $xml.auditbase.contextComponents.component[1].DeviceId
    'MfaPerformed' = $xml.auditbase.contextComponents.component[1].MfaPerformed
    'MfaMethod' = $xml.auditbase.contextComponents.component[1].MfaMethod
    'TokenBindingProvidedId' = $xml.auditbase.contextComponents.component[1].TokenBindingProvidedId
    'TokenBindingReferredId' = $xml.auditbase.contextComponents.component[1].TokenBindingReferredId
    'SsoBindingValidationLevel' = $xml.auditbase.contextComponents.component[1].SsoBindingValidationLevel

    #ProtocolAuditComponent
    'OAuthClientId' = $xml.auditbase.contextComponents.component[2].OAuthClientId
    'OAuthGrant' = $xml.auditbase.contextComponents.component[2].OAuthGrant

    #RequestAuditComponent
    'Server' = $xml.auditbase.contextComponents.component[3].Server
    'AuthProtocol' = $xml.auditbase.contextComponents.component[3].AuthProtocol
    'NetworkLocation' = $xml.auditbase.contextComponents.component[3].NetworkLocation
    'IpAddress' = $xml.auditbase.contextComponents.component[3].IpAddress
    'ProxyIpAddress' = $xml.auditbase.contextComponents.component[3].ProxyIpAddress
    'NetworkIpAddress' = $xml.auditbase.contextComponents.component[3].NetworkIpAddress
    'ProxyServer' = $xml.auditbase.contextComponents.component[3].ProxyServer
    'UserAgentString' = $xml.auditbase.contextComponents.component[3].UserAgentString
    'EndPoint' = $xml.auditbase.contextComponents.component[3].endpoint
}
$report += $myObject
}
 
$date = get-date -Format ("yyyy-MM-dd-")
$path = ".\" + $date  + $env:computername + ".csv"
 
$report |sort timecreated  -Descending:$false |export-csv -path $path -notypeinformation -Encoding UTF8

아래와 같이 모든 항목이 조회되는 것을 있습니다.

CSV 결과

Timecreated 항목의 경우 한글 텍스트가 들어가며, Excel 상에서 정렬하여 사용하기에는 다소 애매한 부분이 있습니다. 부분을 ChatGPT에게 문의하여 UTC 형태로 변환하는 방법에 대하여 확인하였습니다.

You can convert the timecreated property to UTC format so that it can be easily used in Excel. To do this, you can update the script to format the timecreated property as a standard UTC datetime string. You can use the ToUniversalTime() method to convert the datetime to UTC and the ToString() method with a standard format specifier to represent it as a string. Here's the updated portion of the script:

-> ToUniversalTime() 사용하여 변경할 있습니다.

$report = @()
$evts |% {
    $activityid = $_.properties.value[0]
    [XML]$XML = $_.properties.value[1]

    $utcTimeCreated = $_.timecreated.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")

    $myObject = [PSCustomObject]@{
        'timecreated' = $utcTimeCreated
        # Rest of the properties remain unchanged
    }
    $report += $myObject
}

 

최종 수정본은 다음과 같습니다.

$evts = Get-WinEvent -FilterHashtable @{Logname='security';Id=1200,1201,1202,1203,1204,1205,1206,1207}

$report = @()

$evts |% {
    $activityid = $_.properties.value[0]
    [XML]$XML = $_.properties.value[1]
    $utcTimeCreated = $_.timecreated.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")

$myObject = [PSCustomObject]@{
    'timecreated' = $utcTimeCreated
    'Eventid' = $_.id
    'ActivityId' = $activityID

    #Auditbase
    'Audittype' = $xml.auditbase.AuditType
    'AuditResult' = $xml.auditbase.AuditResult
    'FailureType' =$xml.auditbase.FailureType
    'ErrorCode' = $xml.auditbase.ErrorCode
    
    #ResourceAuditComponent
    'RelyingParty' = $xml.auditbase.contextComponents.component[0].RelyingParty
    'ClaimsProvider' = $xml.auditbase.contextComponents.component[0].ClaimsProvider
    'User' = $xml.auditbase.contextComponents.component[0].userid
    
    #ResourceAuditComponent
    'PrimaryAuth' = $xml.auditbase.contextComponents.component[1].PrimaryAuth
    'DeviceAuth' = $xml.auditbase.contextComponents.component[1].DeviceId
    'MfaPerformed' = $xml.auditbase.contextComponents.component[1].MfaPerformed
    'MfaMethod' = $xml.auditbase.contextComponents.component[1].MfaMethod
    'TokenBindingProvidedId' = $xml.auditbase.contextComponents.component[1].TokenBindingProvidedId
    'TokenBindingReferredId' = $xml.auditbase.contextComponents.component[1].TokenBindingReferredId
    'SsoBindingValidationLevel' = $xml.auditbase.contextComponents.component[1].SsoBindingValidationLevel

    #ProtocolAuditComponent
    'OAuthClientId' = $xml.auditbase.contextComponents.component[2].OAuthClientId
    'OAuthGrant' = $xml.auditbase.contextComponents.component[2].OAuthGrant

    #RequestAuditComponent
    'Server' = $xml.auditbase.contextComponents.component[3].Server
    'AuthProtocol' = $xml.auditbase.contextComponents.component[3].AuthProtocol
    'NetworkLocation' = $xml.auditbase.contextComponents.component[3].NetworkLocation
    'IpAddress' = $xml.auditbase.contextComponents.component[3].IpAddress
    'ProxyIpAddress' = $xml.auditbase.contextComponents.component[3].ProxyIpAddress
    'NetworkIpAddress' = $xml.auditbase.contextComponents.component[3].NetworkIpAddress
    'ProxyServer' = $xml.auditbase.contextComponents.component[3].ProxyServer
    'UserAgentString' = $xml.auditbase.contextComponents.component[3].UserAgentString
    'EndPoint' = $xml.auditbase.contextComponents.component[3].endpoint
}
$report += $myObject
}
 
$date = get-date -Format ("yyyy-MM-dd-")
$path = ".\" + $date  + $env:computername + ".csv"
 
$report |sort timecreated  -Descending:$false |export-csv -path $path -notypeinformation -Encoding UTF8

 

아래와 같이 변경된 것을 확인할 있습니다.

 

아래와 같이 각각의 속성을 확인하며 로그인 기록을 관리할 있습니다.

반응형

+ Recent posts