I was interested to see if I could monitor some audit logs, that were stored as a text file on a server for certain access violations and permission escalations, so first we need to find the type of data we are looking for with all the variables required, then we need to look at an e-mail notification system that outlines the actions detected.
This is the results that will land in your inbox, obviously certain details are redacted.
We now need to look at how this works and the flow, so I have already said this will monitor log files and then report on certain events, in this example we are looking for the even that contains the following match, in this instance we are looking for policy updates where assignment are edited by pokey.bear - below you see the name and the samAccountName:
$matches = $content | Select-String 'assigned to.*pokey.bear|was assigned to "pokey.bear1'
Script : AuditLogMonitor.ps1
# Define servers to monitor
$servers = @(
"entry1.bear.local",
"entry2.bear.local"
)
# Create timestamp for log file
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$logFolder = ".\Logging"
$logFile = Join-Path $logFolder "logmonitor_$timestamp.txt"
# Create logging directory if it doesn't exist
if (-not (Test-Path $logFolder)) {
New-Item -ItemType Directory -Path $logFolder -Force
Write-Host "Created logging directory: $logFolder"
}
# Function to write to both console and log file
function Write-OutputAndLog {
param(
[string]$Message,
[string]$LogFile
)
Write-Host $Message
Add-Content -Path $LogFile -Value $Message
}
# Function to scan logs on a remote server
function Scan-RemoteLogs {
param (
[string]$ServerName,
[string]$LogFile
)
Write-OutputAndLog -Message "`n=== Starting scan on server: $ServerName ===" -LogFile $LogFile
try {
# Test server connectivity
Write-OutputAndLog -Message "Testing connection to $ServerName..." -LogFile $LogFile
if (-not (Test-Connection -ComputerName $ServerName -Count 1 -Quiet)) {
Write-OutputAndLog -Message "ERROR: Unable to connect to server: $ServerName" -LogFile $LogFile
return
}
Write-OutputAndLog -Message "Successfully connected to $ServerName" -LogFile $LogFile
# Define the path to scan
$path = "\\$ServerName\C$\Program Files\Twingate\logs"
# Verify path exists
Write-OutputAndLog -Message "Checking path: $path" -LogFile $LogFile
if (-not (Test-Path $path)) {
Write-OutputAndLog -Message "ERROR: Path does not exist on server $ServerName`: $path" -LogFile $LogFile
return
}
Write-OutputAndLog -Message "Path verified successfully" -LogFile $LogFile
# Get all matching log files from the last 24 hours
Write-OutputAndLog -Message "Scanning for Mobility_events_Log_* files from the last 24 hours..." -LogFile $LogFile
$cutoffTime = (Get-Date).AddDays(-1)
$files = Get-ChildItem -Path $path -Filter "Mobility_events_Log_*" -File |
Where-Object { $_.LastWriteTime -gt $cutoffTime }
Write-OutputAndLog -Message "Found $($files.Count) matching files from last 24 hours" -LogFile $LogFile
$matchCount = 0
foreach ($file in $files) {
Write-OutputAndLog -Message "Scanning file: $($file.Name) (Last modified: $($file.LastWriteTime))" -LogFile $LogFile
# Get file content and search for role assignments
$content = Get-Content $file.FullName
$matches = $content | Select-String 'assigned to.*pokey.bear|was assigned to "pokey.bear1'
foreach ($match in $matches) {
$matchCount++
$matchInfo = @{
DateTime = (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
Server = $ServerName
File = $file.Name
LineNumber = $match.LineNumber
Line = $match.Line.Trim()
}
# Extract the role being assigned (if possible)
$roleName = ""
if ($match.Line -match 'role "([^"]+)"') {
$roleName = $matches[1]
}
# Create detailed log entry
$logMessage = "[$($matchInfo.DateTime)] ROLE ASSIGNMENT FOUND`n" + `
" Server: $($matchInfo.Server)`n" + `
" File: $($matchInfo.File)`n" + `
" Line Number: $($matchInfo.LineNumber)`n" + `
$(if ($roleName) {" Role: $roleName`n"}) + `
" Full Entry: $($matchInfo.Line)`n"
Write-OutputAndLog -Message $logMessage -LogFile $LogFile
}
}
}
Write-OutputAndLog -Message "=== Scan completed for $ServerName - Found $matchCount role assignments ===" -LogFile $LogFile
}
catch {
Write-OutputAndLog -Message "ERROR processing server $ServerName`: $_" -LogFile $LogFile
}
}
# Main scanning process
Write-OutputAndLog -Message "=== Starting log scanning process at $(Get-Date) ===" -LogFile $logFile
Write-OutputAndLog -Message "Log file created at: $logFile" -LogFile $logFile
foreach ($server in $servers) {
Scan-RemoteLogs -ServerName $server -LogFile $logFile
}
Write-OutputAndLog -Message "`n=== Scanning process completed at $(Get-Date) ===" -LogFile $logFile
This will then output all the events that match to a log file in the Logging folder however with the limitation to the act that these logs are within 24 hours, to avoid false positives.
The script find a match that match will be extracted to this look file and will look something like this:
[2024-12-17 16:42:34] ROLE ASSIGNMENT MODIFICATION
Server: access1.bear.local
File: Twingate_events_Log_2024-12-11.log
The next step is the next script needs to read all the log files in the logging folder and look for the phrase from earlier of "role assignment modification" it will then from the log file extract the server name and the date and time, if a valid event is found it will sent an email and then rewrite the name of the file from "log" to "temp" so it will not be included in the next check.
Script : ReportMailer.ps1
# Set the path to the Logging folder
$loggingPath = ".\Logging"
# Email configuration
$smtpServer = "smtpbear.local"
$fromAddress = "Role.Monitoring@croucher.cloud"
$toAddress = "lee@croucher.cloud"
# Get all txt and log files in the Logging folder and subfolders
$files = Get-ChildItem -Path $loggingPath -Include *.txt,*.log -Recurse
# Initialize array to store found events
$foundEvents = @()
# Keep track of processed files
$processedFiles = @()
foreach ($file in $files) {
# Read file content and look for "ROLE ASSIGNMENT MODIFICATION"
$content = Get-Content $file.FullName
$fileHasEvents = $false
# Process content line by line
for ($i = 0; $i -lt $content.Count; $i++) {
if ($content[$i] -match "ROLE ASSIGNMENT MODIFICATION") {
$fileHasEvents = $true
# Extract information from the next few lines
$serverLine = $content[$i + 1]
$fileLine = $content[$i + 2]
$lineNumLine = $content[$i + 3]
$roleLine = $content[$i + 4]
$fullEntryLine = $content[$i + 5]
# Extract server name
$server = ($serverLine -split "Server: ")[1].Trim()
# Extract date and time from the full entry
if ($fullEntryLine -match "\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}") {
$datetime = [datetime]($matches[0])
$date = $datetime.ToString("yyyy-MM-dd")
$time = $datetime.ToString("HH:mm:ss")
}
# Create event object
$event = [PSCustomObject]@{
Date = $date
Time = $time
Server = $server
FullEntry = ($fullEntryLine -split "Full Entry: ")[1].Trim()
}
$foundEvents += $event
}
}
if ($fileHasEvents) {
$processedFiles += $file
}
}
# If events were found, send email
if ($foundEvents.Count -gt 0) {
# Create HTML email body
$htmlHeader = @"
<html>
<head>
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
.header { background-color: #cc0000; color: white; padding: 20px; margin-bottom: 20px; }
.event-container { margin-bottom: 30px; border: 1px solid #ddd; padding: 20px; border-radius: 5px; }
.event-header { background-color: #cc0000; color: white; padding: 10px; margin: -20px -20px 20px -20px; border-radius: 5px 5px 0 0; }
.event-detail { margin: 10px 0; }
.label { font-weight: bold; color: #666; }
.value { margin-left: 10px; }
</style>
</head>
<body>
<div class="header">
<h2>Role Assignment Changes Detected</h2>
</div>
"@
$htmlBody = ""
foreach ($event in $foundEvents) {
$htmlBody += @"
<div class="event-container">
<div class="event-header">
<h3>Role Assignment Change</h3>
</div>
<div class="event-detail">
<span class="label">Date:</span>
<span class="value">$($event.Date)</span>
</div>
<div class="event-detail">
<span class="label">Time:</span>
<span class="value">$($event.Time)</span>
</div>
<div class="event-detail">
<span class="label">Reporting Server:</span>
<span class="value">$($event.Server)</span>
</div>
<div class="event-detail">
<span class="label">Event:</span>
<span class="value">$($event.FullEntry)</span>
</div>
</div>
"@
}
$htmlFooter = @"
</body>
</html>
"@
$htmlEmail = $htmlHeader + $htmlBody + $htmlFooter
# Send email
try {
Send-MailMessage -SmtpServer $smtpServer `
-From $fromAddress `
-To $toAddress `
-Subject "Role Assignment Changes Detected" `
-Body $htmlEmail `
-BodyAsHtml `
-Priority High
Write-Host "Email notification sent successfully"
# After successful email, rename processed files
foreach ($file in $processedFiles) {
$newName = Join-Path -Path $file.DirectoryName -ChildPath ($file.BaseName + ".temp")
try {
Rename-Item -Path $file.FullName -NewName $newName -Force
Write-Host "Renamed $($file.Name) to $($file.BaseName).temp"
}
catch {
Write-Host "Error renaming file $($file.Name): $_"
}
}
}
catch {
Write-Host "Error sending email: $_"
}
}
else {
Write-Host "No role assignment changes found"
}