在日常运行某些程序时,如果出现一些 bug,我们会尝试右键,点击以管理员的身份运行,然后问题就解决了,而很多应用在双击的时侯,会直接弹出窗口,让我们点击确认,即使我们当前登录的就是管理员账号,这背后到底有什么秘密,看完这篇文章,相信一切都会明白的。
windows 的权限管理整体上来说,分为两部分,Access Token(访问令牌) 和 SD(安全描述符),Access Token 表示当前进程所具备的权限,而 SD 表示访问当前资源所需的操作权限,类似操作系统中的 CPL 和 DPL。
一、Access Token:访问令牌访问令牌(Access Token)是进程或线程的“安全身份证”,包含其安全上下文信息。可以分为主访问令牌(进程级令牌)和模拟令牌(线程级令牌)。
每个一个进程都有一个主访问令牌(Primary Token)这个令牌是在进程创建时从父进程或登录会话继承而来。而令牌内部包含不同类型的权限信息,每一种都对应一个 SID,常见的类型如下:
权限分类说明TokenUser主用户 SID(如 S-1-5-21-...-1001)TokenGroups所属组列表(含是否启用)TokenPrivileges特权列表(如 SeDebugPrivilege)TokenIntegrityLevel完整性级别 SID(如 S-1-16-8192)TokenTypeTokenPrimary或 TokenImpersonationTokenElevation是否以管理员身份运行(UAC 相关)TokenSessionId会话 ID(用于终端服务)可以使用 OpenProcessToken、GetTokenInformation来查看令牌内容
1.1 TokenUser/TokenGroupsGet-LocalUser | Select Name, SID #查看所有用户SID
Get-LocalGroup | Select Name, SID
Get-WmiObject Win32_UserAccount | Select Name, SID
whoami /all
表示当前进程所属用户、组信息
S-1-5-21-2352286992-1005746632-3940334147-1000
S-1-5-32-545
S:固定字符
1:修订级别
5:颁发机构
21:代表一个具体的用户、组或计算机账户,由 Windows 本地安全账户管理器(SAM)或 Active Directory创建的用户/组 。
32:系统自带的组
2352286992-1005746632-3940334147:这三个数字共同构成 该计算机(或域)的唯一标识符(Domain SID / Machine SID),也就是说,同一个主机中的所有用户,这一部分是一致的,在本地计算机上,这个三元组是在系统首次安装时由 Windows 随机生成的,全局唯一,在域环境中,这三元组由域控制器分配,代表整个域的 SID。
1000:用户 ID
RID账户说明500Administrator默认管理员(本地)501Guest来宾账户502KRBTGTKerberos 服务账户(域)512Domain Admins域管理员组(域)513Domain Users域用户组544Administrators管理员组545Users普通用户组1000+普通用户/组本地创建的用户从 1000 或 1001 开始递增1.2 TokenIntegrityLevel完整性级别 SID:代表进程的访问权限
S-1-16-8192(Medium)
具体如下表
名称RID十进制典型场景Untrusted0x00000Low0x10004096沙箱(如 IE Protected Mode)、浏览器插件、Office 宏Medium0x20008192普通用户默认High0x300012288“以管理员身份运行”的程序System0x400016384系统服务、驱动1.3 TokenPrivileges特权列表,比较重要的是 SeDebugPrivilege特权,拥有此特权的进程可以打开、调试其他进程,很多安全软件、进程注入都是基于此特权的,只有 High IL权限的用户才能有此特权,而且需要通过 AdjustTokenPrivilegesAPI 启用
1.4 Access Token 获取的详细过程步骤 1:用户输入凭据用户在登录界面输入 用户名 + 密码
Winlogon接收凭据,并将其传递给 LSA
步骤 2:LSA 调用认证包验证身份LSA 加载认证包(如 msv1_0.dll,用于本地账户)
认证包:
从 SAM 数据库中取出该用户的密码哈希
将输入密码哈希后与之比对
验证成功 → 返回用户 SID、所属组、默认特权等信息
步骤 3:LSA 创建 主令牌(Primary Token)如果用户是 普通用户(非 Administrators 组成员):
LSA 直接基于用户信息创建一个 标准访问令牌:
Integrity Level = Medium
包含用户所属的所有组(如 Users)
包含默认特权(如 SeChangeNotifyPrivilege)
如果用户属于 Administrators 组,此时,LSA 会执行 UAC Split Token(令牌拆分)机制:
步骤 3a:创建 完整的管理员令牌(Full Token)基于用户 SID 和所有组(包括 Administrators)
完整特权列表(包括 SeDebugPrivilege, SeTakeOwnershipPrivilege等)
Integrity Level = High
步骤 3b:创建 过滤后的标准用户令牌(Filtered Token)LSA 对完整令牌进行以下过滤:
过滤操作说明移除高危特权如SeDebugPrivilege,SeBackupPrivilege等被完全移除将 Administrators 组设为 "Deny-only"该组仍存在于令牌中,但仅用于拒绝访问(不能用于授权)完整性级别降为 MediumIL = Medium(这是 UAC 隔离的关键)保留普通用户组如 Users、Everyone 等注意:这个 Filtered Token就是默认用于启动进程的令牌。
步骤 3c:建立 令牌链接(Linked Token)在 Filtered Token中设置一个指针,指向对应的 Full Token
可通过 API GetTokenInformation(TokenLinkedToken, ...)获取
这是 UAC 提权时能找到“另一个令牌”的关键
步骤 4:启动初始进程(如 Explorer.exe)Winlogon 使用 Filtered Token(Medium IL)启动 userinit.exe→ explorer.exe
所有后续从 Explorer 启动的程序(双击 EXE、命令行等)继承此令牌
二、SD:安全描述符(Get-Acl ./test.txt).Sddl #导出文件的SDDL信息
SD(Security Descriptor,安全描述符)是实现对象级安全控制的核心数据结构。它定义了谁可以访问某个对象(如文件、注册表项、进程、线程、命名管道等),以及允许或拒绝哪些操作。
一个完整的 SD 通常包含以下四个主要部分(某些可选):
成员说明Owner SID对象所有者的用户或组 SIDGroup SID主要组 SID(在 Windows 中通常忽略)DACL定义谁可以/不可以对对象执行哪些操作SACL定义哪些访问行为需要被审计(记录到安全日志)其中 DACL、SACL、IL 都是基于 ACE 格式实现的
typedef struct _SYSTEM_MANDATORY_LABEL_ACE {
ACE_HEADER Header; // AceType = SYSTEM_MANDATORY_LABEL_ACE_TYPE (0x11)
ACCESS_MASK Mask; // 通常为 NO_WRITE_UP, NO_READ_UP, NO_EXECUTE_UP
DWORD SidStart; // 指向一个 Integrity SID
} SYSTEM_MANDATORY_LABEL_ACE;
2.1 DACL:自主访问控制列表DACL 是一个 ACE(Access Control Entry,访问控制项)的列表,也是我们右键文件在安全中展示的不同用户的权限信息,ACE 主要的类型如下
类型说明ACCESS_ALLOWED_ACE允许指定用户/组执行某些操作ACCESS_DENIED_ACE明确拒绝指定用户/组的某些操作ACCESS_ALLOWED_OBJECT_ACE针对属性/子对象的细粒度允许(用于 AD)ACCESS_DENIED_OBJECT_ACE针对属性/子对象的细粒度拒绝O:BAG:S-1-5-21-2352286992-1005746632-3940334147-513D:(A;ID;FA;;;SY)(A;ID;FA;;;BA)(A;ID;FA;;;S-1-5-21-2352286992-1005746632-3940334147-1000)
O:BA:所属用户
BA:Built-in Administrators
G:S-1-5-21-2352286992-1005746632-3940334147-513D:所属组
(A;ID;FA;;;SY):ACE 格式
第一位:ACE 类型
A允许
第二位:继承标志
OI= Object Inherit(文件继承)
CI= Container Inherit(文件夹继承)
ID= OI+ CI
IO= Inherit Only(此 ACE 仅用于继承,不作用于当前对象)
NP= No Propagate(禁止向下进一步继承)
第三位:文件权限
FA:完全控制
RX= Read & Execute(读取和执行)
第四位、第五位:用于对象 ACE(Object ACE) ,比如 Active Directory 对象、带属性的安全对象等
第四位:指定此 ACE 保护的是对象的哪个“属性”或“子对象类型”
第五位:指定此 ACE 可以被哪些类型的子对象继承
第六位:用户
SY:system
2.2 SACL指定哪些操作会被放入系统日志审核中,默认关闭,需要在地安全策略或组策略中启用了 “审核对象访问”(Audit object access)。
2.3 IL:完整性级别文件的 IL 以SYSTEM_MANDATORY_LABEL_ACE的形式,存储在安全描述符的 DACL 中。
默认使用创建文件进程的 IL,而不继承父目录。
(ML;;NW;;;S-1-16-8192):完整性标签
ML= Mandatory Label
NW= No Write up(默认标志,表示“禁止向更高完整性级别写入”)
S-1-16-8192= Medium Integrity
Access Token 如何与 SD 交互?在 Windows Vista 引入了 Mandatory Integrity Control (MIC)机制,用于实现沙箱隔离(如 IE Protected Mode、Edge、Office 宏)。Windows 在 DACL 检查之前,先进行 完整性级别检查:
请求者 IL目标对象 IL默认允许的操作LowMedium读、执行(受限)LowHigh无(完全隔离)MediumHigh读(部分场景),禁止写/注入HighSystem通常禁止关键限制:
低完整性进程不能向高完整性进程发送窗口消息(UIPI)
例如模拟点击“确定”绕过 UAC
低完整性进程不能写入高完整性文件/注册表
低完整性进程不能打开高完整性进程的 HANDLE(如 PROCESS_VM_WRITE)
三、UAC:用户账户控制通过上面的介绍,我们知道了当一个进程的 IL 权限低于目标文件的 IL 时,是无法进行写入等敏感操作的,就算 DACL 允许都不行。为了限制管理员账号的权限,在 Windows Vista 及以后版本中引入了 UAC 机制。
传统 Windows(XP 及以前):管理员登录后,所有程序都拥有完整管理员权限 → 恶意软件可随意破坏系统。
UAC 改进:管理员账户(用户账户被加入Administrators 组中)被“拆分”为两个令牌:
标准用户令牌(Filtered Token):默认使用,权限受限(Medium IL)
管理员令牌(Full Token):仅在明确提权时使用(High IL)
注意:内置的Administrators 账户是不受UAC控制的,默认拥有High IL 令牌
3.1 双令牌机制(Split Token)当一个属于 Administrators 组的用户登录时,LSA(Local Security Authority)会创建 两个访问令牌:
令牌类型权限特点完整性级别所属组Standard User Token(过滤令牌)移除了高危特权(如 SeDebugPrivilege),禁用 Administrators 组 SIDMedium (S-1-16-8192)Users + 其他非管理员组Elevated Token(完整令牌)包含全部特权和 Administrators SIDHigh (S-1-16-12288)Administrators + Users + 所有组并不是当前账户通过UAC获得了High权限,而是UAC使用 High权限启动了新的进程
3.2 UAC 提示等级设置非管理员用户管理员用户始终通知提示输入凭据提示确认仅当应用尝试更改时通知(默认)提示输入凭据静默同意(不提示)← 默认!仅当应用尝试更改时通知(无桌面 dimming)同上同上(视觉效果不同)从不通知自动拒绝提权自动提升(危险!)注意:默认设置下,管理员运行提权程序不会弹窗!
四、UAC Bypass绕过前提用户在 administrator 组
UAC 提示等级默认之下,不是始终通知
查看UAC提示等级注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System,有两个关键值
注册表值类型含义ConsentPromptBehaviorAdminDWORD决定管理员账户的 UAC 行为EnableLUADWORD是否启用 UAC(1=启用,0=禁用)ConsentPromptBehaviorAdmin的取值含义:
值UAC 等级(图形界面显示)行为说明0从不通知(最低)管理员静默提权,无提示(不推荐)1(仅 Win7/8 使用)需要凭据(类似标准用户)2(保留)—3默认(Windows 默认)提示但不锁屏(仅确认)4(保留)—5始终通知(最高)提示并锁屏(安全桌面)通过POWERSHELL查看
(Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System).ConsentPromptBehaviorAdmin
(Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System).EnableLUA
4.1 SHELL APIPOCreg add "HKCU\Software\Classes\ms-settings\Shell\Open\command" /v "DelegateExecute" /t REG_SZ /d "" /f
reg add "HKCU\Software\Classes\ms-settings\Shell\Open\command" /ve /t REG_SZ /d "cmd.exe /c start powershell.exe -ep bypass" /f #设置默认值为要执行的命令
1. fodhelper.exe
2. sdclt.exe /kickoffelev
3. C:\Windows\System32\computerdefaults.exe
reg 注册表操作
/v:指定要操作的键
/ve:表示操作的是默认值
/t:值的类型
RES_ZE:字符串
/d:具体的值
/f:强制执行
powershell
-ep:-ExecutionPolicy
bypass:绕过 PowerShell 执行策略限制(允许运行未签名脚本)
-F:脚本路径
-Command:后跟要执行的 PowerShell 命令(可多条,用分号 ;分隔)
执行完后自动退出(除非加 -NoExit)
漏洞原理正常情况下,当程序(如 fodhelper.exe)通过 ShellExecute调用 ms-settings:协议时:
如果存在 DelegateExecute值,Windows 会尝试加载对应的 COM 对象(更安全的现代方式)。
如果 不存在或为空,则回退到执行 (Default)值中的命令(旧式 command方式)。
因此清空DelegateExecute,强制 fodhelper.exe走老路径 → 执行 (Default)中的任意命令。
ms-settings是一个自定义协议,被设计处来允许应用程序或脚本通过调用 ms-settings:<页面标识符>快速跳转到 Windows 设置应用中的某个具体面板,无需用户手动导航,因此一些系统设置相关的程序会在启动的时候自动执行ms-settings
默认情况下,ms-settings协议由 HKEY_LOCAL_MACHINE(HKLM)定义,指向安全的系统组件(如 SystemSettings.exe)。
但 Windows 优先检查当前用户的HKEY_CURRENT_USER(HKCU)下是否存在同名协议:如果存在,则 覆盖系统默认行为。
因此导致了被利用来进行UAC Bypass
攻击检测注册表
HKCU\Software\Classes\下出现非标准协议(如 ms-settings, mscfile, computerdefaults)
存在 Shell\Open\command且 DelegateExecute为空
可疑进程树
explorer.exe
└─ fodhelper.exe (High IL) / sdclt.exe
└─ cmd.exe
└─ powershell.exe
4.2 COM组件ICMLuaUtil:UACme 41
MMC20: {49B2791A-B1AE-4C90-9B8E-E868BA8E76CC}
MMC10: {1D268049-1EEA-4A5C-B707-01D61D920046}
漏洞原理即使调用者只有Midlle IL 权限,系统也会自动以 High IL(高权限,管理员)启动该 COM 对象
通过 CoGetObject创建DOM对象
然后使用对象的ShellExec接口(本质上是对 ShellExecuteEx的封装)执行命令
[Medium IL Process]
│
├── CoGetObject("Elevation:...{CLSID}")
│
▼
[COM Subsystem] → 检查 CLSID 是否 auto-elevate?
│
├── 是 → 启动新进程: dllhost.exe (High IL)
│ │
│ └── 加载 CMSTPLUA.dll
│ │
│ └── 创建 CMLuaUtil 对象
│
◄────────────── 返回 ICMLuaUtil 接口指针
│
└── 调用 ShellExec("cmd.exe")
│
▼
[High IL cmd.exe] ← 无 UAC 弹窗!
4.3 DLL漏洞原理DLL伪装
修改 PEB->ProcessParameters->ImagePathName为类似setup.exe
然后触发UAC提权,比如创建一个管理员权限的cmd
#构造SHELLEXECUTEINFO结构
SHELLEXECUTEINFO sei = {0};
sei.lpFile = L"cmd.exe";
sei.nShow = SW_SHOW;
sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_DDEWAIT;
sei.lpVerb = L"runas"; // 请求提权
ShellExecuteEx(&sei);
AppInfo 服务检查 cmd.exe的父进程(即当前进程)的 ImagePathName发现路径含 "setup.exe"→ 判定为“安装程序”
自动批准提权,不弹 UAC 窗口
DLL劫持:wsreset.exe
DLL加载顺序一般如下:
可执行文件当前所在目录
系统目录:%SystemRoot%\System32
16位系统目录:%SystemRoot%\System
Windows目录:%SystemRoot%
当前工作目录
PATH 环境变量中列出的目录
对于一些特殊的DLL:
已知 DLL(KnownDLLs)某些核心系统 DLL(如 kernel32.dll, user32.dll)被注册为 KnownDLLs,系统会直接从\System32加载,跳过常规搜索顺序,且不会从其他位置加载,以增强安全性和性能。
清单文件(Manifest)或 Side-by-Side (SxS) 组件如果程序使用了清单文件指定依赖的 DLL 版本,系统会优先从 WinSxS 缓存中加载。
利用DLL加载的优先级,让指定的进程加载我们伪造的dll
4.4 计划任务schtasks /delete /tn "name" /f #删除计划任务
schtasks /query /tn "任务名称" /v /fo list #查看详细内容
C:\Windows\System32\Tasks #计划任务路径
schtasks /run /tn "MyTask" #立即执行一次任务
schtasks /create /tn "MyStartupTask" /tr "C:\scripts\startup.bat" /sc onstart /ru SYSTEM /rl HIGHEST /f #创建计划任务
参数说明/tn任务名称(Task Name),支持路径如\MyTasks\MyTask/tr要运行的程序或命令(Task Run),需用引号包裹(如"C:\test.bat")/sc计划类型(Schedule):MINUTE,HOURLY,DAILY,WEEKLY,MONTHLY,ONCE,ONSTART,ONLOGON,ONIDLE/st开始时间(24 小时格式,如14:30)/sd开始日期(YYYY/MM/DD)/ed结束日期/ri重复间隔(分钟),配合/du使用/du持续时间(如01:00表示 1 小时)/f强制创建(覆盖同名任务)/ru运行任务的用户账户(如SYSTEM,Administrator, 或域用户)/rp用户密码(若/ru需要密码)/rl运行级别(仅限 Vista+):LIMITED(默认)或HIGHEST(以最高权限运行)需 UAC 允许/it仅当用户已登录时交互式运行(常用于 GUI 程序)漏洞原理SilentCleanup
执行SilentCleanup定时任务时,cleanmgr.exe会读取 HKCU 下的StateFlags0001和Actions,因此如果攻击者将恶意命令写入注册表,就可以得到执行
4.6 绕过总结通过上述这个例子,我们可以发现,Uac Bypass的整体思想,其实就分为两步
找到一个白名单(autoElevate)进程
想办法从他手中拿到shell的控制权
因此响应的防御手段,也是从这两点出发
启动UAC的“始终通知”模式
缩小白名单范围
禁用非必要 auto-elevate 组件(通过组策略或补丁)
升级系统,使用已移除 auto-elevate 的新版 Windows
启用DLL安全加载
必要的话,用户可以不加入本地管理组