GETPATHS.CMD und AppLocker am WTS

GETPATHS.CMD wird mit aktiviertem AppLocker am Terminalserver geblockt.
Microsoft-Windows-AppLocker/MSI and Script: %OSDRIVE%\USERS\ADM-DOMAIN\APPDATA\LOCAL\TEMP\2\GETPATHS.CMD was prevented from running.

Das „GETPATHS.CMD“ Script wird bei jeder Anmeldung am Terminalserver neu im %TEMP% Verzeichnis generiert und mit aktiviertem AppLocker direkt an der Ausführung gehindert.

Wo kommt das Script „GETPATHS.CMD“ her und was macht es? Während des Anmeldevorgangs am WTS wird die Batchdatei „USERLOGON.CMD“ aus „%WinDir%\System32“ aufgerufen („HKEY_LOCAL_MACHINE\ Software \Microsoft \Windows NT \CurrentVersion \Winlogon \Appsetup“ (Set up logon script only for Terminal Server users – Windows Server | Microsoft Learn)). Die „USERLOGON.CMD“ startet dann unter anderem das Script „SETPATHS.CMD“ aus dem Ordner „Application Compatibility Scripts“. In diesem Script wird die „ACRegL.exe“ mit verschiedenen Parametern aufgerufen, welche dann letztlich die „GETPATHS.CMD“ im %TEMP% des Benutzers erstellt, anschließend aufruft und danach wieder löscht. Startet man die „ACRegL.exe“ samt der Parameter, kann man sich das Script ansehen:

Aufruf ACRegL.exe:

"%systemroot%\Application Compatibility Scripts\ACRegL.exe" "%TEMP%\getpaths.cmd" COMMON_PATHS "HKLM\Software" "" GETPATHS

%TEMP%\GETPATHS.CMD:

SET COMMON_START_MENU=C:\ProgramData\Microsoft\Windows\Start Menu
SET COMMON_STARTUP=C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup
SET COMMON_PROGRAMS=C:\ProgramData\Microsoft\Windows\Start Menu\Programs
SET USER_START_MENU=C:\Users\<Benutzer>\AppData\Roaming\Microsoft\Windows\Start Menu
SET USER_STARTUP=C:\Users\<Benutzer>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
SET USER_PROGRAMS=C:\Users\<Benutzer>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs
SET MY_DOCUMENTS=Documents
SET TEMPLATES=Templates
SET APP_DATA=Roaming

Application Compatibility Scripts?! Die Application Compatibility Scripts stammen ungefähr aus der Zeit, als Gummistiefel noch aus Holz waren. 😉 Gebraucht wurden die Scripte „damals“, um Anwendungen Multi-User – also terminalserverfähig – zu machen.

Welches Problem gibt es jetzt mit AppLocker? Mit den Standard Regeln wird das Ausführen von Scripts im %TEMP% der User nicht erlaubt. Das hat auch durchaus Sinn! Da wo User Schreibrechte haben und somit Dateien erstellen können, sollten die User definitiv keine Scripte / Anwendungen ausführen dürfen.

Aber in der Fehlermeldung aus der Ereignisanzeige („%OSDRIVE%\USERS\ADM-DOMAIN\APPDATA\LOCAL\TEMP\2\GETPATHS.CMD“) steht doch schon die Lösung? Nein! Die AppLocker Regel für Scripts müsste dann mindestens folgendermaßen aussehen: „%OSDRIVE%\USERS\*\APPDATA\LOCAL\TEMP\*\GETPATHS.CMD“. Damit wäre es leider jederzeit möglich eine Datei „GETPATHS.CMD“ in %TEMP% zu platzieren und auszuführen. Das ist eher unschön.

Und jetzt? Einfach blocken. Zumindest gab es in meinen ersten Tests keine Probleme. Allerdings gibt es dann bei jeder Anmeldung eines Benutzers einen entsprechenden Error in der Ereignisanzeige, was ebenfalls sehr unschön ist.

Log Name: Microsoft-Windows-AppLocker/MSI and Script
Source: Microsoft-Windows-AppLocker
Date: 21.12.2022 15:08:45
Event ID: 8007
Task Category: None
Level: Error
Keywords:
User: AD\adm-domain
Computer: JansClient01.ad.adsvc.org
Description:
%OSDRIVE%\USERS\ADM-DOMAIN\APPDATA\LOCAL\TEMP\2\GETPATHS.CMD was prevented from running.

Windows Ereignisanzeige „Microsoft-Windows-AppLocker/MSI and Script“ mit der Event ID 8007

Und jetzt in schön(er)? Passen wir die „SETPATHS.CMD“ per PowerShell Script so an, dass die „GETPATHS.CMD“ nicht mehr ausgeführt wird und die Variablen dennoch gesetzt werden. 😎

function Edit-SetPath{
    param(
        [Parameter(Mandatory=$true, Position=0)]
        [ValidateNotNullOrEmpty()]
        [string]$Path
    )

    $ACL = Get-ACL -Path $Path
    $oldOwner = New-Object System.Security.Principal.NTAccount(
        "NT SERVICE", "TrustedInstaller"
    )
    $newOwner = New-Object System.Security.Principal.NTAccount(
        "NT AUTHORITY", "SYSTEM"
    )
    $tmpAR = New-Object System.Security.AccessControl.FileSystemAccessRule(
        "NT AUTHORITY\SYSTEM",
        "Modify",
        "Allow"
    )
    $oldAR = New-Object System.Security.AccessControl.FileSystemAccessRule(
        "NT AUTHORITY\SYSTEM",
        "ReadandExecute",
        "Allow"
    )

    $ACL.SetOwner($newOwner)
    $ACL.SetAccessRule($tmpAR)
    Set-Acl -Path $Path `
        -AclObject $ACL

    $SPContent = [System.IO.StreamReader]::new($Path)
    while($SPContent.EndOfStream -eq $false) {
        $line = $SPContent.ReadLine()
        if($line -eq "Call `"%TEMP%\getpaths.cmd`""){
            $line = -join("REM ", $line, "`n")
            $line += "for /f `"tokens=*`" %%i in ('type `"%TEMP%\getpaths.cmd`"') do %%i"
        }
        $newSPContent += -join($line, "`n")
    }
    $SPContent.Close()

    Set-Content -Path $Path `
        -Value $newSPContent

    $ACL.SetOwner($oldOwner)
    $ACL.RemoveAccessRule($tmpAR)
    $ACL.SetAccessRule($oldAR)
    Set-Acl -Path $Path `
        -AclObject $ACL
}

$LogFile = "C:\_install\GetPathLog.txt"
$Path = Join-Path -Path $env:windir `
    -ChildPath "application compatibility scripts\SETPATHS.CMD"

if(Test-Path -Path $Path -PathType Leaf){
    $objSPCMD = Get-Item -Path $Path
    $objOS = Get-CimInstance -Query "SELECT Caption from Win32_OperatingSystem"

    # LastWriteTime.Year:
    # 2018 -> Windows Server 2019
    # 2021 -> Windows Server 2022
    # 2024 -> Windows Server 2025
    # Wird nur einmalig angewendet, falls MS die SETPATHS.CMD mal ändern sollte

    if($objSPCMD.LastWriteTime.Year -eq "2018" -and $objOS.Caption -match "Server 2019"){
        Edit-SetPath -Path $Path
    } elseif($objSPCMD.LastWriteTime.Year -eq "2021" -and $objOS.Caption -match "Server 2022"){
        Edit-SetPath -Path $Path
    } elseif($objSPCMD.LastWriteTime.Year -eq "2024" -and $objOS.Caption -match "Server 2025"){
        Edit-SetPath -Path $Path
    } else{
        Add-Content -Path $LogFile `
            -Value $("{0}: Bitte AppLocker-SETPATHSCMD.ps1 prüfen!" -f $(Get-Date)) `
            -Confirm:$false `
            -Force
    }
}

Was macht das Script und wie wird es gestartet?

  1. Der Pfad zur „SETPATHS.CMD“ wird erstellt, die derzeitige ACL des Files wird ausgelesen und die benötigten Rechte werden definiert
  2. Es wird geprüft, ob es sich noch um die original Datei von Windows Server 2019, 2022 oder 2025 handelt
  3. Die ACL wird angepasst und der Besitz vom TrustedInstaller wird auf SYSTEM geändert und SYSTEM bekommt ändern Rechte auf die Datei
  4. Die Datei wird zeilenweise eingelesen und der Aufruf der „GETPATHS.CMD“ wird auskommentiert
  5. Eine neue Zeile wird eingefügt, welche bewirkt, dass die „GETPATHS.CMD“ mit dem type-Befehl ausgegeben wird, wodurch die SET Befehle aus der „GETPAHTS.CMD“ (siehe oben) ausgeführt werden
  6. Die Datei „SETPAHTS.CMD“ wird geschlossen und der neue Inhalt geschrieben
  7. Die geänderten Berechtigungen werden rückgängig gemacht, damit der TrustedInstaller wieder Besitzer ist und SYSTEM die Datei nur noch Lesen und Ausführen darf

Das Script kann jetzt beispielsweise mit einem geplanten Task per Gruppenrichtlinien auf die Remote Desktop Session Hosts gebracht und dort mit dem Trigger „Bei Systemstart“ ausgeführt werden. Alternativ wäre auch ein Computer-StartUp-Script oder eine der vielen Lösungen zur Softwareverteilung denkbar. Möchte man das Script „nur“ im Golden Image / PVS Image ausführen, muss in der FileSystemAccessRule „$tmpAR“ „NT AUTHORITY“ durch die Domäne und „SYSTEM“ durch den User geändert werden. Oder man macht es kurz händisch und kommentiert Zeile 34 durch ein „REM “ am Anfang aus und fügt in die nächste Zeile folgendes „for /f „tokens=*“ %%i in (‚type „%TEMP%\getpaths.cmd“‚) do %%i“ ein. Dann sollte die „SETPAHTS.CMD“ so aussehen:

...
:Cont1
REM Call "%TEMP%\getpaths.cmd"
for /f "tokens=*" %%i in ('type "%TEMP%\getpaths.cmd"') do %%i
Del "%TEMP%\getpaths.cmd" >Nul: 2>&amp;1
...

Die gesuchte Lösung noch nicht gefunden oder benötigen Sie Hilfe bei anderen Themen aus meinem Blog? Nehmen Sie gerne Kontakt mit mir bzw. meinem Unternehmen Jan Mischo IT auf. Ich freue mich auf Ihre Anfrage: https://janmischo.it/kontakt/


+49 2801 7004300

info@janmischo.it

Kommentare

6 Antworten zu „GETPATHS.CMD und AppLocker am WTS“

  1. Sascha

    Moin Jan,

    haben wir so an unseren Citrix Servern vor einem Jahr umgesetzt. Heute ist mir bei einen SFC /scannow aufgefallen dass die Änderung aber SFC Fehler wirft. Schon mal gesehen? Hier der Auszug ausm CBS Log

    2025-02-03 19:11:23, Info CSI 00006527 [SR] Beginning Verify and Repair transaction
    2025-02-03 19:11:23, Info CSI 00006529 [SR] Cannot repair member file [l:12]’SETPATHS.CMD‘ of Microsoft-Windows-TerminalServices-AppCompatTools, version 10.0.17763.1, arch amd64, nonSxS, pkt {l:8 b:31bf3856ad364e35} in the store, hash mismatch
    2025-02-03 19:11:23, Info CSI 0000652c [SR] Cannot repair member file [l:12]’SETPATHS.CMD‘ of Microsoft-Windows-TerminalServices-AppCompatTools, version 10.0.17763.1, arch amd64, nonSxS, pkt {l:8 b:31bf3856ad364e35} in the store, hash mismatch
    2025-02-03 19:11:23, Info CSI 0000652d [SR] This component was referenced by [l:126]’Microsoft-Windows-TerminalServices-AppCompat-Opt-Package~31bf3856ad364e35~amd64~~10.0.17763.1.e580eaad627ae6c068d348704d8e24ba‘
    2025-02-03 19:11:23, Info CSI 00006530 [SR] Could not reproject corrupted file \??\C:\Windows\Application Compatibility Scripts\\SETPATHS.CMD; source file in store is also corrupted
    2025-02-03 19:11:23, Info CSI 00006532 [SR] Repair complet

    1. Hi Sascha,

      ist mir noch nicht aufgefallen, habe aber auch tatsächlich noch nicht danach gesucht. 😉 Ich würde an der Stelle aber tatsächlich damit leben und die Fehler im CBS Log ignorieren.

      HTH
      Jan

  2. CBo

    Hi Jan,
    At first I thought this idea is genius.
    However, if an attacker manages to write a malicious version of getpath.cmd and then starts the modified setpath.cmd, the whole getpath-script still gets executed.

    Kind regards,

    1. Hey CBo,

      thanks for commenting on this one. Of course that’s a very valid concern but if the attacker manages to write to „C:\Windows\application compatibility scripts“ IMO it’s likely he is also able to write to „C:\Windows“ and other protected paths. In that case there should be no need to modify getpath.cmd for doing (other) bad stuff. I guess some sort of best practices could/would be to deploy fresh Sessions Hosts / Worker (from hardened images) and put them hardened in production. When implementing AppLocker in an already running environment there is always risk of having been compromised before.

      Best regards,
      Jan

  3. CBo

    Hi Jan,
    Thanks for your quick reply.

    What I tried to explain is that in my view,
    – the attacker only needs to write to getpaths.cmd, which is in the personal temp-folder of the user.
    – And that your modification of the setpath.cmd-script actually makes sure that all commands in the (now malicious) getpath.cmd still get executed.
    In this way, there is no need for the attacker to be able to modify the setpath-script, which is indeed protected by the NTFS-permissions on the C:\Windows\…-folders.

    Or I may misunderstand your solution.

    Kind Regards,

    1. Hi CBo,

      again thank you for your great comment!

      Got it! You are absolutely right. In that case this line („“%systemroot%\Application Compatibility Scripts\ACRegL.exe“ „%TEMP%\getpaths.cmd“ COMMON_PATHS „HKLM\Software“ „“ GETPATHS“) of „SETPATHS.CMD“ should overwrite / create a new „%TEMP%GETPATHS.CMD“. I gave it a quick try and created a „getpaths.cmd“ in the users %temp% and set „write deny“ to it. Then SETPATHS.CMD will fail:
      Echo Unable to retrieve common or user paths.
      ...
      Goto Failure
      ...
      Echo One or more queries for the common or user paths have failed!
      One or more queries for the common or user paths have failed!
      ...
      Echo Applications relying on this script may not install successfully.
      Applications relying on this script may not install successfully.
      ...
      Echo Please resolve the problem and try again.
      Please resolve the problem and try again.

      But I will give it some more testing! With a good timing it could be possible to place a modified GETPATS.CMD in %temp% to get it run.

      Best regards,
      Jan

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Seite verwendet Akismet, um Spam zu reduzieren. Erfahre, wie deine Kommentardaten verarbeitet werden..