TL;DR
- Done as a normal user without admin privs
- Change %TEMP%/%TMP% to point to a location that allows execution of scripts defined by AppLocker
- Start Powershell with the new environment variables that you set for %TEMP%/%TMP% and profit!
Background
This blogpost covers a technique I discovered when digging further into AppLocker to bypass Powershell Constrained Language Mode. The issue has been reported to Microsoft, but they don’t want to service this and does not consider this to meet the bar for servicing. The discovery was made while I was looking at the event log while starting Powershell. I noticed that it always triggers two alerts saying that it cannot execute:
It triggers an alert for a randomly generated .PSM1 file as well as a .PS1 file. The files are randomly built and the structure is like this:
__PSSCRIPTPOLICYTEST_<8 random chars>.<3 random chars>.PS1
__PSSCRIPTPOLICYTEST_<8 random chars>.<3 random chars>.PSM1
When you look at the alert you will notice that it tried to execute these files in the %temp% folder of the user and of course this is not allowed by AppLocker rules unless you specifically defined it.
I also captured the files to look at the content. I used a simple loop script to capture this using Powershell.
while($true) { Copy-Item -Path $("$env:temp\*.ps*") -Destination C:\temp }
This is what the contents looks like (they both contained the same):
After thinking a while on how to bypass this check I came up with an idea that I would pre-create every possible .PS1 and .PSM1 file and use hardlink to a place that allowed execution. After figuring out how many files that would actually mean to create I gave up that idea pretty fast. I then thought that maybe Powershell reads the variables to the temporary directory from the users environment variables, and it turned out that is was exactly what it did.
The bypass
Knowing that I can change the %TEMP% and %TMP% variables in the registry under HKCU\Environment, I decided to try that first. Changing it is easy by just opening regedit and change the values. In my test I used the default AppLocker rules and these allows execution of scripts from C:\Windows\*, so I decided that I would change the %TEMP% and %TMP% to point to c:\windows\temp since I know that the user has write access to that folder. Remember that if a user has write access to a path that allows scripts to execute he can plant his PS1 script in that folder and execute it in Full Language Mode.
After I changed the registry values I started a new Powershell window. To my big surprise it did not catch up the new environment variables.
After a little Googling I found that you can use “Start-Process” with the parameter -UseNewEnvironment. This worked for most processes, but Powershell was not able to launch this way. It pops a window really quick and closes. Okay, back to the drawing board….
After some experimenting I remembered that you can use WMIC to spawn processes and you can use WMI commands from Powershell. I decided to try that for a spin and that worked.
The script I ended up using was this:
$CurrTemp = $env:temp $CurrTmp = $env:tmp $TEMPBypassPath = "C:\windows\temp" $TMPBypassPath = "C:\windows\temp" Set-ItemProperty -Path 'hkcu:\Environment' -Name Tmp -Value "$TEMPBypassPath" Set-ItemProperty -Path 'hkcu:\Environment' -Name Temp -Value "$TMPBypassPath" Invoke-WmiMethod -Class win32_process -Name create -ArgumentList "powershell" sleep 5 #Set it back Set-ItemProperty -Path 'hkcu:\Environment' -Name Tmp -Value $CurrTmp Set-ItemProperty -Path 'hkcu:\Environment' -Name Temp -Value $CurrTemp
The command that helped me was the <Invoke-WmiMethod -Class win32_process -Name create -ArgumentList “powershell”> command. When this script is launched from a constrained language mode it will launch a new Powershell window that has full language mode. This is shown in the GIF here:
I found that this was really cool and decided to write a function for it to my PowerAL project. (PowerAppLocker)
While I was writing the code for it I discovered a even better way of doing this that did not require changing the user environment variables, thanks to Matt Graeber and his awesome blogpost here:
https://posts.specterops.io/bypassing-application-whitelisting-with-runscripthelper-exe-1906923658fc
In the post he shows how you can start a process from Win32_Process and supply your own defined environment variables. And that was exactly what I needed.
My raw code for the function here (The code is wrapped in function that does some more stuff in the PowerAL module. The module can be found here: https://github.com/api0cradle/PowerAL ):
#Path to Powershell $CMDLine = "$PSHOME\powershell.exe" #Getting existing env vars [String[]] $EnvVarsExceptTemp = Get-ChildItem Env:\* -Exclude "TEMP","TMP"| % { "$($_.Name)=$($_.Value)" } #Custom TEMP and TMP $TEMPBypassPath = "Temp=C:\windows\temp" $TMPBypassPath = "TMP=C:\windows\temp" #Add the to the list of vars $EnvVarsExceptTemp += $TEMPBypassPath $EnvVarsExceptTemp += $TMPBypassPath #Define the start params $StartParamProperties = @{ EnvironmentVariables = $EnvVarsExceptTemp } $StartParams = New-CimInstance -ClassName Win32_ProcessStartup -ClientOnly -Property $StartParamProperties #Start a new powershell using the new params Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{ CommandLine = $CMDLine ProcessStartupInformation = $StartParams }
Prevention
How do you prevent someone from exploiting this?
You have to define the scripts rules in such a way that it does not allow execution of scripts from a location where the user has write access. Meaning that if you allow c:\windows\* you need to exclude the folders where the user can write to. For instance C:\windows\temp or C:\windows\tracing. To get the complete list of folders the user can write to I recommend running Accesschk. I have a pre-made batch script that should help you discover writeable folders here:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
accesschk -w -s -q -u Users "C:\Program Files" >> programfiles.txt | |
accesschk -w -s -q -u Everyone "C:\Program Files" >> programfiles.txt | |
accesschk -w -s -q -u "Authenticated Users" "C:\Program Files" >> programfiles.txt | |
accesschk -w -s -q -u Interactive "C:\Program Files" >> programfiles.txt | |
accesschk -w -s -q -u "This Organization" "C:\Program Files" >> programfiles.txt | |
accesschk -w -s -q -u "Authentication authority asserted identity" "C:\Program Files" >> programfiles.txt | |
accesschk -w -s -q -u "Mandatory Label\Medium Mandatory Level" "C:\Program Files" >> programfiles.txt | |
accesschk -w -s -q -u %username% "C:\Program Files" >> programfiles.txt | |
accesschk -w -s -q -u Users "C:\Program Files (x86)" >> programfilesx86.txt | |
accesschk -w -s -q -u Everyone "C:\Program Files (x86)" >> programfilesx86.txt | |
accesschk -w -s -q -u "Authenticated Users" "C:\Program Files (x86)" >> programfilesx86.txt | |
accesschk -w -s -q -u Interactive "C:\Program Files (x86)" >> programfilesx86.txt | |
accesschk -w -s -q -u "This Organization" "C:\Program Files (x86)" >> programfilesx86.txt | |
accesschk -w -s -q -u "Authentication authority asserted identity" "C:\Program Files (x86)" >> programfilesx86.txt | |
accesschk -w -s -q -u "Mandatory Label\Medium Mandatory Level" "C:\Program Files (x86)" >> programfilesx86.txt | |
accesschk -w -s -q -u %username% "C:\Program Files (x86)" >> programfilesx86.txt | |
accesschk -w -s -q -u Users "C:\Windows" >> windows.txt | |
accesschk -w -s -q -u Everyone "C:\Windows" >> windows.txt | |
accesschk -w -s -q -u "Authenticated Users" "C:\Windows" >> windows.txt | |
accesschk -w -s -q -u Interactive "C:\Windows" >> windows.txt | |
accesschk -w -s -q -u "This Organization" "C:\Windows" >> windows.txt | |
accesschk -w -s -q -u "Authentication authority asserted identity" "C:\Windows" >> windows.txt | |
accesschk -w -s -q -u "Mandatory Label\Medium Mandatory Level" "C:\Windows" >> windows.txt | |
accesschk -w -s -q -u %username% "C:\Windows" >> windows.txt |
That’s it. Hope you enjoyed this blogpost and feedback is always welcome.
1. If you have an AppLocker Path rule that grants a non-admin the ability to run anything in a user-writable directory, you don’t have whitelisting. “AaronLocker” creates exclusions for all the user-writable subdirectories of the Windows and Program Files directories, and it is much more comprehensive in its definition of “user-writable” than your samples are. (BTW, “AaronLocker” also blocks non-admins from running wmic.exe, mshta.exe, and a few other built-in tools.)
2. If you need an easy way to get PowerShell to put its random-named policy-test script in a different location, just do this from a CMD.EXE:
SET TMP=C:\TEMP
PowerShell.exe
LikeLike
This article has much inspired and helped! Thank you very much for sharing.
Btw, Malwarebytes blocks this page saying a Trojan is contained which is hopefully not the case.
LikeLike