ADTIER-005: SQL / Database Service Accounts in Privileged Groups

Platform
Active Directory
Category
Tier-0 Hygiene & Hybrid Identity Surface
Severity
High
Zero Trust pillar
Identity (weight 2)
Golden fixtures
3
Branch coverage
Observed: fixtures prove the verdicts they exercise
Provenance
baseline

What it checks

Guerrilla performs a paged LDAP search against the writable domain naming context and resolves the membership of the following privileged groups by SID (so renamed groups are still caught): - Domain Admins (S-1-5-21-<domain>-512) - Enterprise Admins (S-1-5-21-<root>-519) - Schema Admins (S-1-5-21-<root>-518) - Administrators (S-1-5-32-544) - Account Operators (S-1-5-32-548) - Server Operators (S-1-5-32-549) - Backup Operators (S-1-5-32-551) - Print Operators (S-1-5-32-550) Membership is resolved transitively using the LDAP_MATCHING_RULE_IN_CHAIN extended match (1.2.840.113556.1.4.1941) against tokenGroups, so nested group membership and primaryGroupID = 512 cases are both caught. For every user-class principal that lands in one of those groups, the check reads servicePrincipalName, sAMAccountName, description, userAccountControl, and msDS-ManagedPasswordId. A finding is raised when any of the following is true for a privileged-group member: - servicePrincipalName contains MSSQLSvc/, MSOLAPSvc.3/, MSSQL$<instance>, MySQL/, or postgres/ - sAMAccountName matches the regex ^(svc[_-])?(sql|mssql|mysql|postgres|oracle|db[_-]?admin|sa[_-]).* - description contains the literal strings "SQL Server service", "MySQL service", "PostgreSQL service", "Oracle service", or "Database service" - The account has a registered SPN of the form MSSQLSvc/<host>:1433 or MSSQLSvc/<host>:<instance> The check explicitly excludes gMSA principals (objectClass=msDS-GroupManagedServiceAccount) from the finding because gMSA accounts cannot be members of Domain Admins by Microsoft policy and are the recommended remediation target. It also excludes computer accounts (the LocalSystem-as-MACHINE$ case) because those are evaluated by ADTIER-002.

Why it matters

A SQL Server installation that selected "Use existing AD account" during setup and was handed a Domain Admin credential by the DBA team creates two compounding problems. First, the credential is loaded into the LSASS process and the SQL Server service process on every host the database runs on, which means any local administrator on that host (which routinely includes server-team operators, monitoring agents, backup software, and the DBAs themselves) can pull the cleartext or NT hash via mimikatz sekurlsa::logonpasswords, lsadump, or a procdump-then-offline-pypykatz pass. Second, the SPN registered against the account (MSSQLSvc/sql01.corp.contoso.com:1433) is queryable by any Domain Users principal, so the account is trivially discoverable as a Kerberoasting target. A weak password on a Domain Admin SQL service account is the canonical SpecterOps red-team finding. The blast radius is total. A Kerberoast against svc_sql with a crackable password yields Domain Admin. An unauthenticated SQL Server CVE (CVE-2020-0618 Reporting Services RCE, CVE-2021-1636 SQL Server elevation of privilege, CVE-2023-21528, the historical xp_cmdshell-as-sa abuses) executed against a database whose service runs as Domain Admin yields Domain Admin without any credential phase. A SQL injection in any application that connects to the database, when xp_cmdshell is enabled and the service account is privileged, yields the same result. The principle being violated is Tier-Zero containment: a Tier-1 application host (the database server) is being granted Tier-0 trust because the service account is a Tier-0 principal, and any compromise of the Tier-1 host therefore compromises Tier-0. Microsoft has stated since the Windows Server 2008 R2 era that SQL Server service accounts should never be members of any privileged Active Directory group and that the installer should provision per-instance virtual service accounts (NT SERVICE\MSSQLSERVER, NT SERVICE\MSSQL$INSTANCE) or, where domain authentication is needed, a Group Managed Service Account. The persistence of Domain Admin SQL accounts in audited environments is almost always traceable to a single DBA who needed the service to "just work" against a clustered file share or a linked-server topology and reached for the highest-privilege account that would solve the immediate problem.

Attack path

1. Discovery: the attacker authenticates with any Domain Users credential and enumerates privileged group membership. With PowerView: Get-DomainGroupMember -Identity "Domain Admins" -Recurse | Select MemberName, MemberObjectClass Or natively: Get-ADGroupMember -Identity "Domain Admins" -Recursive | Where-Object objectClass -eq user 2. SPN filtering: the attacker reads servicePrincipalName on every privileged-group user member and keeps those with database SPNs. Get-DomainUser -SPN -LDAPFilter "(servicePrincipalName=MSSQLSvc/*)" | Where-Object { (Get-DomainGroupMember "Domain Admins" -Recurse).MemberSID -contains $_.objectSid } 3. Kerberoast: the attacker requests a TGS for the SQL SPN using any Domain Users TGT. The TGS is encrypted with the service account NT hash, which is the password hash for svc_sql. Rubeus.exe kerberoast /user:svc_sql /outfile:hashes.txt /nowrap 4. Offline crack: hashcat mode 13100 against rockyou.txt with corp-specific rules cracks the typical "CompanyName2024!" or "SQLAdmin#1" password within minutes on a single GPU. The 2023 Verizon DBIR notes that 86 percent of cracked Kerberoast hashes fall to rules-based attacks against common wordlists. hashcat -m 13100 -a 0 hashes.txt rockyou.txt -r OneRuleToRuleThemAll.rule 5. Pass-the-ticket or direct logon: armed with the cleartext, the attacker authenticates as svc_sql, which is now a Domain Admin. From any joined workstation: runas /netonly /user:CORP\svc_sql cmd.exe net group "Domain Admins" attacker /add /domain 6. Alternate path without cracking: the attacker pivots to the SQL host through any of: a SQL injection in a connected application, an unauthenticated SQL Server CVE, an authenticated SQL login plus xp_cmdshell, or a Reporting Services exposure. Once they have OS execution on the SQL host as the database service account, they already have Domain Admin and skip steps 3 and 4 entirely. 7. LSASS extraction (no-Kerberoast path): if the attacker reaches the SQL host as local admin by any other route (RDP, WMI, PsExec, vulnerable backup agent), they dump LSASS and extract the cleartext or NT hash of the running service account: procdump.exe -accepteula -ma lsass.exe lsass.dmp pypykatz lsa minidump lsass.dmp 8. Persistence: with Domain Admin in hand, the attacker performs a DCSync (T1003.006) against any DC to harvest krbtgt and all account hashes, forges a Golden Ticket, and is now resident at Tier-0 indefinitely.

How Guerrilla assesses it

Guerrilla uses System.DirectoryServices.Protocols to bind to the domain root, reads RootDSE -> defaultNamingContext, then issues a single paged LDAP search per privileged group using the LDAP_MATCHING_RULE_IN_CHAIN OID (1.2.840.113556.1.4.1941) against the member attribute. That extended match returns every transitive member in one round trip, including nested groups and primaryGroupID = 512 attachments that a naive member-attribute walk would miss. For each returned user-class principal, the check pulls sAMAccountName, servicePrincipalName, description, userAccountControl, objectClass, msDS-SupportedEncryptionTypes, and adminCount in a single attribute list to minimize wire chatter. Classification of "database service account" is performed against three signals (any one is sufficient): SPN prefix match against the regex ^(MSSQLSvc|MSOLAPSvc\.3|MySQL|postgres)\/, sAMAccountName regex match, or a description-field literal match. The check is read-only, requires only Domain Users membership, and runs against any reachable DC. False-positive suppression is applied to gMSA accounts (objectClass contains msDS-GroupManagedServiceAccount) and to computer accounts. The finding severity is escalated to Critical when the matched account also has a Kerberoastable SPN and the account password has not changed in the past 365 days (read from pwdLastSet), because that combination converts a misconfiguration into an immediately exploitable Kerberoast target.

Recommended value

Database service accounts run as gMSAs or dedicated service identities with no privileged-group membership.

Remediation

Migrate SQL service accounts to gMSA where supported. For accounts that must remain user-based, remove privileged group membership and grant only the local rights the database engine requires (Log on as a service, Bypass traverse checking, Adjust memory quotas for a process — see Microsoft docs for the specific rights).

Fixture-proven verdicts

Every verdict below is proven by a golden fixture in the module's gating test suite. This table derives from the last green run; it cannot be edited by hand.

Verdict scenarios for ADTIER-005
ScenarioExpected verdict
cleanPASS
known-badFAIL
throttledNot Assessed

Framework mappings

NIST SP 800-53
AC-6
CIS AD Benchmark
8.2.4
MITRE ATT&CK
T1078.002