When IT Goes Rogue: The Cost of Insufficient Offboarding
Just read the report about the former IT employee from the Iowa school district getting sentenced to 21 months. It’s a classic insider threat scenario that highlights a fundamental failure in Identity and Access Management (IAM). The attacker didn't need a sophisticated zero-day; they likely used cached credentials or a backdoor account left active post-termination.
This wasn't a technological exploit but a process failure. When an admin leaves, their access needs to be revoked instantly, not at the end of the pay cycle. However, verifying that removal is difficult without automation.
We’ve been auditing our Active Directory environment to ensure we don't have dormant privileged accounts lingering. Here is a quick PowerShell snippet we use to audit the Domain Admins group for accounts that haven't logged in recently, which could indicate stale access:
# Find Domain Admins inactive for 90+ days
$DaysInactive = 90
$InactiveDate = (Get-Date).AddDays(-$DaysInactive)
Get-ADGroupMember -Identity "Domain Admins" |
Get-ADUser |
Where-Object { $_.LastLogonDate -lt $InactiveDate -and $_.Enabled -eq $true } |
Select-Object Name, SamAccountName, LastLogonDate, Enabled
In this specific case, the disruption to classroom operations was the real damage. It underscores that availability is often the critical security pillar in education, arguably more than confidentiality.
How are you handling offboarding for senior sysadmins? Do you rely on HR tickets, or do you have automated scripts that trigger the moment a termination status is flagged in your HRIS?
From a SOC perspective, this is why we push for User and Entity Behavior Analytics (UEBA). We set up alerts for any 'critical' admin accounts performing mass deletions or group modifications outside of business hours.
We use a KQL rule similar to this to catch the noise:
IdentityInfo
| where AssignedRoles contains "Admin"
| join kind=inner (SecurityEvent
| where EventID in (4728, 4729, 4733, 4756)
| where TimeGenerated > ago(2h)
) on Account
| summarize count() by bin(TimeGenerated, 10m), Account
| where count_ > 5
It catches the occasional script error, but it definitely catches the malicious ones too.
Automation is the only way to go. We had a similar close call a few years ago. Now, we utilize a password vault for all privileged accounts. When an employee is marked as 'terminated' in Okta, a webhook fires to disable their AD account and immediately rotate the passwords for any shared accounts they had access to in the vault.
It adds a bit of overhead to the helpdesk to check out new credentials, but it beats having to explain to the superintendent why the grading system is down.
The hardest part we've found is the 'break-glass' accounts. If a disgruntled admin knows the local administrator password on the domain controllers or the firmware passwords, no amount of AD disabling helps.
We've started enforcing LAPS (Local Administrator Password Solution) rigorously and ensuring BitLocker is active so they can't just boot into a live USB to reset the local admin. Physical security is part of the offboarding process too—you have to collect their keys.
Solid points on break-glass accounts. Beyond AD access, we often see persistence via scheduled tasks or services set to run as SYSTEM, which survive a password reset.
During offboarding, we trigger an EDR query to hunt for recently created tasks on their assigned machines:
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in ("schtasks.exe", "sc.exe")
| where InitiatingProcessAccountName contains @"TARGET_USER"
Catching these before they fire is crucial.
To add to the persistence discussion, don't forget about orphaned service accounts and API keys. Disabling the user account does nothing if a scheduled task is running as a service principal with a static key.
We run a quarterly audit to identify stale service accounts that haven't authenticated in 90 days but still have privileged group memberships:
Get-ADServiceAccount -Filter * | Where-Object {$_.LastLogonDate -lt (Get-Date).AddDays(-90)} | Get-ADPrincipalGroupMembership
Without this, the offboarding process is incomplete.
Verified Access Required
To maintain the integrity of our intelligence feeds, only verified partners and security professionals can post replies.
Request Access