Skip to content

AD-GPP-002: Credentials in SYSVOL or NETLOGON scripts

high   Group Policy   MITRE ATT&CK T1552.001   Reads: SYSVOL (SMB)

A script in SYSVOL or NETLOGON appears to contain a literal password, and every domain user can read those shares.

Why it matters

Logon scripts, startup scripts and deployment helpers live in SYSVOL (under each GPO's folder) and in NETLOGON (SYSVOL\<domain>\scripts). Both shares are readable by every authenticated user, by design, because every computer and user has to run what is in them.

A script that maps a drive with net use ... /user:svc_backup P@ss..., builds a PSCredential from a plain-text string, or sets password= in an INI file hands that credential to anyone who logs on to the domain. Attackers grep these shares early in an intrusion (Snaffler, NetExec's spider_plus), because the accounts involved are often service accounts with broad rights, or the local administrator.

How ad-doctor detects it

ad-doctor connects to the SYSVOL share over SMB as the scanning user and walks <domain>\Policies and <domain>\scripts (NETLOGON). It reads files ending in .bat, .cmd, .ps1, .psm1, .vbs, .kix, .ini or .txt (except gpt.ini), the first 1 MB of each, skips blank and comment lines (#, ::, ', ;, rem), and reports the first pattern that matches on a line:

Kind What matches
net use with a password net use ... /user:NAME <literal>
plain-text SecureString ConvertTo-SecureString "<literal>" -AsPlainText
password argument -Password <literal>
password assignment password, passwd or pwd followed by = or : and a literal

A literal is at least three characters and does not start with %, $ or !, so %PASSWORD%, $pw and !pw! (variables) are not reported. The secret itself is never stored or printed: each object is the path relative to SYSVOL, the line number and the kind, for example scripts\map.bat:1 (net use with a password).

Attributes and probes read
  • SYSVOL Policies\ and scripts\ (NETLOGON): *.bat, *.cmd, *.ps1, *.psm1, *.vbs, *.kix, *.ini, *.txt

Severity

Always high: the credential is readable by every authenticated user, and logon and deployment scripts tend to carry the passwords of service or administrator accounts.

False positives and limits

  • Placeholders and examples such as password=CHANGEME in a template or a README saved as .txt. Remove them anyway or accept with an owner (../ci.md#accepted-risks).
  • Values that are not passwords after a pwd: or password= key (a hash, a file path, a prompt text).
  • Obfuscated or encoded credentials (Base64, a password split over variables) are not found; a clean result is not proof that SYSVOL holds no secrets.

Fix

  1. Open the file and line named in the finding, for example \\corp.example\NETLOGON\map.bat line 1, and identify the account.

  2. Change that account's password: any domain user could have read it.

  3. Remove the credential from the script. Map drives with the user's own identity, run startup tasks as SYSTEM or a gMSA, and keep secrets in a vault the script fetches from at run time with its own identity.

  4. Search for others the check cannot see (read-only):

    Get-ChildItem "\\$env:USERDNSDOMAIN\SYSVOL" -Recurse -File -ErrorAction SilentlyContinue |
        Select-String -Pattern 'password|passwd|pwd|/user:|AsPlainText' -List |
        Select-Object Path, LineNumber
    

Verify

ad-doctor scan dc01.corp.example --ldaps -u 'CORP\auditor' --only AD-GPP-002

To accept the risk instead, add an entry to the file passed with --accept (see accepted risks):

[[accept]]
check = "AD-GPP-002"
objects = ["..."]        # optional; without it the whole check is accepted
reason = "..."
expires = 2027-03-31

References

Microsoft and vendors

Research

Tools

MITRE ATT&CK

Available since 0.1.0.