PowerShell and Security Audit
Greetings to you! I want to share with you a way that can facilitate the routine work of the system administration of the Win system using PowerShell.
One fine day, I was faced with the task of daily tracking the activity of users who use a terminal server as workstations. I think I will express not only my opinion, saying that “Event Viewer” included in the Windows administration tools is not the most convenient means of tracking the situation on the server. Yes, there is a filter by which you can filter out only the events of interest to us, but there is no convenient way that changes the display format of this information. As a result, an idea appeared with the help of PowerShell to parse security log events.
To get the list of events we need the Get-EventLog command one of the parameters of which is the name of the log, in our case security.

')
The team displays the contents of the entire journal, which is not at all suited to me. But everything is not so bad, what you see in the screenshot is not just text, but quite objects, with the properties of which you can do anything within the capabilities of PowerShell. Get properties of these objects allows the Get-Member cmdlet. By running the command line
Get-EventLog security | Get-Member
Get-EventLog security | Get-Member
, we will get a list of properties of all objects displayed by Get-EventLog.

By knowing the property list, you can manipulate the results of Get-EventLog. For example, to get a list of all events for today, the easiest way would be to use the parameters of the Get-EventLog cmdlet, namely the
-after
parameter. Full list of parameters can be found
here . As a result, we get the command
Get-EventLog security -after (Get-date -hour 0 -minute 0 -second 0)
, where the Get-Date cmdlet
Get-EventLog security -after (Get-date -hour 0 -minute 0 -second 0)
current date and time, but the hour, minute, and second parameters set the time output from the beginning the current day. As a result, we will receive a list of events that occurred today. Already better, but still not that.
I need to get a list of all users logged into the server using the RPD protocol, which led me to study the EventID and EntryType values. Further I will give not the full list of these values.
EventID values
Each login event is supplemented with a specific input type, the list of which will be listed below.
- 528 - Successful user login to computer.
- 529 - Failure to login. Invalid username or password.
- 530 - Failure to login. Attempting to log in with a user account outside the valid time interval.
- 531 - Failure to login. Attempting to log in using a disabled user account.
- 532 - Failure to login. Attempt to log in using a legacy user account.
- 533 - Failure to login. Attempting to log on to a user who is not allowed to log on to this computer.
- 534 - Failure to login. Attempt to login with an unauthorized login type.
- 535 - Failure to login. The password for the specified account has expired.
- 536 - Failure to login. Net Logon service is disabled.
- 537 - Failure to login. An attempt to log in failed for other reasons (In some cases, the reason for the denial to log in may not be known).
- 538 - The user logout process is complete.
- 539 - Failure to login. During a login attempt, the user account is locked.
- 540 - Successful user logon.
- 541-IKE basic authentication mode completed between the local computer and the registered peer-to-peer identity (establishing a strong mapping), or a fast mode established data channel.
- 542 - The data channel is disabled.
- 543 - The main mode is disabled. (The reason for this may be the end of the time interval that limits the duration of a reliable connection (by default - 8 hours), a change in policy or peer-to-peer termination).
- 544 - Basic authentication mode failure because the partner did not provide a valid certificate or the signature was not authenticated.
- 545 - Basic authentication fail due to Kerberos failure or invalid password.
- 546 - Failure to create a reliable IKE connection caused by the receipt of an unacceptable offer from a partner. Receive a packet containing invalid data.
- 547 - Failure during the IKE connection establishment procedure.
- 548 - Failure to login. The reliability identifier (SID) received from the trusted domain does not match the SID of the domain account for the client.
- 549 - Failure to login. All SID security identifiers corresponding to untrusted namespaces were filtered during forest authentication.
- 550 - Message notification, which may indicate a possible attack on the service.
- 551 - The user has initiated the logout process.
- 552 - The user successfully logged on to the computer using the correct credentials, despite the fact that he had already logged in as another user.
- 682 - User re-connected to disconnected terminal server session.
- 683 - The user is disconnected from the terminal server session without logging out (This event is generated when the user is connected to the terminal server session via the network. It appears on the terminal server).
EntryType Values
- 2 - Interactive. Successful user logon to computer.
- 3 - Network. A user or computer logged into this computer through the network.
- 4 - Batch. Batch input type is used by batch servers, the execution of processes on which is performed on behalf of the user, but without his direct intervention.
- 5 - Service. The service is running Service Control Manager.
- 7 - Unlocking. This workstation is unlocked.
- 8 - NetworkCleartext. The user logged into this computer through the network. The user password is passed to the authentication package in its unshipped form. Integrated authentication wraps all hashed accounts before sending them over the network. Credentials are not transmitted over the network in clear text.
- 9 - NewCredentials. The visitor has cloned his current token and specified new accounts for outgoing connections. A new login session has the same local identity, but uses different accounts for network connections.
- 10 - RemoteInteractive. The user logged on to this computer remotely using Terminal Services or Remote Desktop.
- 11 - CachedInteractive. The user logged on to this computer with network credentials that were stored locally on the computer. The domain controller was not used to validate credentials.
This information is drawn mainly from this
source . From the obtained information, we can conclude that we need an event with EventID = 528 and EntryType = 10, which will correspond to the entrance to the computer via RDP. A little change our team.
Get-EventLog security -message "* :?10*" -after (Get-date -hour 0 -minute 0 -second 0) | ?{$_.eventid -eq 528 }
The
-message
parameter reflects the complete message of our event, which contains the “Entry type” (“Entry Type”, since I have the Russian version of 2003), through the templates we set the search for the line of interest.
Since I did not find the parameters of the Get_EventLog
-EventID
cmdlet, I had to use the properties of the object:
$_
means the object itself which appears initially
-eq
means equality of value, in our case 528
The result will be the following:

In general, what is needed, but only that information is displayed to us. We will fix it. For me, three parameters of the object are relevant: time, username, IP address. In the future, we will create an object and enter the data of interest into it. I created the script “test.ps1”, because the whole team will be problematic to recruit.
$Events = Get-EventLog security -message "* :?10*" -after (get-date -hour 0 -minute 0 -second 0) | ?{$_.eventid -eq 528 }
$Data = New-Object System.Management.Automation.PSObject
$Data | Add-Member NoteProperty Time ($null)
$Data | Add-Member NoteProperty UserName ($null)
$Data | Add-Member NoteProperty Address ($null)
$Events | %{
$Data.time = $_.TimeGenerated
$message = $_.message.split("`n") | %{$_.trimstart()} | %{$_.trimend()}
$Data.UserName = ($message | ?{$_ -like ":*"} | %{$_ -replace "^.+:."} )
$Data.Address = ($message | ?{$_ -like " :*"} | %{$_ -replace "^.+:."})
$data
}
Let's sort this code closer.
$Events = Get-EventLog security -message "* :?10*" -after (get-date -hour 0 -minute 0 -second 0) | ?{$_.eventid -eq 528 }
$Events = Get-EventLog security -message "* :?10*" -after (get-date -hour 0 -minute 0 -second 0) | ?{$_.eventid -eq 528 }
- we put the results of the sample on the events in a variable, so that later it would be convenient to work with it.
Next, create a “pattern” of our future table containing three values: time, user name, and address.
$Data = New-Object System.Management.Automation.PSObject
$Data | Add-Member NoteProperty Time ($null)
$Data | Add-Member NoteProperty UserName ($null)
$Data | Add-Member NoteProperty Address ($null
$Data = New-Object System.Management.Automation.PSObject
$Data | Add-Member NoteProperty Time ($null)
$Data | Add-Member NoteProperty UserName ($null)
$Data | Add-Member NoteProperty Address ($null
)
$Events | %{}
$Events | %{}
- go through each object that will be in the selection results
$Data.time = $_.TimeGenerated
-
$Data.time = $_.TimeGenerated
time
$message = $_.message.split("`n") | %{$_.trimstart()} | %{$_.trimend()}
$message = $_.message.split("`n") | %{$_.trimstart()} | %{$_.trimend()}
- put the array of strings in the message variable, which are separated by the line break ('n), the trimstart (), trimend () functions remove all extra characters at the end and the beginning of the line, the split function splits the line .
$Data.UserName = ($message | ?{$_ -like ":*"} | %{$_ -replace "^.+:."} )
$Data.Address = ($message | ?{$_ -like " :*"} | %{$_ -replace "^.+:."})
$Data.UserName = ($message | ?{$_ -like ":*"} | %{$_ -replace "^.+:."} )
$Data.Address = ($message | ?{$_ -like " :*"} | %{$_ -replace "^.+:."})
Next, in the newly formed array, we are looking for matches on the string " User: "and" Network address of the source: ", and -replace later removes these regular expressions, leaving the information itself.
Run the script with the command
.\test.ps1
. (As you can see, to run the PS script, you have to specify the path, even if it is in the current working folder):

If the script does not start, then most likely your PoSh is not configured to run scripts. Run the
Set-ExecutionPolicy RemoteSignet
.
It looks quite good, but I think you can improve the script. For convenience, we will add to it the possibility of setting parameters, and highlighting lines with a color based on the IP address mask.
param ($key1,$val1,$val2,$val3,$val4,$val5,$val6)
if ($val1 -eq $null) {$val1=0};
$mydate = Get-date -hour 0 -minute 0 -second 0;
if ($key1 -eq "year") { $mydate = (Get-date -hour 0 -minute 0 -second 0 -day 1 -month 1); $mydate = $mydate.addyears(-$val1); };
if ($key1 -eq "month") { $mydate = (Get-date -hour 0 -minute 0 -second 0 -day 1); $mydate = $mydate.addmonths(-$val1); };
if ($key1 -eq "day") { $mydate = $mydate.adddays(-$val1) };
if ($key1 -eq "date") { $mydate = (Get-date -hour 0 -minute 0 -second 0 -day $val1 -month $val2 -year $val3); }; #
if ($val4 -eq $null) {$Events = Get-EventLog security -message "* :?10*" -after ($mydate) | ?{$_.eventid -eq 528 }}
if ($val4 -ne $null) {$Events = Get-EventLog security -message "* :?10*" -after ($mydate) -before (get-date -hour 0 -minute 0 -second 0 -day $val4 -month $val5 -year $val6) | ?{$_.eventid -eq 528 }}
$Data = New-Object System.Management.Automation.PSObject
$Data | Add-Member NoteProperty Time ($null)
$Data | Add-Member NoteProperty UserName ($null)
$Data | Add-Member NoteProperty Address ($null)
$Events | %{
$Data.time = $_.TimeGenerated
$message = $_.message.split("`n") | %{$_.trimstart()} | %{$_.trimend()}
$Data.UserName = ($message | ?{$_ -like ":*"} | %{$_ -replace "^.+:."} )
$Data.Address = ($message | ?{$_ -like " :*"} | %{$_ -replace "^.+:."})
$textcolor = $host.ui.rawui.foregroundcolor
$host.ui.rawui.foregroundcolor = "red"
if ($data.address -like "192.168.0*") {$host.ui.rawui.foregroundcolor = "DarkGreen"}
if ($data.address -like "10.*") {$host.ui.rawui.foregroundcolor = "yellow"}
$data
$host.ui.rawui.foregroundcolor = $textcolor
}
param ($key1,$val1,$val2,$val3,$val4,$val5,$val6)
- defines the parameters passed to the script.
if ($key1 -eq "day") { $mydate = $mydate.adddays(-$val1) };;
we check the passed parameters for compliance with the key, if the key is the same, then we adjust the date according to the specified parameters. In this case, the “day” key is passed as an argument by whose argument we will translate the date a certain number of days ago. Those. the log will be displayed for a certain number of days, the rest of the conditions are fulfilled by analogy, for a month and for a year. If the key “date” is indicated, then a specific date indicated by a space, for example, “01 05 2011”, is taken as a reference point, if we also specify another date after the space, then a certain period specified in these dates will be displayed on the screen. To display information in color, it was originally planned to use the Write-Host cmdlet, which has the parameters -backgroundcolor and -foregroundcolor, but eventually had to be discarded because it is not friendly with the output of objects.

I made the internal LAN display green, external yellow, and all other unknown addresses in red for clarity.
And if you specify
{$_.eventid -eq 529 }
then the result will be all login attempts with the wrong passwords.

The list is quite long, it is useful to check and block such villains on the firewall a couple of times a day.
As a result, the script, with minimal modifications, can be adapted to conveniently display any information contained in the Event Log.