Home > front end >  Regex Pattern in Powershell doesn't work as expected
Regex Pattern in Powershell doesn't work as expected

Time:09-21

I have a string in Powershell which contains the following data

  An account failed to log on.

Subject:
    Security ID:        S-1-5-18
    Account Name:       TEX
    Account Domain:     TD2
    Logon ID:       0x3E7

Logon Type:         8

Account For Which Logon Failed:
    Security ID:        S-1-0-0
    Account Name:       test_mathysf
    Account Domain:     tdlz2

Failure Information:
    Failure Reason:     Unknown user name or bad password.
    Status:         0xC000006D
    Sub Status:     0xC000006A

Process Information:
    Caller Process ID:  0x4f80
    Caller Process Name:    C:\Windows\System32\inetsrv\w3wp.exe

Network Information:
    Workstation Name:   T22
    Source Network Address: 192.168.10.28
    Source Port:        45221

Detailed Authentication Information:
    Logon Process:      Advapi  
    Authentication Package: Negotiate
    Transited Services: -
    Package Name (NTLM only):   -
    Key Length:     0

This event is generated when a logon request fails. It is generated on the computer where access was attempted.

The Subject fields indicate the account on the local system which requested the logon. This is most commonly a service such as the Ser
ver service, or a local process such as Winlogon.exe or Services.exe.

The Logon Type field indicates the kind of logon that was requested. The most common types are 2 (interactive) and 3 (network).

The Process Information fields indicate which account and process on the system requested the logon.

The Network Information fields indicate where a remote logon request originated. Workstation name is not always available and may be l
eft blank in some cases.

The authentication information fields provide detailed information about this specific logon request.
    - Transited services indicate which intermediate services have participated in this logon request.
    - Package name indicates which sub-protocol was used among the NTLM protocols.
    - Key length indicates the length of the generated session key. This will be 0 if no session key was requested.

I wanted to escape everything between Subject and Key Lenght with the following regex Pattern.

$pattern = "(?<=.*Subject:)\w ?(?=Length:*)"

The result of the regex should be like that

Subject:
    Security ID:        S-1-5-18
    Account Name:       TEX
    Account Domain:     TD2
    Logon ID:       0x3E7

Logon Type:         8

Account For Which Logon Failed:
    Security ID:        S-1-0-0
    Account Name:       test_mathysf
    Account Domain:     tdlz2

Failure Information:
    Failure Reason:     Unknown user name or bad password.
    Status:         0xC000006D
    Sub Status:     0xC000006A

Process Information:
    Caller Process ID:  0x4f80
    Caller Process Name:    C:\Windows\System32\inetsrv\w3wp.exe

Network Information:
    Workstation Name:   T22
    Source Network Address: 192.168.10.28
    Source Port:        45221

Detailed Authentication Information:
    Logon Process:      Advapi  
    Authentication Package: Negotiate
    Transited Services: -
    Package Name (NTLM only):   -
    Key Length:     0

But in my project, the regex doesn't work. (it doesn't get a match)

After that I would use the ConvertFrom-StringData cmdlet to create a hash table with the entries (For examle Security ID --> Key = S-1-5-18)

Have somebody an idea where the problem could be?

CodePudding user response:

You seem to want to extract the structured information from the event text.

Here is what I would do.

function ExtractEventData {
    param(
        [string]$EventText
    )
    $pattern = [regex]"(?m)^(.*):\s*(^    \S.*:.*\n) "
    $result = @{}

    foreach ($match in $pattern.Matches($EventText)) {
        $section_name = $match.Groups[1].Value
        $result[$section_name] = @{}
        foreach ($line in $match.Groups[2].Captures) {
            $key, $value = $line.Value.Split(':'.ToCharArray(), 2)
            $result[$section_name][$key.Trim()] = $value.Trim()
        }
        $result[$section_name] = [pscustomobject]$result[$section_name]
    }
    [pscustomobject]$result
}

When you call it like this with your sample string above

$result = ExtractEventData -EventText $sampleEvent

it produces this data structure:

Process Information                 : @{Caller Process ID=0x4f80; Caller Process Name=C:\Windows\System32\inetsrv\w3wp.exe}
Detailed Authentication Information : @{Key Length=0; Logon Process=Advapi; Package Name (NTLM only)=-; Authentication Package=Negotiate; Transited Services=-}
Subject                             : @{Account Domain=TD2; Security ID=S-1-5-18; Account Name=TEX; Logon ID=0x3E7}
Account For Which Logon Failed      : @{Account Domain=tdlz2; Security ID=S-1-0-0; Account Name=test_mathysf}
Failure Information                 : @{Status=0xC000006D; Failure Reason=Unknown user name or bad password.; Sub Status=0xC000006A}
Network Information                 : @{Workstation Name=T22; Source Network Address=192.168.10.28; Source Port=45221}

Which you can access directly, e.g.

$result.'Network Information'.'Workstation Name'   # => T22

The regex is

(?m)                 # multiline mode
^                    # start of line
(.*):\s*             # anything (e.g. 'Network Information' into group 1), a colon, whitespace
(                    # group 2 (e.g. '    Workstation Name:   T22')
  ^    \S.*:.*\n     #   start of line, 4 spaces, a non-space, a colon, anything, newline
)                    # end group 2, repeat

This way, each section gets handled individually, and within each section, each line.

CodePudding user response:

This is the Message property you got from Get-WinEvent, which is a multiline string with LOCALIZED properties.

Instead of converting that string into an array of objects, better use the event XML notation where you can get universally named properties.

Something like this:

$filter = @{LogName='Security';ProviderName='Microsoft-Windows-Security-Auditing';ID=4625 }
$result = Get-WinEvent -FilterHashtable $filter -ComputerName SECRETSERVER | ForEach-Object {
    # convert the event to XML and grab the Event node
    $eventXml = ([xml]$_.ToXml()).Event
    # output the properties you need in your output
    [PSCustomObject]@{
        Time     = [DateTime]$eventXml.System.TimeCreated.SystemTime
        UserName = ($eventXml.EventData.Data | Where-Object { $_.Name -eq 'TargetUserName' }).'#text'
        UserSID  = ($eventXml.EventData.Data | Where-Object { $_.Name -eq 'TargetUserSid' }).'#text'
        Computer = ($eventXml.EventData.Data | Where-Object { $_.Name -eq 'WorkstationName' }).'#text'
    }
}

# output on screen
$result | Format-Table -AutoSize

# output to CSV file
$result | Export-Csv -Path 'X:\FailedLogons.csv' -NoTypeInformation

Here you can see all properties the XML would contain for event 4625. The example above just lists the time the logon failure happened, the computer on which that attempt was made and the user name that failed to log in.

CodePudding user response:

You can use

(?ms)^Subject:.*?Length:(?-s).*

See the regex demo.

Regex details:

  • (?ms) - inline modifiers, m makes ^ match start of any line and $ match any line end position, and s makes . match any chars including LF chars
  • ^ - start of a line
  • Subject: - a literal text
  • .*? - any zero or more chars as few as possible
  • Length: - a literal text
  • (?-s) - now, . cannot match LF chars any longer
  • .* - any zero or more chars other than a newline (LF) char, as many as possible.
  • Related