BypassUAC的研究和思路
基礎知識
用戶帳戶控制(User Account Control)是Windows Vista(及更高版本操作系統)中一組新的基礎結構技術,可以幫助阻止惡意程序(有時也稱為“惡意軟件”)損壞系統,同時也可以幫助組織部署更易于管理的平臺。
使用UAC,應用程序和任務總是在非管理員帳戶的安全上下文中運行,但管理員專門給系統授予管理員級別的訪問權限時除外。UAC會阻止未經授權應用程序的自動安裝,防止無意中對系統設置進行更改。
用戶帳戶控制(UAC)是新版Windows的核心安全功能,也是其最常被人誤解的眾多安全功能當中的一種。

而在這些程序里面,有的需要授權、有的不需要,是因為UAC是分授權等級的
首先請按Win+R,輸入gpedit.msc,打開組策略。
然后我們在左側窗口找到“計算機配置–Windows設置–安全設置–本地策略–安全選項”,再在右側窗口找到“用戶帳戶控制:管理員批準模式中管理員的提升權限提示的行為”,雙擊該條目,打開設置窗口,如下圖:

不提示直接提升:關閉UAC,需要權限時直接提升權限。
在安全桌面上提示憑據:需要權限時在安全桌面上輸入管理員密碼提升權限。
在安全桌面上同意提示:需要權限時在安全桌面上選擇“允許”提升權限。
提示憑據:需要權限時在普通窗口中輸入管理員密碼提升權限。
同意提示:需要權限時在普通窗口中選擇“允許”提升權限。
非 Windows 二進制文件的同意提示:(默認設置)當非 Microsoft 應用程序的某個操作需要提升權限時,選擇“允許”提升權限。
因為普通應用執行權限有限,某些操作必然會要求更高的管理員權限。此時,通常就需要一個權限提升的操作。程序可以向系統請求提權,系統會將此請求通過提一個提示框,請用戶確認。
如果當前用戶的用戶組權限不是管理員,提權操作是要求輸入管理員密碼的,這點和在Linux中的相應操作類似。
?程序只能在運行前要求提權。如果已經在運行了,那么將失去申請提權的能力
?權限提升僅對此次進程有效
提升權限的操作大致有兩個:
?自動提權請求
?手動提權請求
手動提權就是“以管理員身份運行”,自動提權請求就是程序本身就一運行就開始申請權限,如:注冊表編輯器
在開發的過程中,程序員若要開發一個程序,可以在編譯器配置,寫入一個配置文件,用于向系統標識該應用程序是必須要管理員權限運行的。
visual studio里面的uac
在visual studio里面有一個manifest文件,這個文件本質上是一個xml文件,用于標識當前應用程序的配置屬性。其中這幾個級別明細如下
?aslnvoker 默認權限
?highestAvailable 最高權限
?requireAdministrator 必須是管理員權限

將編譯選項調整為requireAdministrator,則當用戶運行程序后,將獲得管理員權限會話,不需要繞過UAC。
挖掘白名單的uac程序
有一些系統程序是會直接獲取管理員權限同時不彈出UAC彈窗,這類程序被稱為白名單程序。這些程序擁有autoElevate屬性的值為True,會在啟動時就靜默提升權限。
那么我們要尋找的uac程序需要符合以下幾個要求:
1. 程序的manifest標識的配置屬性 autoElevate 為true 2. 程序不彈出UAC彈窗 3. 從注冊表里查詢Shell\Open\command鍵值對
首先是尋找autoElevate為true的程序,這里就寫一個py腳本去批量跑一下,這里就找system32目錄下面的
import os
from subprocess import *
path = 'c:\windows\system32'
files = os.listdir(path)
print(files)
def GetFileList(path, fileList):
newDir = path
if os.path.isfile(path):
if path[-4:] == '.exe':
fileList.append(path)
elif os.path.isdir(path):
try:
for s in os.listdir(path):
newDir=os.path.join(path,s)
GetFileList(newDir, fileList)
except Exception as e:
pass
return fileList
files = GetFileList(path, [])
print(files)
for eachFile in files:
if eachFile[-4:] == '.exe':
command = r'.\sigcheck64.exe -m {} | findstr auto'.format(eachFile)
print(command)
p1 = Popen(command, shell=True, stdin=PIPE, stdout=PIPE)
if 'true' in p1.stdout.read().decode('gb2312'):
copy_command = r'copy {} .\success'.format(eachFile)
Popen(copy_command, shell=True, stdin=PIPE, stdout=PIPE)
print('[+] {}'.format(eachFile))
with open('success.txt', 'at') as f:
f.writelines('{}'.format(eachFile))

整理之后exe如下所示
c:\windows\system32\bthudtask.exe c:\windows\system32\changepk.exe c:\windows\system32\ComputerDefaults.exe c:\windows\system32\dccw.exe c:\windows\system32\dcomcnfg.exe c:\windows\system32\DeviceEject.exe c:\windows\system32\DeviceProperties.exe c:\windows\system32\djoin.exe c:\windows\system32\easinvoker.exe c:\windows\system32\EASPolicyManagerBrokerHost.exe c:\windows\system32\eudcedit.exe c:\windows\system32\eventvwr.exe c:\windows\system32\fodhelper.exe c:\windows\system32\fsquirt.exe c:\windows\system32\FXSUNATD.exe c:\windows\system32\immersivetpmvscmgrsvr.exe c:\windows\system32\iscsicli.exe c:\windows\system32\iscsicpl.exe c:\windows\system32\lpksetup.exe c:\windows\system32\MSchedExe.exe c:\windows\system32\msconfig.exe c:\windows\system32\msra.exe c:\windows\system32\MultiDigiMon.exe c:\windows\system32ewdev.exe c:\windows\system32\odbcad32.exe c:\windows\system32\PasswordOnWakeSettingFlyout.exe c:\windows\system32\pwcreator.exe c:\windows\system32\rdpshell.exe c:\windows\system32\recdisc.exe c:\windows\system32\rrinstaller.exe c:\windows\system32\shrpubw.exe c:\windows\system32\slui.exe c:\windows\system32\Sysprep\sysprep.exe c:\windows\system32\SystemPropertiesAdvanced.exe c:\windows\system32\SystemPropertiesComputerName.exe c:\windows\system32\SystemPropertiesDataExecutionPrevention.exe c:\windows\system32\SystemPropertiesHardware.exe c:\windows\system32\SystemPropertiesPerformance.exe c:\windows\system32\SystemPropertiesProtection.exe c:\windows\system32\SystemPropertiesRemote.exe c:\windows\system32\SystemSettingsAdminFlows.exe c:\windows\system32\SystemSettingsRemoveDevice.exe c:\windows\system32\Taskmgr.exe c:\windows\system32\tcmsetup.exe c:\windows\system32\TpmInit.exe c:\windows\system32\WindowsUpdateElevatedInstaller.exe c:\windows\system32\WSReset.exe c:\windows\system32\wusa.exe
然后再去尋找不彈uac框的程序,這里我就從上往下開始嘗試,找到的是ComputerDefaults.exe 這個exe

運行之后直接就會出現默認應用這個界面

uac程序特性探究
通常以shell\open\command命名的鍵值對存儲的是可執行文件的路徑,如果exe程序運行的時候找到該鍵值對,就會運行該鍵值對的程序,而因為exe運行的時候是靜默提升了權限,所以運行的該鍵值對的程序就已經過了uac。所以我們把惡意的exe路徑寫入該鍵值對,那么就能夠過uac執行我們的惡意exe。
這里使用proc監聽ComputerDefaults.exe

發現他會去查詢HKCU\Software\Classes\ms-settings\Shell\Open\command里面的值

那么我們創建一個HKCU\Software\Classes\ms-settings\Shell\Open\command路徑,再對ComputerDefaults.exe進行監聽嘗試

然后發現他還會去查詢HKCU\Software\Classes\ms-settings\Shell\Open\command\DelegateExecute,而且Result顯示的是NAME NOT FOUND,那么可以認為首先去查詢HKCU\Software\Classes\ms-settings\Shell\Open\command路徑下的注冊表,再去查詢HKCU\Software\Classes\ms-settings\Shell\Open\command\DelegateExecute是否存在

那么這里我創建一個DelegateExecute的鍵值對,然后把默認鍵值對指向我的一個程序進行嘗試

當我運行c:\windows\system32\ComputerDefaults.exe的時候,發現不再彈出的是默認進程的界面,而是打開了我自己的程序

那么這里就可以大膽的猜測一下,首先運行ComputerDefaults.exe這個程序之前,會查詢HKCU:\Software\Classes\ms-settings\shell\open\command這個目錄是否存在,若存在繼續尋找同目錄下是否有DelegateExecute這個鍵值對,若兩者都存在則執行HKCU:\Software\Classes\ms-settings\shell\open\command指向的exe路徑
為了驗證猜想,這里我將exe路徑改為cmd,若猜測成立則可以獲得管理員權限的cmd

whoami /priv查看一下果然成功

bypass的實現
經過上面的探究過后,我們整理下思路,首先要創建注冊表,并添加DelegateExecute這個鍵值對,并修改command的指向exe路徑即可bypassuac,那么這里用到一下幾個函數
RegCreateKeyExA
首先是創建注冊表項,對應的是之前創建HKCU\Software\Classes\ms-settings\Shell\Open\command這個路徑的操作,這個路徑默認情況下是不存在的
LSTATUS RegCreateKeyExA(
[in] HKEY hKey,
[in] LPCSTR lpSubKey,
DWORD Reserved,
[in, optional] LPSTR lpClass,
[in] DWORD dwOptions,
[in] REGSAM samDesired,
[in, optional] const LPSECURITY_ATTRIBUTES lpSecurityAttributes,
[out] PHKEY phkResult,
[out, optional] LPDWORD lpdwDisposition
);
hkey:句柄
lpSubKey:此函數打開或創建的子項的名稱
Reserved:保留參數,必須為0
lpClass:該鍵的用戶定義類類型。可以忽略此參數。此參數可以為NULL
dwOptions:有幾個值,使用的時候具體查詢
samDesired:指定要創建的密鑰的訪問權限的掩碼
lpSecurityAttributes:指向SECURITY_ATTRIBUTES結構的指針
phkResult:指向接收打開或創建的鍵的句柄的變量的指針
lpdwDisposition:指向處置值變量的指針
RegSetValueExA
再就是修改注冊表項,指向我們的惡意exe路徑
LSTATUS RegSetValueExA(
[in] HKEY hKey,
[in, optional] LPCSTR lpValueName,
DWORD Reserved,
[in] DWORD dwType,
[in] const BYTE *lpData,
[in] DWORD cbData
);
hkey:句柄
lpValueName:要設置的值的名稱
Reserved:保留參數,必須為0
dwType:lpData參數指向的數據類型
lpData:要存儲的數據
cbData:lpData參數指向的信息的大小,以字節為單位
RegDeleteTreeA
執行exe過后我們為了隱蔽最好是刪除這個路徑,那么就需要用到這個api
LSTATUS RegDeleteTreeA( [in] HKEY hKey, [in, optional] LPCSTR lpSubKey );
hkey:句柄
lpSubKey:密鑰的名稱
這里測試一下,先把路徑寫成cmd.exe
char filePath[] = "C:\\Windows\\System32\\cmd.exe";
實現效果如下

這里cmd的實現成功了,那么直接使用cs的馬上線能不能夠直接bypassuac呢,實驗一下,這里把路徑改為cs馬的路徑

實現效果如下

這里為了驗證已經bypass了uac,我后面手動自己點了一下cs的木馬,第一個就是我們通過我們寫的程序上線的,第二個就是直接點擊上線的,看一下區別
仔細看的話這里bypass過的右上角有一個*

首先看一下第一個對話

再看下第二個對話,很明顯這里已經bypass過了uac

思考
我們所使用的幾個api,如RegCreateKeyExA、RegSetKeyExA都是直接修改注冊表的操作,這種操作應該被歸類為敏感操作,那么這里會不會被殺軟攔截呢,去測試一下
windows defender正常上線

獲取到的權限也是bypassuac后的權限

再看一下360,這里我試了兩個方法,第一種方法是直接對artifact.exe做一下免殺,然后執行bypassuac.exe可以正常上線并bypassuac,但是如果使用注入shellcode到其他的exe就不能夠獲得uac權限

如圖所示,這里我思考了一下,因為如果注入shellcode到其他程序,注冊表指向的還是這個注入shellcode到其他程序的exe,所以后面那種方法是不能夠bypassuac的

工具已經打包至github:
https://github.com/Drunkmars/BypassUAC
切勿用于非法用途!