Hur man hittar misslyckade inloggningsförsök i Windows med hjälp av PowerShell

Att säkerställa IT-systemens säkerhet är en mycket viktig uppgift. Att identifiera misstänkta aktiviteter, t.ex. flera misslyckade inloggningsförsök, är en viktig åtgärd för att minska potentiella hot. Det medföljande skriptet skrivet i PowerShell fungerar som ett mångsidigt verktyg för att hjälpa IT-proffs och tjänsteleverantörer att få insikter om misslyckade inloggningshändelser.

Bakgrund

Att förstå de misslyckade inloggningsförsöken på ett system kan ge viktiga insikter för IT-administratörer. De kan upptäcka eventuella säkerhetsöverträdelser, övervaka användarnas beteende och upprätthålla systemets integritet. Det medföljande PowerShell-skriptet hämtar effektivt dessa data och erbjuder en robust lösning för yrkesverksamma. Vikten av detta verktyg kan inte nog understrykas. Med ökande hot mot cybersäkerheten är det viktigt för tjänsteleverantörer och IT-specialister att ha en effektiv metod för att upptäcka avvikelser i användarinloggningar.

Manus

#Requires -Version 3.0 -RunAsAdministrator

<#
.SYNOPSIS
    Returns the number of recent failed login attempts.
.DESCRIPTION
    Returns the number of recent failed login attempts of all users or of a specific user. If a user is specified then just a number is returned.
.EXAMPLE
    No parameters needed.
    Returns all users, of the local machine, with a could of failed login attempts.
Output Example:
UserName  FailedLoginAttempts
--------  -------------------
Fred                        4
Bob                         0
.EXAMPLE
     -UserName "Fred"
    Returns the number of failed login attempts of the user Fred on the local machine.
Output Example:
4
.EXAMPLE
     -ComputerName "FredPC" -UserName "Fred"
    Returns the number of failed login attempts of the user Fred on the computer named FredPC.
Output Example:
4
.EXAMPLE
     -ComputerName "FredPC" -UserName "Fred" -Detailed
    Returns the number of failed login attempts of the user Fred on the computer named FredPC, but will more details of each failed and successful logins.
Output Example:

TimeGenerated   : 10/18/2019 7:52:43 AM
EventID         : 4624
Category        : 12544
ADUsername      : Fred
Domain          : FredPC
UserSID         : S-1-0-0
Workstation     : -
SourceIP        : -
Port            : -
FailureReason   : Interactive
FailureStatus   : Incorrect password
FailureSubStatus: Other
.EXAMPLE
    PS C:> Monitor-Failed-Password-Attempts.ps1 -ComputerName "FredPC" -UserName "Fred"
    Returns the number of failed login attempts of the user Fred on the computer named FredPC.
Output Example:
4
.OUTPUTS
    System.Int32 Number of failed login attempts.
.OUTPUTS
    PSCustomObject List of user names and a count of failed login attempts.
.NOTES
    Minimum OS Architecture Supported: Windows 7, Windows Server 2012
    If ComputerName is specified, then be sure that the computer that this script is running on has network and permissions to access the Event Log on the remote computer.
    Release Notes:
    Initial Release
By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use.
    Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. 
    Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. 
    Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. 
    Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. 
    Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. 
    Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. 
    EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).
.COMPONENT
    ManageUsers
#>

param (
    # The name of a remote computer to get event logs for failed logins
    [Parameter(Mandatory = $false)]
    [String]
    $ComputerName = [System.Net.Dns]::GetHostName(),
    # A username
    [Parameter(Mandatory = $false)]
    [String]
    $UserName,
    # Returns all relevant events, sorted by TimeGenerated
    [Switch]
    $Detailed
)

# Support functions
# Returns the matching FailureReason like Incorrect password
function Get-FailureReason {
    Param($FailureReason)
    switch ($FailureReason) {
        '0xC0000064' { "Account does not exist"; break; }
        '0xC000006A' { "Incorrect password"; break; }
        '0xC000006D' { "Incorrect username or password"; break; }
        '0xC000006E' { "Account restriction"; break; }
        '0xC000006F' { "Invalid logon hours"; break; }
        '0xC000015B' { "Logon type not granted"; break; }
        '0xc0000070' { "Invalid Workstation"; break; }
        '0xC0000071' { "Password expired"; break; }
        '0xC0000072' { "Account disabled"; break; }
        '0xC0000133' { "Time difference at DC"; break; }
        '0xC0000193' { "Account expired"; break; }
        '0xC0000224' { "Password must change"; break; }
        '0xC0000234' { "Account locked out"; break; }
        '0x0' { "0x0"; break; }
        default { "Other"; break; }
    }
}
function Get-LogonType {
    Param($LogonType)
    switch ($LogonType) {
        '0' { 'Interactive'; break; }
        '2' { 'Interactive'; break; }
        '3' { 'Network'; break; }
        '4' { 'Batch'; break; }
        '5' { 'Service'; break; }
        '6' { 'Proxy'; break; }
        '7' { 'Unlock'; break; }
        '8' { 'Networkcleartext'; break; }
        '9' { 'NewCredentials'; break; }
        '10' { 'RemoteInteractive'; break; }
        '11' { 'CachedInteractive'; break; }
        '12' { 'CachedRemoteInteractive'; break; }
        '13' { 'CachedUnlock'; break; }
        Default {}
    }
}
#-Newest $Records
$Events = Get-EventLog -ComputerName $ComputerName -LogName 'security' -InstanceId 4625, 4624 | Sort-Object -Property TimeGenerated | ForEach-Object {
    if ($_.InstanceId -eq 4625) {
        $_ | Select-Object -Property @(
            @{Label = 'TimeGenerated'; Expression = { $_.TimeGenerated } },
            @{Label = 'EventID'; Expression = { $_.InstanceId } },
            @{Label = 'Category'; Expression = { $_.CategoryNumber } },
            @{Label = 'Username'; Expression = { $_.ReplacementStrings[5] } },
            @{Label = 'Domain'; Expression = { $_.ReplacementStrings[6] } },
            @{Label = 'UserSID'; Expression = { (($_.Message -Split 'rn' | Select-String 'Security ID')[1] -Split 's+')[3] } },
            # @{Label = 'UserSID'; Expression = { $_.ReplacementStrings[0] } },
            @{Label = 'Workstation'; Expression = { $_.ReplacementStrings[13] } },
            @{Label = 'SourceIP'; Expression = { $_.ReplacementStrings[19] } },
            @{Label = 'Port'; Expression = { $_.ReplacementStrings[20] } },
            @{Label = 'LogonType'; Expression = { $_.ReplacementStrings[8] } },
            @{Label = 'FailureStatus'; Expression = { Get-FailureReason($_.ReplacementStrings[7]) } },
            @{Label = 'FailureSubStatus'; Expression = { Get-FailureReason($_.ReplacementStrings[9]) } }
        )
    }
    elseif ($_.InstanceId -eq 4624 -and (Get-LogonType($_.ReplacementStrings[8])) -notlike 'Service') {
        $_ | Select-Object -Property @(
            @{Label = 'TimeGenerated'; Expression = { $_.TimeGenerated } },
            @{Label = 'EventID'; Expression = { $_.InstanceId } },
            @{Label = 'Category'; Expression = { $_.CategoryNumber } },
            @{Label = 'Username'; Expression = { $_.ReplacementStrings[5] } },
            @{Label = 'Domain'; Expression = { $_.ReplacementStrings[6] } },
            @{Label = 'UserSID'; Expression = { $_.ReplacementStrings[0] } },
            @{Label = 'Workstation'; Expression = { $_.ReplacementStrings[11] } },
            @{Label = 'SourceIP'; Expression = { $_.ReplacementStrings[18] } },
            @{Label = 'Port'; Expression = { $_.ReplacementStrings[19] } },
            @{Label = 'LogonType'; Expression = { Get-LogonType($_.ReplacementStrings[8]) } },
            @{Label = 'LogonID'; Expression = { Get-FailureReason($_.ReplacementStrings[7]) } },
            @{Label = 'LogonProcess'; Expression = { Get-FailureReason($_.ReplacementStrings[9]) } }
        )
    }
}

if ($Detailed) {
    if ($UserName) {
        $Events | Where-Object {
            $_.Username -like $UserName
        }
    }
    else {
        $Events | Where-Object {
            $_.Username -notlike "DWM*" -and
            $_.Username -notlike "UMFD*" -and
            $_.Username -notlike "SYSTEM"
        }
    }
}
else {
    $UserNames = if ($UserName) {
        ($Events | Select-Object -Property Username -Unique).Username | Where-Object {
            $_ -like "$UserName"
        }
    }
    else {
        ($Events | Select-Object -Property Username -Unique).Username | Where-Object {
            $_ -notlike "DWM*" -and
            $_ -notlike "UMFD*" -and
            $_ -notlike "SYSTEM"
        }
    }
    
    $UserNames | ForEach-Object {
        $CurrentUserName = $_
        $FailedLoginCount = 0
        for ($i = 0; $i -lt $Events.Count; $i++) {
            if ($Events[$i].EventID -eq 4625 -and $Events[$i].Username -like $CurrentUserName) {
                # User failed to login X times
                # Count the number of failed logins
                $FailedLoginCount++
            }
            elseif ($Events[$i].EventID -eq 4624 -and $Events[$i].Username -like $CurrentUserName) {
                # User logged in successfully
                # Reset the number of failed logins to 0
                $FailedLoginCount = 0
            }
        }
        if ($UserName) {
            # If a UserName was specified, then return only the failed login count
            $FailedLoginCount
        }
        else {
            # If no UserName was specified, then return the user name and failed login count
            [PSCustomObject]@{
                UserName            = $CurrentUserName
                FailedLoginAttempts = $FailedLoginCount
            }
        }
    }
}

 

Få tillgång till över 300 skript i NinjaOne Dojo

Få tillgång till

Detaljerad uppdelning av manuset

Skriptet hämtar data från händelseloggarna på en viss dator och riktar in sig på specifika händelse-ID:n som representerar misslyckade och lyckade inloggningsförsök.

  • Parametrar: Skriptet börjar med att definiera parametrar som ComputerName, UserName och Detailed. Detta gör att användaren kan ange maskin, användare och detaljnivå för inloggningsförsöken.
  • Funktioner: Två funktioner, Get-FailureReason och Get-LogonType, omvandlar kodad information från händelseloggarna till läsbara data om typen av inloggning och orsaken till en misslyckad inloggning.
  • Hämta händelser: Skriptet hämtar sedan händelseloggarna och filtrerar dem för att endast behålla nödvändig information. Detta innebär att du väljer de instanser som har relevanta Event ID.
  • Bearbetning: Beroende på om detaljerade data begärs ger skriptet antingen en omfattande sammanställning av varje inloggningsförsök eller en sammanfattning av misslyckade försök för varje användare.

Potentiella användningsområden

Tänk dig en IT-administratör på ett medelstort företag. IT-avdelningen har nyligen noterat en ökning av antalet misslyckade inloggningsförsök, särskilt under icke arbetstid. Med hjälp av skriptet kan administratören snabbt kontrollera vilka användare som har misslyckats med inloggningsförsöken och hur ofta. När de upptäckte att ett enda användarkonto hade flera misslyckade försök under en kort tidsperiod kunde de dra slutsatsen att detta konto kan ha varit måltavla. På så sätt bidrar skriptet till tidig upptäckt och snabb åtgärd.

Alternativt tillvägagångssätt

Det finns flera metoder för att spåra misslyckade inloggningsförsök. Med Windows inbyggda säkerhetsgranskning kan du t.ex. visa säkerhetsloggarna via Händelsevisaren. Även om denna metod är okomplicerad kan den vara tidskrävande. Vårt PowerShell-skript effektiviserar processen och erbjuder en mer ändamålsenlig och anpassningsbar lösning.

Vanliga frågor

  • Hur identifierar skriptet en händelse med misslyckad inloggning?
    Skriptet letar efter specifika händelse-ID:n i händelseloggen, t.ex. 4625 för misslyckade inloggningar.
  • Kan jag hämta data från en fjärransluten maskin?
    Ja, genom att ange parametern ComputerName kan du hämta data från en fjärrdator.

Konsekvenser

Genom att förstå antalet misslyckade inloggningsförsök kan IT-administratörer förebygga potentiella säkerhetsöverträdelser. Avvikelser i inloggningsmönster är ofta ett tidigt tecken på skadlig aktivitet. Genom att agera på dessa data kan yrkesverksamma därför stärka sina system mot potentiella hot.

Rekommendationer

  • Kontrollera att du har nödvändiga behörigheter för att hämta händelseloggar.
  • Kör skriptet regelbundet, särskilt för system som innehåller känslig information.
  • Undersök eventuella mönster av misslyckade inloggningar och meddela de berörda användarna.

Avslutande tankar

I en tid med allt fler cyberhot är verktyg som vårt PowerShell-skript oumbärliga. För en heltäckande säkerhetslösning kan plattformar som NinjaOne integreras, vilket säkerställer övervakning och hantering i realtid. NinjaOne, i kombination med proaktiva skript som det som diskuterats, ger ytterligare skydd mot cyberhot.

Nästa steg

För att bygga upp ett effektivt och handlingskraftigt IT-team krävs en centraliserad lösning som fungerar som ett centralt redskap för att leverera IT-tjänster. NinjaOne gör det möjligt för IT-teams att övervaka, hantera, säkra och stödja alla sina enheter, oavsett var de befinner sig, utan behovet av en komplex infrastruktur på plats.

Lär dig mer om NinjaOne endpoint-hantering, ta en live tour, eller starta en gratis provperiod av NinjaOne.

Kategorier:

Du kanske även gillar

×

Se NinjaOne i aktion!

Genom att skicka detta formulär accepterar jag NinjaOne:s integritetspolicy.

NinjaOne Villkor och bestämmelser

Genom att klicka på knappen ”Jag accepterar” nedan anger du att du accepterar följande juridiska villkor samt våra användarvillkor:

  • Äganderätt: NinjaOne äger och kommer att fortsätta att äga alla rättigheter, titlar och intressen i och till manuset (inklusive upphovsrätten). NinjaOne ger dig en begränsad licens att använda skriptet i enlighet med dessa juridiska villkor.
  • Begränsning av användning: Du får endast använda skriptet för dina legitima personliga eller interna affärssyften, och du får inte dela skriptet med någon annan part.
  • Republikbildning Förbud: Du får under inga omständigheter återpublicera skriptet i något skriptbibliotek som tillhör eller kontrolleras av någon annan programvaruleverantör.
  • Friskrivning från garantiansvar: Skriptet tillhandahålls ”i befintligt skick” och ”som tillgängligt”, utan garanti av något slag. NinjaOne ger inga löften eller garantier om att skriptet kommer att vara fritt från defekter eller att det kommer att uppfylla dina specifika behov eller förväntningar.
  • Antagande av risk: Din användning av skriptet sker på egen risk. Du bekräftar att det finns vissa inneboende risker med att använda skriptet, och du förstår och tar på dig var och en av dessa risker.
  • Avstående och befrielse: Du kommer inte att hålla NinjaOne ansvarig för några negativa eller oavsiktliga konsekvenser till följd av din användning av skriptet, och du avstår från alla juridiska eller skäliga rättigheter eller rättsmedel som du kan ha mot NinjaOne i samband med din användning av skriptet.
  • EULA: Om du är en NinjaOne-kund omfattas din användning av skriptet av det licensavtal för slutanvändare som gäller för dig (EULA).