Skip to content
ADscan Docs

GPO Abuse — Immediate Scheduled Task

Plant a SYSTEM-level Immediate Scheduled Task on every machine linked to a writable GPO, and roll it back cleanly.

ADscan implements GPO abuse through the Immediate Scheduled Task technique, natively — no New-GPOImmediateTask, no external subprocess. When a principal you control can write to a Group Policy Container, ADscan plants a computer-side Immediate Scheduled Task. At the next gpupdate /force, or the next 90-120 minute refresh, every machine linked to that GPO executes it as NT AUTHORITY\SYSTEM.

What the attack does

It exploits write access to a GPO — GenericAll, GenericWrite, WriteDACL, WriteOwner, or WriteProperty on gPCFileSysPath, gPCMachineExtensionNames or versionNumber. ADscan drops a ScheduledTasks.xml of type Immediate Task into SYSVOL, bumps the GPO version in both LDAP and gpt.ini, and lets each linked machine's Group Policy client execute it on its next cycle.

The privilege gained is not on one host. It is on every machine the GPO is linked to, at once.

When it applies

Preconditions:

  • A valid domain credential.
  • At least one Group Policy Container where the principal, or a group in its tokenGroups, holds one of:
    • GenericAll, GenericWrite, WriteDACL, WriteOwner
    • WriteProperty:gPCFileSysPath
    • WriteProperty:gPCMachineExtensionNames
    • WriteProperty:versionNumber
  • SMB access to the domain controller's SYSVOL share.

ADscan refuses the attack when the principal belongs to Protected Users. That group blocks RC4, NTLM and unconstrained delegation, and the vector cannot be completed reliably from it.

How ADscan runs it

  1. Discovery comes from the graph, not from a separate query. The native LDAP collector enumerates every groupPolicyContainer, reads nTSecurityDescriptor with SD_FLAGS_DACL, and persists each GPO as a kind="GPO" node in attack_graph.json — together with the ACL edges (GenericAll, GenericWrite, WriteDACL, WriteOwner), the MemberOf edges, and the GPLink edges to OUs, the domain and sites. The attack-path engine surfaces writable GPOs as findings automatically. The exploit-gpo-abuse wizard filters that graph (the principal's transitive membership, intersected with the relevant ACL relations) and issues no additional LDAP traffic.

  2. The wizard shows you the blast radius before you commit. A table with a risk column (CRITICAL when the GPO touches Tier 0), an impact panel listing the linked scopes of management, a red warning if it affects OU=Domain Controllers or the domain root, payload selection, and an optional dry run.

  3. The mutation is written through a ledger. Every step is recorded before it is applied:

    • mkdir \\<dc>\SYSVOL\<domain>\Policies\{GUID}\Machine\Preferences\ScheduledTasks\
    • Upload ScheduledTasks.xml — an Immediate Task running as NT AUTHORITY\SYSTEM.
    • Bump Version= in gpt.ini and versionNumber in LDAP (the machine half), and merge the client-side-extension GUIDs into gPCMachineExtensionNames.
  4. Rollback restores the original bytes. At session close, or when you run exploit-gpo-rollback, ADscan reverts SYSVOL byte for byte and restores the LDAP attributes to their prior values.

The whole stack is native and asynchronous: Kerberos, LDAP with automatic LDAPS-to-LDAP fallback, SMB for SYSVOL, and a native security-descriptor parser for the DACLs.

Impact

Command execution as NT AUTHORITY\SYSTEM on every machine linked to the GPO. Where the GPO is linked to an OU holding high-value servers, or to OU=Domain Controllers, the route to Domain Admin is immediate — which is why ADscan marks those GPOs CRITICAL and requires an explicit confirmation.

Rollback

ADscan reverts every SYSVOL and LDAP mutation, then verifies the resulting state matches the state before the attack: the original gpt.ini, the original versionNumber, the original gPCMachineExtensionNames, and no ScheduledTasks.xml. The ledger keeps the change_id values for audit even after the session ends.

If you leave the task in place in order to escalate to Domain Admin, the deferred rollback runs automatically when the session closes — unless you have already triggered it with exploit-gpo-rollback <ledger_id>.

Example session

adscan> exploit-gpo-abuse
[ Checking feasibility... ]
  Domain:   example.local
  User:     alice
  DC:       dc01.example.local (10.0.0.10)

[ Enumerating writable GPOs... ]

┌───┬──────────────────────────┬───────────────────────┬──────────┬────────┬────────┬──────────┐
│ # │ GPO                      │ Rights                │ Via      │ # SOMs │ Tier 0 │ Risk     │
├───┼──────────────────────────┼───────────────────────┼──────────┼────────┼────────┼──────────┤
│ 1 │ Workstation Hardening    │ GenericAll            │ S-1-...  │   2    │        │ MEDIUM   │
│ 2 │ Servers Baseline         │ WriteDACL             │ S-1-...  │   4    │        │ HIGH     │
│ 3 │ Default Domain Policy    │ GenericWrite          │ S-1-...  │   1    │   x    │ CRITICAL │
└───┴──────────────────────────┴───────────────────────┴──────────┴────────┴────────┴──────────┘

? Select the GPO to exploit:  Servers Baseline (4 SOMs, HIGH)

╭─ Impact preview ─────────────────────────────────────────────────╮
│ GPO: Servers Baseline                                            │
│ Linked SOMs:                                                     │
│   OU=Servers,DC=example,DC=local                                 │
│   OU=AppServers,OU=Servers,DC=example,DC=local                   │
│ Risk: HIGH                                                       │
╰──────────────────────────────────────────────────────────────────╯

? Continue? This modifies SYSVOL and LDAP on this DC.  Yes
? Select payload: Raw command (RawCommandPayload)
? Command: net localgroup administrators alice /add
? Dry-run first? Yes

[ DRY RUN — no mutations ]
  - LDAP versionNumber += 1, merge CSE GUIDs
  - mkdir \\dc01\SYSVOL\example.local\Policies\{...}\Machine\Preferences\ScheduledTasks
  - write ScheduledTasks.xml + gpt.ini bump

? Apply for real now? Yes
[ok] Immediate Scheduled Task planted (4 changes recorded, ledger id: ...).
? Run rollback now? No  -> deferred to session close.

To revert by hand:

adscan> exploit-gpo-rollback <ledger_id>

References

Find this useful?
Pass it to the next pentester running an AD engagement
Running 2+ AD engagements/year?
Get PRO free — beta access·Free in exchange for feedback
Automated PDF reports. Save ≥1 day per engagement.

ADscan — AD pentest automation for security consultants

GPO Abuse — Immediate Scheduled Task | ADscan