Kerberoasting
Most common technique for obtaining service account credentials: requests a service ticket and cracks the password hash offline, no lockout
0 MFA
Multi-factor authentication prompts that service accounts receive: making compromised service account credentials immediately usable without additional factor
4624 / 4648
Event IDs for investigating service account authentication: logon events (4624) and explicit credential use (4648) reveal what the account accessed
5-15 minutes
Target time to complete credential rotation once the investigation establishes it is safe: delay means continued attacker access

SponsoredRetool

Retool's new app builder is where AI-generated code ships safely

Building apps with AI is easy. Getting them to production safely is another story.

Start building for free today

Service account compromises are different from user account compromises in three important ways: the account often has broad access to databases, APIs, and applications that give it significant reach; there is no MFA to prevent the attacker from immediately using the credentials; and the credentials may be cached on many systems (wherever the service runs), requiring more extensive cleanup.

The investigation needs to answer four questions: How did the attacker get the credentials? What did they access with them? Did they establish persistence beyond the service account? And how do you rotate the credentials without breaking the 12 services that depend on this account?

Phase 1: Initial Triage: Confirm and Scope

Confirm the account is compromised:

Detection signals that indicate service account compromise:

  • SIEM alert for Kerberoasting (Event ID 4769, RC4 encryption, SPN-registered account)
  • EDR alert for process using the account in an unusual context
  • Threat intel notification (credentials found in underground forum)
  • Login alert from unusual IP, time, or geolocation for the account
  • Failed authentication to a system the account should not access
# Check the account's current status and recent activity
Get-ADUser -Identity svc-webapp -Properties * | 
  Select-Object SamAccountName, Enabled, PasswordLastSet, LastLogonDate,
                PasswordNeverExpires, ServicePrincipalNames

# Check password age: Kerberoasted accounts often have old passwords
$pwAge = (Get-Date) - (Get-ADUser svc-webapp -Properties PasswordLastSet).PasswordLastSet
Write-Host "Password age: $($pwAge.Days) days"
# > 90 days: elevated crack risk
# > 365 days: high risk that the hash has been cracked

Determine what the account has access to:

# Find all group memberships (including nested)
Get-ADUser -Identity svc-webapp -Properties MemberOf | 
  Select-Object -ExpandProperty MemberOf | 
  ForEach-Object { (Get-ADGroup $_).Name }

# Check for direct ACL grants on sensitive objects
# (BloodHound is better for this: see the BloodHound article)
Get-ADUser -Identity svc-webapp | 
  ForEach-Object { (Get-Acl "AD:\$($_.DistinguishedName)").Access } |
  Where-Object IdentityReference -match 'svc-webapp'

# Find service principal names (what services use this account)
Get-ADUser -Identity svc-webapp -Properties ServicePrincipalNames | 
  Select-Object -ExpandProperty ServicePrincipalNames
# MSSQLSvc/sqlserver.corp.local:1433 → account runs SQL Server
# HTTP/webapp.corp.local → account runs an IIS application pool

Phase 2: Determine How Credentials Were Obtained

Check for Kerberoasting (most common for SPN-registered accounts):

# Check if the account has SPNs (Kerberoastable)
(Get-ADUser svc-webapp -Properties ServicePrincipalNames).ServicePrincipalNames
# If not empty → the account was Kerberoastable

# Check Event ID 4769 on DCs for RC4 ticket requests targeting this account
Get-WinEvent -ComputerName dc01 -FilterHashtable @{
  LogName = 'Security'
  Id = 4769
  StartTime = (Get-Date).AddDays(-30)
} | Where-Object {
  $_.Properties[0].Value -eq 'svc-webapp' -and  # Service name matches
  $_.Properties[5].Value -eq '0x17'  # RC4 encryption type
} | Select-Object TimeCreated, @{n='Client';e={$_.Properties[9].Value}},
    @{n='ClientIP';e={$_.Properties[10].Value}}

Check for password spray or brute force:

# Look for failed logon attempts (4625) for the account in the past 30 days
Get-WinEvent -ComputerName dc01 -FilterHashtable @{
  LogName = 'Security'
  Id = 4625
  StartTime = (Get-Date).AddDays(-30)
} | Where-Object { $_.Properties[5].Value -eq 'svc-webapp' } |
  Group-Object @{e={$_.Properties[19].Value}} |  # Group by source IP
  Sort-Object Count -Descending | Select-Object -First 10

Check for credential dumping on hosts where the service runs:

// Sentinel: check for LSASS access on hosts running the service (Sysmon Event 10)
SysmonEvent
| where EventID == 10
| where TargetImage endswith "lsass.exe"
| where Computer in ("sqlserver", "webapp01", "webapp02")  // Hosts where svc-webapp runs
| where TimeGenerated > ago(30d)
| project TimeGenerated, Computer, SourceImage, GrantedAccess
Free daily briefing

Briefings like this, every morning before 9am.

Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.

Phase 3: Determine What the Attacker Did With the Credentials

Authentication timeline: what systems did the account access:

# Query all authentication events for the service account across all DCs
# Focus on logons from unexpected source IPs or at unexpected times

$startTime = (Get-Date).AddDays(-30)
$domainControllers = (Get-ADDomainController -Filter *).Hostname

$logons = foreach ($dc in $domainControllers) {
  Get-WinEvent -ComputerName $dc -FilterHashtable @{
    LogName = 'Security'
    Id = @(4624, 4648)
    StartTime = $startTime
  } -ErrorAction SilentlyContinue | Where-Object {
    $_.Properties[5].Value -eq 'svc-webapp' -or  # Event 4624
    $_.Properties[1].Value -eq 'svc-webapp'       # Event 4648
  } | Select-Object TimeCreated,
    @{n='Event';e={$_.Id}},
    @{n='LogonType';e={$_.Properties[8].Value}},
    @{n='SourceIP';e={if ($_.Id -eq 4624) {$_.Properties[18].Value} else {$_.Properties[12].Value}}},
    @{n='TargetServer';e={if ($_.Id -eq 4648) {$_.Properties[9].Value} else {'N/A'}}},
    @{n='DC';e={$dc}}
}

# Identify anomalous source IPs
$logons | Group-Object SourceIP | Sort-Object Count -Descending
# Expected: the servers running the svc-webapp service
# Unexpected: workstations, external IPs, admin machines that never use this account

KQL: build authentication timeline in Sentinel:

SecurityEvent
| where EventID in (4624, 4648, 4625)
| where AccountName == "svc-webapp"
| where TimeGenerated > ago(30d)
| project
    TimeGenerated,
    EventID,
    LogonType,
    IpAddress,
    TargetServerName,
    Computer
| sort by TimeGenerated asc
// Look for: source IPs outside the expected service hosts
// Logon types: 3 (network) or 10 (RDP) from unexpected sources = lateral movement

Check for actions performed using the account:

# If the account has database access: check DB audit logs
# SQL Server: Look for logins and queries from unusual hosts
Invoke-Sqlcmd -ServerInstance sqlserver -Query "
  SELECT login_name, host_name, program_name, login_time, last_request_time
  FROM sys.dm_exec_sessions
  WHERE login_name = 'CORP\svc-webapp'
  ORDER BY login_time DESC"

# If the account has Azure/AWS access: check cloud logs
Get-AzLog -StartTime (Get-Date).AddDays(-30) | 
  Where-Object { $_.Claims['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn'] -match 'svc-webapp' }

Phase 4: Credential Rotation Without Breaking Services

Rotating a service account password requires updating every system that uses it. Rotating before identifying all consumers breaks services: but every minute without rotation extends attacker access.

Find all systems using the service account:

# Find Windows services running as this account across the domain
# Requires admin access to each server
$servers = Get-ADComputer -Filter * -Properties OperatingSystem |
  Where-Object OperatingSystem -match 'Server' | 
  Select-Object -ExpandProperty Name

$serviceMap = foreach ($server in $servers) {
  Get-CimInstance -ComputerName $server -ClassName Win32_Service \
    -ErrorAction SilentlyContinue | 
    Where-Object StartName -match 'svc-webapp' |
    Select-Object @{n='Server';e={$server}}, Name, DisplayName, StartName, State
}

$serviceMap | Format-Table -AutoSize

# Also check:
# IIS application pools (look for the account in IIS Manager)
# Scheduled tasks
$tasks = foreach ($server in $servers) {
  (Get-ScheduledTask -CimSession $server -ErrorAction SilentlyContinue) | 
    Where-Object { $_.Principal.UserId -match 'svc-webapp' } |
    Select-Object @{n='Server';e={$server}}, TaskName
}

Rotate the password:

# Generate a strong random password
$newPw = [System.Web.Security.Membership]::GeneratePassword(32, 8)
$secPw = ConvertTo-SecureString $newPw -AsPlainText -Force

# Rotate the AD password
Set-ADAccountPassword -Identity svc-webapp -NewPassword $secPw -Reset

# Immediately update all services found above
foreach ($service in $serviceMap) {
  Invoke-Command -ComputerName $service.Server -ScriptBlock {
    $svc = Get-Service -Name $using:service.Name
    $svc | Set-Service -Credential (New-Object PSCredential('CORP\svc-webapp', $using:secPw))
    $svc | Restart-Service -Force
  }
}

# Better long-term solution: migrate to gMSA
# gMSA rotates its own password automatically (120 chars)
# No need for manual rotation after migration
New-ADServiceAccount -Name gMSA-webapp \
  -DNSHostName webapp.corp.local \
  -PrincipalsAllowedToRetrieveManagedPassword webapp-servers-sg

Post-rotation verification:

# Verify all services restarted successfully with new credentials
foreach ($service in $serviceMap) {
  $status = Get-Service -ComputerName $service.Server -Name $service.Name
  Write-Host "$($service.Server) / $($service.Name): $($status.Status)"
}

# Monitor for authentication failures with the old password hash
# (Attacker trying to use the old credentials)
# Event ID 4625 with the account name after rotation = attacker still trying

The bottom line

A compromised service account investigation runs four phases: confirm compromise and scope access (group memberships, SPNs, dependent systems), determine how credentials were obtained (Kerberoasting Event 4769, brute force Event 4625, credential dumping via Sysmon), build an authentication timeline to identify what the attacker accessed (Event IDs 4624 and 4648, anomalous source IPs), then rotate credentials after mapping all consumers to avoid breaking services. Migrate compromised service accounts to gMSAs as the permanent fix: automatic 120-character password rotation eliminates the Kerberoasting attack surface.

Frequently asked questions

How do you investigate a compromised service account in Active Directory?

Four phases: determine how credentials were obtained (check Event ID 4769 for Kerberoasting, 4625 for brute force, Sysmon Event 10 for LSASS dumping on service hosts), build an authentication timeline (all Event 4624 and 4648 events for the account, identifying anomalous source IPs), assess what the attacker accessed using the account, then rotate credentials after mapping all dependent services to avoid outages. Monitor for Event 4625 with the old credentials post-rotation.

How do you rotate a service account password without breaking services?

Before rotating, enumerate all services using the account: run Get-CimInstance Win32_Service across domain servers filtering for the account name, check IIS application pools, and check scheduled tasks. Document every consumer. Rotate the AD password with Set-ADAccountPassword, then immediately update each service's logon credentials and restart them. The permanent fix is migrating to Group Managed Service Accounts (gMSAs), which automatically rotate their own 120-character passwords.

What are the indicators of a compromised service account?

Compromised service accounts show distinct behavior from their normal service activity patterns. Key indicators: interactive logon events (service accounts should show only Logon Type 5 — service logon; interactive logons Type 2 or Type 10 are anomalous), logon from unexpected source IPs or workstations, authentication to systems the service does not normally access, unusual hours of activity, use of Kerberoastable service ticket types (RC4 ticket requests where the account only needs AES), and password spray failures (multiple 4625 events with wrong passwords indicating brute-force attempts). Any service account showing interactive logon events should be treated as a high-priority investigation.

How do I contain a compromised service account without disrupting services?

Service account containment requires balancing disruption risk against continued attacker access. Steps in order: (1) Determine all services that use the account before taking any action — blind disabling causes cascading failures. (2) If the account has overprivileged rights (domain admin membership, excessive ACLs), reduce them immediately while leaving the account enabled — this limits what the attacker can do without disrupting services. (3) Reset the password: this terminates existing Kerberos sessions and forces Kerberos ticket renewal. (4) Coordinate with application teams to update passwords in all service consumers simultaneously. (5) After password rotation, monitor for new authentication failures indicating missed consumers. For critical production services, plan containment during a maintenance window when disruption impact is lowest.

How do I find all services using a specific Active Directory service account?

No single AD attribute stores this — you must scan service consumers across the environment. PowerShell script to discover all usages: (1) Windows services: Invoke-Command -ComputerName (Get-ADComputer -Filter *).Name -ScriptBlock { Get-WmiObject Win32_Service | Where-Object {$_.StartName -like '*svc-targetaccount*'} | Select PSComputerName, Name, StartName }. (2) Scheduled tasks: Get-ScheduledTask | Where-Object {$_.Principal.UserId -match 'svc-targetaccount'}. (3) IIS application pools: Get-WebConfiguration system.applicationHost/applicationPools/add | Where-Object {$_.processModel.userName -match 'svc-targetaccount'}. Run these across all domain-joined servers before any password rotation. Missing even one consumer causes a service failure after rotation.

How should I harden a service account to reduce Kerberoasting exposure after a compromise investigation?

Kerberoasting is only possible against accounts that have a Service Principal Name (SPN) registered and use RC4 encryption for their Kerberos service tickets. After investigating a Kerberoasted account, apply these hardening steps: first, migrate the account to a Group Managed Service Account (gMSA) -- gMSAs use a 120-character randomly generated password that Windows rotates automatically, making offline cracking infeasible. If a gMSA migration is not immediately possible, change the account password to a 25-plus character random string immediately, set AES-only Kerberos encryption on the account ('Set-ADUser -Identity svc-account -KerberosEncryptionType AES256,AES128' and disable RC4 by removing the flag), and set 'This account supports Kerberos AES 256 bit encryption' in the account's attribute editor. Remove any SPNs from the account that are not actively used -- each SPN is an independent Kerberoastable target. Monitor the account for new 4769 events with encryption type 0x17 (RC4) after hardening, which would indicate the AES enforcement failed to apply correctly.

Sources & references

  1. MITRE ATT&CK: Valid Accounts (T1078)
  2. Microsoft: Monitoring Service Accounts in AD
  3. NIST SP 800-61: Incident Response

Free resources

25
Free download

Critical CVE Reference Card 2025–2026

25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.

No spam. Unsubscribe anytime.

Free download

Ransomware Incident Response Playbook

Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.

No spam. Unsubscribe anytime.

Free newsletter

Get threat intel before your inbox does.

50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.

Unsubscribe anytime. We never sell your data.

Eric Bang
Author

Founder & Cybersecurity Evangelist, Decryption Digest

Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.

Black Hat Giveaway

Win a $2,495 Black Hat pass.

Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.

Joins Decryption Digest daily briefing. Unsubscribe anytime.

Giveaway: Black Hat USA 2026 Full-Access Pass ($2,495 value)

Details →
Daily Briefing

Subscribe to enter the giveaway

Every subscriber is automatically entered. You also get daily threat intel every morning: zero-days, ransomware, and nation-state campaigns. Free. No spam.

Already subscribed? You're already entered.

Giveaway

Win a $2,495 Black Hat USA 2026 pass.