Come trovare i tentativi di login non riusciti in Windows usando PowerShell

Garantire la sicurezza dei sistemi informatici è un’attività cruciale. Identificare attività sospette, come ad esempio numerosi tentativi di login falliti, è una misura importante per ridurre i rischi delle potenziali minacce. Lo script fornito, scritto in PowerShell, è uno strumento versatile per aiutare i professionisti IT e gli MSP a ottenere informazioni sui tentativi di login non riusciti.

Background

Tracciare i tentativi di login non riusciti su un sistema può fornire informazioni cruciali per gli amministratori IT. Questi tentativi possono fornire informazioni su possibili violazioni della sicurezza, aiutare a monitorare i comportamenti degli utenti e a mantenere l’integrità del sistema. Lo script PowerShell fornito recupera in modo efficiente questi dati, offrendo una soluzione sicura per i professionisti. L’importanza di questo strumento non sarà mai sottolineata abbastanza. Con l’aumento delle minacce alla sicurezza informatica, disporre di un metodo efficiente per rilevare le anomalie nei login degli utenti diventa essenziale per gli MSP e i professionisti IT.

Lo script per ottenere informazioni sui tentativi di login non riusciti

#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
            }
        }
    }
}

 

Accedi a oltre 700 script nel Dojo di NinjaOne

Ottieni l’accesso

Descrizione dettagliata dello script

Nella sua funzione principale, lo script recupera i dati dai log degli eventi di un determinato computer, cercando specifici ID evento che rappresentano i tentativi di accesso falliti e riusciti.

  • Parametri: Lo script inizia definendo parametri come ComputerName, UserName e Detailed. Consente all’utente di specificare il computer, l’utente e il livello di dettaglio dei tentativi di accesso.
  • Funzioni: Due funzioni, Get-FailureReason e Get-LogonType traducono le informazioni codificate dei log degli eventi in dati leggibili relativi al tipo di login e al motivo di un login fallito.
  • Recupero degli eventi: Lo script recupera quindi i log degli eventi, filtrandoli per conservare solo le informazioni necessarie. Si tratta di selezionare le istanze con gli ID evento rilevanti.
  • Elaborazione: Se vengono richiesti dati dettagliati, lo script fornisce un’analisi completa di ogni tentativo di accesso, altrimenti viene visualizzato un riepilogo dei tentativi falliti per ogni utente.

Possibili casi d’uso

Immagina un amministratore IT di un’azienda di medie dimensioni. Di recente, il reparto IT ha notato un incremento del numero di tentativi di accesso falliti, soprattutto durante le ore non lavorative. Utilizzando lo script, l’amministratore può verificare rapidamente quali utenti hanno effettuato tentativi di accesso non riusciti e con quale frequenza. Nel riscontrare che un singolo account utente ha fallito più tentativi in un breve intervallo di tempo, l’amministratore può concludere che questo account potrebbe essere stato compromesso. In questo modo, lo script favorisce il rilevamento tempestivo di un possibile problema e una pronta risoluzione.

Approccio alternativo

Esistono diversi metodi per tracciare i tentativi di login non riusciti. Il monitoraggio di sicurezza integrato di Windows, per esempio, consente di visualizzare i log di sicurezza tramite il Visualizzatore eventi. Sebbene questo approccio sia semplice, può richiedere molto tempo. Il nostro script PowerShell semplifica il processo, offrendo una soluzione più efficiente e personalizzabile.

Domande frequenti

  • Come fa lo script a identificare un’attività di login fallita?
    Lo script cerca ID evento specifici nel log eventi, ad esempio 4625 per i login non riusciti.
  • È possibile recuperare i dati da un computer remoto?
    Sì, fornendo il parametro ComputerName è possibile ottenere dati da un computer remoto.

Implicazioni

Individuando i tentativi di accesso falliti, gli amministratori IT possono prevenire potenziali minacce alla sicurezza. Le anomalie nei modelli di login sono spesso un primo segnale di attività pericolose. Pertanto, agendo su questi dati, i professionisti possono rendere i loro sistemi più sicuri contro le potenziali minacce.

Raccomandazioni

  • Assicurati di disporre delle autorizzazioni necessarie per recuperare i log degli eventi.
  • Esegui lo script con regolarità, soprattutto per i sistemi che contengono informazioni sensibili.
  • Esamina eventuali modelli di accessi non riusciti e informa gli utenti interessati.

Considerazioni finali

Nell’era delle crescenti minacce informatiche, strumenti come il nostro script PowerShell sono essenziali. Per una soluzione di sicurezza completa, è possibile implementare piattaforme come NinjaOne, che garantiscono il monitoraggio e la gestione in tempo reale. NinjaOne in combinazione con script proattivi come quello di cui abbiamo parlato, permette di ottenere un livello ulteriore di difesa contro le minacce informatiche.

Passi successivi

La creazione di un team IT efficiente ed efficace richiede una soluzione centralizzata che funga da principale strumento per la fornitura di servizi. NinjaOne consente ai team IT di monitorare, gestire, proteggere e supportare tutti i dispositivi, ovunque essi si trovino, senza la necessità di una complessa infrastruttura locale.

Per saperne di più su NinjaOne Endpoint Management, fai un tour dal vivo, o inizia la tua prova gratuita della piattaforma NinjaOne.

Categorie:

Ti potrebbe interessare anche

Guarda una demo×
×

Guarda NinjaOne in azione!

Inviando questo modulo, accetto La politica sulla privacy di NinjaOne.

Termini e condizioni NinjaOne

Cliccando sul pulsante “Accetto” qui sotto, dichiari di accettare i seguenti termini legali e le nostre condizioni d’uso:

  • Diritti di proprietà: NinjaOne possiede e continuerà a possedere tutti i diritti, i titoli e gli interessi relativi allo script (compreso il copyright). NinjaOne ti concede una licenza limitata per l’utilizzo dello script in conformità con i presenti termini legali.
  • Limitazione d’uso: Puoi utilizzare lo script solo per legittimi scopi personali o aziendali interni e non puoi condividere lo script con altri soggetti.
  • Divieto di ripubblicazione: In nessun caso ti è consentito ripubblicare lo script in una libreria di script appartenente o sotto il controllo di un altro fornitore di software.
  • Esclusione di garanzia: Lo script viene fornito “così com’è” e “come disponibile”, senza garanzie di alcun tipo. NinjaOne non promette né garantisce che lo script sia privo di difetti o che soddisfi le tue esigenze o aspettative specifiche.
  • Assunzione del rischio: L’uso che farai dello script è da intendersi a tuo rischio. Riconosci che l’utilizzo dello script comporta alcuni rischi intrinseci, che comprendi e sei pronto ad assumerti.
  • Rinuncia e liberatoria: Non riterrai NinjaOne responsabile di eventuali conseguenze negative o indesiderate derivanti dall’uso dello script e rinuncerai a qualsiasi diritto legale o di equità e a qualsiasi rivalsa nei confronti di NinjaOne in relazione all’uso dello script.
  • EULA: Se sei un cliente NinjaOne, l’uso dello script è soggetto al Contratto di licenza con l’utente finale (EULA) applicabile.