2

I have a log file Input.log which records failed and successful login attempts made by different users and it keeps updating in real time. I am interested only in failed login attempt made by one user i.e. master. Whenever there is a failed login attempt by user master, following 3 fixed text strings will always come in 3 consecutive lines as shown below in sample Input.log file:

Authenticating the user "master" with the password
Failed to logon to the system due to something unexpected
SSCMPE-00102: Failed to authenticate user. Invalid credentials. Enter valid credentials

Input.log file sample for a failed login attempt by master:

[2021-05-14T04:18:41.378-06:00] [FoundationServices0] [NOTIFICATION] [01216] [oracle.bi.bifndnepm.bpmui.logon.CSSAuthenticate] [tid: 30] [userId: <anonymous>] [ecid: 00j8DrPuyNGB1FwDwFj8CW0001hC0004FL,0:1] [APP: WORKSPACE#11.1.2.0] [SRC_CLASS: com.hyperion.bpm.logon.CSSAuthenticate] [SRC_METHOD: authenticateUser:473] Authenticating the user "master" with the password "*********".
[2021-05-14T04:18:41.573-06:00] [FoundationServices0] [ERROR] [02601] [oracle.bi.bifndnepm.bpmui.logon.LogonServlet] [tid: 30] [userId: <anonymous>] [ecid: 00j8DrPuyNGB1FwDwFj8CW0001hC0004FL,0:1] [APP: WORKSPACE#11.1.2.0] [SRC_CLASS: com.hyperion.bpm.logon.LogonServlet] [SRC_METHOD: writeLogonCssException:206] Failed to logon to the system due to something unexpected.[[
SSCMPE-00102: Failed to authenticate user. Invalid credentials. Enter valid credentials.
    at com.hyperion.css.store.identity.IdentityStoreImpl.authenticate(IdentityStoreImpl.java:1845)
    at com.hyperion.css.spi.impl.nvdb.NativeProvider.authenticate(NativeProvider.java:74)
    at com.hyperion.css.facade.impl.CSSAbstractAuthenticator.authenticateUser(CSSAbstractAuthenticator.java:645)
    at com.hyperion.css.facade.impl.CSSAPIAuthenticationImpl.authenticate(CSSAPIAuthenticationImpl.java:69)

I want to create a monitoring script sothat as soon as we have these 3 text strings appeared in 3 consecutive lines, I should get an email alert about the failed login attempt made by user master.

I will schedule the script to run in Windows task scheduler. I'd like to make the script run continuously to detect the failed login attempts in real time. So it should read only freshly written entries in Input.log file from the previous run of the script.

So far I have below code that, in failed.log, gives me all the lines matching above three strings coming consecutively in three lines (what I actually want) but also many other unwanted lines matching the three strings individually in different lines (which I don't want).

$File = "C:\data\Input.log"

$EmailParam=@{
    To='usergroup@domain.com'
    From='user@domain.com'
    SmtpServer='smtp.serveraddress.com'
    Subject='Failed Login Attempt by the user Master'
    Body='Alert! Failed Login Attempt found for the user Master'
    Attachment='failed.log'
}

$String='Authenticating the user "master" with the password','Failed to logon to the system due to something unexpected','SSCMPE-00102: Failed to authenticate user. Invalid credentials. Enter valid credentials'

Get-Content $File | Select-string -Pattern $String | Set-Content failed.log | ForEach {
    Send-MailMessage @EmailParam
}

Would appreciate if you could guide me to fix it. Thanks!

Squashman
  • 13,649
  • 5
  • 27
  • 36
Aarie
  • 39
  • 7
  • your lines are always consecutives? – Frenchy May 21 '21 at 17:58
  • @Frenchy...yes, for a failed login attempt by user master, those 3 lines consisting those three strings are always consecutive. – Aarie May 21 '21 at 18:03
  • Aside from your immediate issue, this will cause problems if the script finishes executing exactly after the 1st or 2nd failed attempt are logged: “it should read only freshly written entries in Input.log file from the previous run of the script.” Because the 3rd failed attempt will look like it’s only the 1st one when the script runs again. You’ll need to reread at least the last two lines to make sure you trigger your alert properly... – mclayton May 21 '21 at 18:28
  • But then how do you know they’re not failed attempts 2 and 3 from a *different* sequence of failures? You’re going to need to keep some sort of state between runs that you can rehydrate to pick up the counting where the previous run left off... – mclayton May 21 '21 at 18:30
  • hope your log file is not too big – Frenchy May 21 '21 at 18:57
  • @Frenchy...yes, my log file is not big. – Aarie May 22 '21 at 09:18

2 Answers2

1

a possible solution: the regex pattern looks at 3 consecutives lines

$pat1 = 'Authenticating the user "master" with the password'
$pat2 = 'Failed to logon to the system due to something unexpected'
$pat2 = 'SSCMPE-00102: Failed to authenticate user. Invalid credentials. Enter valid credentials'
$pat = $pat1 + ".*?`r?`n.*?" + $pat2 + ".*?`r?`n.*?" + $pat3

$path = "C:\Users\ThierryK\Documents\test.log"
$lines = Get-Content -Raw -Path $path 

$option = [System.Text.RegularExpressions.RegexOptions]::Singleline 

$pattern = [regex]::new($pat, $option)

$matches = $pattern.Matches($lines)


foreach($m in $matches){
   $m.Groups[0].Value
   #send your email
}

i suggest you to keep the time each time you scan the log to avoid to scan the same thing every time

Frenchy
  • 16,386
  • 3
  • 16
  • 39
  • @Frenchy....Thanks..It extracts out the desired lines from the log file. For 'New Log entry status/counting factor', I have used mklement0 method mentioned in one of the answers. – Aarie May 22 '21 at 09:23
1
$matchingLineCount = 0
$matchingLines = @()

 Get-Content -Wait $File | ForEach-Object {
  if ($matchingLineCount -eq 2) { # 3rd line -> send email.
    $matchingLines += $_
    $matchingLines | Set-Content failed.log
    Send-MailMessage @EmailParam
  }
  elseif ($matchingLineCount -eq 0 -and $_ -match 'Authenticating the user "master"') {
    $matchingLines += $_
    ++$matchingLineCount
    return
  } elseif ($matchingLineCount -eq 1 -and $_ -match 'Failed to logon to the system due to something unexpected') {
    $matchingLines += $_
    ++$matchingLineCount
    return
  }
  $matchingLineCount = 0; $matchingLines = @()
}

Note:

  • Given that only 3 lines must be collected, I'm defining $matchingLines as a regular array (@()) and using += to "append" lines to it, for brevity and convenience.
  • However, in general this approach to iteratively building a collection is inefficient, because every += causes a new array to be created behind the scenes - of necessity, given that arrays are immutable with respect to their element count - see this answer for more information.
    • GitHub issue #5643 discusses switching PowerShell's default collection data structure from non-extensible arrays to an extensible collection type.
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • @mklement0...this exactly works the way I wanted. Thanks. One query...I have variable `$Body += (Get-Content $OutputFile) -join '
    '` defined in my EmailParam section. But as the $OutputFile is written/updated after the variable $Body is defined in EmailParam section at the top, $OutputFile content in not added in Email body, although $OutputFile with updated content is getting attached in the email. Any workaround?
    – Aarie May 22 '21 at 09:14
  • 1
    @Aarie, glad to hear it. As for the `$Body` problem: From that description can't tell what the problem is, but since comments aren't the right place for follow-up questions, please create a _new_ question post focused on that problem - feel free to notify me here once you have done so. As for the question at hand: Allow me to give you the standard advice to newcomers in the next comment: – mklement0 May 22 '21 at 12:46