做Windows安全测试的人,十有八九都听过“烂土豆”这名字。JuicyPotato,中文社区习惯叫它烂土豆,是一个经典到不能再经典的本地提权技术。它利用Windows服务账户默认开启的SeImpersonatePrivilege权限,通过精心构造的COM对象来模拟令牌,把服务账户权限直接提升到SYSTEM。这篇文章把它的底层原理、利用条件、实操参数和检测防御一次讲透,适合刚接触Windows提权的新手,也适合想彻底搞懂Potato家族技术细节的老手。
先说清楚一件事:本文所有内容仅用于本地实验环境、CTF比赛或企业授权的安全测试,目的是帮助蓝队和安全开发理解攻击原理,从而更好地防御。拿它去搞未授权系统,后果自己承担。
1. 烂土豆到底是个啥
1.1 Potato家族的来龙去脉
很多人第一次听到“Potato”是在提权漏洞列表里,但Potato并不是某一个具体漏洞的名字,而是一个技术家族的统称。最早出圈的是2016年的Rotten Potato,也就是“ rotten 土豆”,由FoxGlove Security的两位研究员提出。他们发现Windows服务账户如果开启了SeImpersonatePrivilege或SeAssignPrimaryTokenPrivilege,就可以通过NTLM中继和本地COM对象,模拟一个高权限令牌,把当前服务账户权限提成SYSTEM。
后来的几年里,这个思路被不断改进。有人做出了Rogue Potato、Sweet Potato、PrintSpoofer、GodPotato等等,它们本质上是同一类逻辑,区别在于触发令牌模拟的通道不同。JuicyPotato是其中流传最广、参数最灵活的一个版本,它不依赖系统上的特定服务,而是自己指定COM对象来触发模拟流程,所以适用面比初代Rotten Potato大得多。
Windows的安全研究员圈子里习惯叫它“烂土豆”,一方面是为了和Rotten Potato区分,另一方面是这名字好记。实际用的时候,JuicyPotato的exe程序名通常是JuicyPotato.exe或jp.exe,和“烂土豆”这个名字对不上号,但不妨碍大家在聊天时直接用烂土豆指代整个技术流派。
1.2 烂土豆能解决什么实际问题
在Windows内网渗透中,拿到一个WebShell或服务漏洞后,当前进程常常是IIS应用池账户、MSSQL服务账户、Windows服务账户或普通域用户。这些账户权限有限,很多敏感操作做不了。比如你想读取SAM文件、加载驱动、操作其他用户的进程、写入受保护目录,或者用管理员权限执行命令,都需要更高级别的权限。
烂土豆解决的就是这个从“普通服务账户”到“SYSTEM”的跨越。它不需要知道管理员密码,不需要挖掘额外的内核漏洞,只要满足几个条件,就能把一个低权限的服务账户直接提升到Windows系统最高的SYSTEM权限。这在实战中非常实用,也是它成为经典的原因。
不过要特别说明,烂土豆不是万能的。它只对开启了SeImpersonatePrivilege或SeAssignPrimaryTokenPrivilege的账户生效,而且必须从服务上下文启动,普通登录的桌面用户并不受这个漏洞影响。这也是很多人测试失败的第一原因——拿管理员开的cmd去跑烂土豆,结果提示没有权限,完全是用法不对。
2. 提权原理拆解
2.1 Windows令牌与模拟权限
要理解烂土豆,首先得理解Windows的令牌机制。Windows里每个进程都有一个访问令牌,令牌里记录了用户SID、组SID、特权列表等信息。系统判断你能做什么操作,本质是看令牌里有没有对应的特权。
令牌分两种:主令牌(Primary Token)和模拟令牌(Impersonation Token)。主令牌描述进程本身的身份,模拟令牌则允许一个进程临时“变成”另一个用户。打个生活化的比方:主令牌是你的身份证,模拟令牌是别人暂时借给你的工牌。工牌能让你进别人的办公区,但你本人身份证没变。
Windows为了让服务在某些场景下替用户干活,提供了SeImpersonatePrivilege权限。拥有这个权限的进程,可以获取另一个用户的模拟令牌,并用这个令牌执行操作。这个机制本身是设计的正常功能,很多服务程序都需要它。但问题在于,Windows默认会给一部分服务账户发放这个权限,最典型的就是IIS应用池账户、MSSQL和Oracle数据库服务账户,还有Network Service等内置账户。
烂土豆盯上的就是这些默认就带特权的大户。
2.2 SeImpersonatePrivilege:一切的关键
我们先看什么条件下能拿到SeImpersonatePrivilege。用管理员权限打开cmd,输入whoami /priv,能看到当前账户的特权列表。我随便拿一台Windows Server 2016上跑着IIS的机器举例,IIS应用池默认账户是iis apppool\defaultapppool,它具备的特权里就有SeImpersonatePrivilege和SeAssignPrimaryTokenPrivilege。
这两个特权是Potato家族的命根子。SeImpersonatePrivilege允许进程模拟其他用户的令牌,SeAssignPrimaryTokenPrivilege允许进程将一个模拟令牌关联到新进程的主令牌上。有了这两个特权,理论上只要你能搞到一个高权限的模拟令牌,就能以高权限执行命令。
搞到这个高权限令牌有两种常见路径。第一种是通过NTLM认证。Windows很多服务会以SYSTEM身份向某个端点发起NTLM认证请求,你可以诱导这个认证请求经过一个由你控制的进程或COM对象,中间截获认证过程中的模拟令牌。第二种,也是JuicyPotato主要的路径,是直接调用Windows COM对象的某些接口,让系统以SYSTEM身份调用我们指定的COM对象,进而在我们的进程里生成一个SYSTEM模拟令牌。
COM对象这里多说一句。COM是Windows的老牌组件模型,很多系统功能通过COM接口暴露。某些COM对象在执行特定方法时,会以调用者身份启动,但有一部分COM对象的设计是固定的,它们被配置为以“交互式用户”或“SYSTEM”身份启动。JuicyPotato要做的事,就是找到这些可以以SYSTEM身份启动的COM对象,然后诱导它们在自己内部触发一个模拟过程,最终拿到SYSTEM令牌。
2.3 JuicyPotato的利用流程
JuicyPotato的利用流程可以分成五步:
- 加载账户令牌。进程先确认自己具备SeImpersonatePrivilege或SeAssignPrimaryTokenPrivilege。
- 创建一个命名管道服务器,监听一个随机或指定的管道名。
- 强制本机的某个高权限进程(比如通过COM或NTLM触发)向这个命名管道发起身份验证请求。
- 在这个验证请求过程中,利用ImpersonateNamedPipeClient调用,从客户端拿到高权限的模拟令牌。
- 用这个模拟令牌创建新进程,以SYSTEM身份执行我们指定的命令。
第3步是整个利用的核心,也是最容易出问题的地方。JuicyPotato提供了多个COM对象和触发模式,因为不同的Windows版本上可用且能以SYSTEM身份启动的COM对象不一样。
我实际测试时最常用的是CLSID为{4991d34b-80a1-4291-83b6-3328366b9097}的COM对象,这是适用于Windows 10 1809和Server 2019的一个对象。老版本系统,比如Server 2008、Windows 7,则需要用别的CLSID。选错CLSID,程序会报错,或者模拟出来的令牌权限不够,这是烂土豆最典型的“挑系统”问题。
2.4 为什么能拿到SYSTEM令牌
看到这里你可能会想:就算我能让SYSTEM进程往命名管道发验证请求,但ImpersonateNamedPipeClient不是只能模拟连接管道的那个客户端吗?为什么拿到的令牌权限是SYSTEM?
关键在于Windows的模拟级别。JuicyPotato在创建命名管道时,会把管道的安全描述符设置为允许任意用户连接,同时请求的模拟级别是SecurityImpersonation。当SYSTEM进程作为客户端连接管道并完成NTLM握手后,服务端调ImpersonateNamedPipeClient,可以拿到客户端令牌的模拟句柄。这时候客户端是谁?是SYSTEM。所以拿到的模拟令牌自然就是SYSTEM级别的。
在某些触发模式下,系统甚至会把一个SYSTEM令牌通过COM调用直接传给我们的进程。JuicyPotato拿到这个令牌后,用CreateProcessWithTokenW或者类似API创建子进程,子进程的主令牌就是SYSTEM,于是命令执行权限直接拉满。
这里有个关键点:模拟令牌不是主令牌。拿到模拟令牌后,如果你用它来创建进程,需要把模拟令牌“提升”为主令牌。SeAssignPrimaryTokenPrivilege正是干这个用的。所以烂土豆要求两个特权里至少具备一个,最好两个都有。这也解释了为什么普通桌面的管理员用户用不了烂土豆——普通管理员登录系统后,默认是没有SeImpersonatePrivilege的。
3. 利用条件与实验环境准备
3.1 满足哪些条件才能用
我把自己踩过的坑总结成一张条件自查表,你照着排查,能省不少时间。
| 条件 | 说明 |
|---|---|
| 当前进程要有服务上下文 | IIS应用池、MSSQL、Windows服务、计划任务以服务方式运行均可 |
| 要具备SeImpersonatePrivilege或SeAssignPrimaryTokenPrivilege | 两者具备其一是底线,两个都有成功率更高 |
| 系统的COM对象库可用 | 需要对应版本的CLSID,且目标系统没有禁用相关COM组件 |
| 目标系统不是新版本 | Windows Server 2022和Win11 21H2+默认修复了旧CLSID,需要GodPotato或PrintSpoofer家族新项目 |
| 当前用户对临时目录有写权限 | 某些版本需要释放DLL,临时目录无写权限会失败 |
| Windows Defender未拦截 | 新版Defender对经典烂土豆的查杀概率很高,实战常需混淆 |
简单说,烂土豆最舒服的战场是Windows Server 2012、2016、2019,以及Windows 10 1809之前的部分系统。太老或太新都不好搞。
3.2 实验环境搭建
为了安全演示,我建议用VMware或VirtualBox搭一个隔离环境。目标机建议用Windows Server 2016或2019的评估版,安装IIS和MSSQL,这样就有现成的服务账户可以用。
具体搭建步骤:
- 创建一个Windows Server 2019虚拟机,分配2核4G内存即可。
- 安装IIS角色,默认站点放个asp/aspx文件,方便模拟WebShell场景。
- 再装一个SQL Server Express版,服务账户设置为NT Service\MSSQLSERVER。
- 确认防火墙关闭或允许本地流量,方便在虚拟机里直接调试。
- 把JuicyPotato的exe上传到一个低权限用户可写但可执行的目录,比如C:\temp。
这里的技巧是:先不要急着跑提权,先用当前身份确认权限。你在WebShell里执行whoami,看到的是iis apppool\默认应用池账户,然后执行whoami /priv,确认有SeImpersonatePrivilege,再做后续动作。
3.3 常用参数解析
JuicyPotato的命令行参数很固定,用多了自然就背下来了。核心参数是这些:
JuicyPotato.exe -l 9999 -p cmd.exe -a "/c whoami > C:\temp\result.txt" -t t -c {4991d34b-80a1-4291-83b6-3328366b9097}每个参数的含义如下:
-l指定COM监听端口,随便挑一个没被占用的端口。-p指定要启动的程序。-a传给程序的参数。-t模拟方式。t代表CreateProcessWithTokenW,u代表CreateProcessAsUser,m是默认方式,一般用t。-c指定COM对象的CLSID,花括号不能省略。-m指定COM模块路径,某些CLSID需要手动指定,不常用。
很多新手不明白为什么要先执行whoami输出到文件,而不是直接弹回一个交互式shell。原因很简单:如果提权成功,我们会得到一个SYSTEM权限的进程,但它的输入输出可能没法直接绑定到当前WebShell上,所以最稳妥的方式是让目标命令把结果写到文件里,再回头读取。这样能清楚确认提权是否成功。
4. 实操演示与结果分析
4.1 场景:IIS应用池账户提权
我在实验环境里搭好IIS后,用一个低权限的WebShell模拟攻击者。当前执行whoami返回的是iis apppool\defaultapppool,看起来是个没什么存在感的账户。但执行whoami /priv,明确能看到SeImpersonatePrivilege和SeAssignPrimaryTokenPrivilege两个特权,这就是烂土豆的敲门砖。
接下来先把JuicyPotato.exe上传到C:\temp,然后用WebShell执行提权命令。这里我用了比较通用的CLSID:
C:\temp\JuicyPotato.exe -l 1337 -p C:\Windows\System32\cmd.exe -a "/c whoami > C:\temp\whoami_result.txt" -t t -c {4991d34b-80a1-4291-83b6-3328366b9097}执行后等待几秒,查看C:\temp\whoami_result.txt,如果内容是nt authority\system,说明提权成功。
我实验里第一次用的CLSID是网上常见的老版本号,结果程序报错,输出一直卡在[+] Attempting to impersonate via COM,然后没有后续。后来换成上面那个CLSID,一次成功。这说明CLSID版本匹配比什么都重要。
4.2 执行过程与输出解读
JuicyPotato正常执行时输出的日志很有参考价值。我贴一段典型输出(不同版本略有差异):
[+] AuthZ SSO enabled [+] CreateProcessWithTokenW OK [+] Creating process with token... [+] Getting SE_RESTORE_NAME and SE_BACKUP_NAME privileges... [+] Starting COM server... [+] Calling CoGetObject with CLSID:{4991d34b-80a1-4291-83b6-3328366b9097}... [+] Got a SYSTEM token. Running as SYSTEM...看到Got a SYSTEM token基本就成了。但有时候你会在前面看到一行警告:
[+] Trying: {F87B28C6-2D1F-4B7B-9F4F-7A6A0F6D9E9F} [!] FindFirstFile failed: 拒绝访问。这个警告不是你权限不够,而是这个CLSID不存在或不可读。只要最终有能成功的CLSID就行,不必纠结每条警告。
有个容易被忽略的点:为了让命令真正以SYSTEM权限运行,-a参数里的命令最好写绝路径。有些版本的cmd.exe可能被Path影响,或者WebShell当前工作目录没权限,导致命令压根没执行。写死C:\Windows\System32\cmd.exe能避开这类问题。
4.3 为什么有时成功有时失败
我帮人排查过很多次烂土豆失败的问题,原因主要集中在下面几个方向。
第一个是CLSID不匹配。不同Windows版本上,能以SYSTEM身份激活的COM对象不一样。老版本的JuicyPotato项目文档里提供了一长串CLSID,但很多只适用于旧系统。你拿Win10 21H2的CLSID去跑Server 2012,大概率失败。
第二个是权限检查。你执行whoami /priv看到的特权列表,必须确认当前进程的令牌里有SeImpersonatePrivilege。有时候你虽然是以服务账户运行,但进程令牌被过滤了特权,比如IIS应用池如果开启了“加载用户配置文件”,某些特权可能被移除。这种情况需要换个触发方式或改用其他工具。
第三个是杀软。Windows Defender对JuicyPotato.exe的检测率非常高,我实验时直接运行原版exe,会被秒删。这是导致很多人“明明条件都满足,却执行失败”的最大原因。解决办法是只做研究,不要把原版exe上传到生产环境,或者用自己编译的修改版来做测试。
第四个是环境变量问题。某些WebShell执行程序时,会因为没有继承系统Path找不到cmd,所以-p参数必须写绝对路径,-a里调用的程序也一样。
5. 检测与防御
5.1 蓝队怎么发现烂土豆利用
作为防守方,我们不需要看懂每一行攻击代码,但一定要知道攻击成功后系统会留下什么痕迹。
烂土豆利用的核心动作是创建命名管道、模拟令牌、创建一个SYSTEM权限的新进程。这三种行为都能在系统日志里找到蛛丝马迹。
首先看4688事件(进程创建)。如果发现某个低权限服务账户(比如iis apppool\defaultapppool)创建了cmd.exe或powershell.exe,而且新进程的权限等级是SYSTEM,这基本是典型的提权行为。正常情况下,IIS应用池账户不应该去启动cmd.exe。
其次看Sysmon的管道事件(Event ID 18)和进程访问事件(Event ID 10)。烂土豆会频繁创建命名管道,并让SYSTEM进程去连接这个管道。如果某个WebShell目录下多出了一个exe,然后系统很快出现大量命名管道连接记录,那就要重点排查了。
最后检查敏感文件和命令的历史记录。攻击者提权成功后通常会执行whoami、net localgroup administrators、reg save之类的命令。你可以在日志里搜索这些命令行的特征字符串,再回溯来源进程。
5.2 如何加固系统
对系统管理员来说,防御烂土豆最直接的办法是缩小服务账户的特权范围。
如果是老系统且无法升级,优先考虑修改服务账户的权限。以IIS为例,可以在本地安全策略里找到对应的应用池账户,从“模拟身份后客户端”权限中移除它。注意,这么做可能会让某些依赖NTLM模拟的功能失效,所以要在安全和业务之间做权衡。
第二个办法是升级系统。微软已经在后续版本中收紧了COM对象的模拟行为,Windows Server 2022和Windows 11 21H2之后,经典烂土豆基本打不通。对于存量系统,至少要把补丁打全,尤其是涉及Windows COM、NTLM和特权提升的补丁。
第三个办法是用日志监控。部署Sysmon并开启Event ID 18规则,对常见的提权管道名做告警。比如监听所有命名管道的创建,再关联进程创建事件,一旦发现iis apppool\*账户创建了cmd.exe,立即封禁来源IP并隔离机器。
我个人还建议把WebShell的落地作为重点防护。烂土豆需要先有一个可执行文件的落地路径,如果Web目录本身设置了严格的禁止写入和执行策略,攻击者在第一步就会被拦下来。这一点比单纯关注土豆技术本身更务实。
6. 常见问题与避坑指南
6.1 遇到Access denied怎么办
这是烂土豆使用中最常见的报错。先说结论:报Access denied,通常是当前进程权限不足,而不是命令写错了。
要注意的是,烂土豆的exe必须从目标服务上下文运行。你在桌面管理员cmd里跑,就算加了参数也会报Access denied,因为管理员进程默认没有SeImpersonatePrivilege。正确做法是从WebShell、MSSQL xp_cmdshell、计划任务或者服务配置这种已经具备服务上下文的入口执行。
另外,如果系统开启了UAC且你的服务账户是标准用户,可能也会在创建进程那一步卡住。此时可以试试用-t u参数换一种创建进程的方式。-t u走CreateProcessAsUser,和默认的-t m在令牌处理上有细微差别,某些环境下能绕过权限检查。
还有一点:CLSID对应的COM对象有访问控制列表。不是所有CLSID都允许任意用户激活。如果你用的CLSID对当前服务账户没有激活权限,同样会报Access denied。换一个CLSID经常能解决。
6.2 烂土豆和PrintSpoofer怎么选
经常有人问:既然烂土豆这么好用,为什么现在很多工具不再内置它了?原因是Windows后续版本对COM模拟做了限制,经典烂土豆在新系统上成功率直线下降。
PrintSpoofer走的是打印服务(Print Spooler)的路径,利用Print Spooler的某些接口触发SYSTEM令牌模拟。它在Windows Server 2019和Windows 10 1809后的系统上表现更好。如果你测试的目标是Server 2022,烂土豆基本没戏,直接上PrintSpoofer或者GodPotato反而更快。
选型建议:Server 2012/2016/2019优先试烂土豆;Server 2022和Win11新系统直接放弃经典烂土豆;终端服务器(有RDP打印服务)优先PrintSpoofer;如果你需要更通用的COM模拟方案,可以了解GodPotato,它整合了多个CLSID,自动适应不同系统。
这里的核心思路是:不要死磕一个工具,Potato家族的不同变种覆盖了不同系统版本,灵活切换才是正解。
6.3 提权成功但命令没回显
在WebShell场景里,提权后执行命令经常没有回显。这不是提权失败,而是SYSTEM进程的输出没有重定向回当前会话。我建议所有测试命令都写成输出到文件的方式,比如cmd.exe /c whoami > C:\temp\1.txt,然后再用WebShell的文件读取功能查看结果。
如果连文件都没生成,优先检查-a参数里的命令是否被正确解析。如果命令包含空格和重定向符号,一定要用引号括起来。有些WebShell会转义特殊字符,导致命令没有按预期执行,这种时候可以写一个简单的bat脚本,再通过-p cmd.exe -a "/c C:\temp\a.bat"调用。
另外,提权成功后启动的SYSTEM进程不一定有完整的用户环境变量。某些命令比如powershell.exe可能从一个受限的Path启动,导致DLL加载失败。解决办法是用完整路径调用系统程序,并且尽量不要依赖当前工作目录。
我实际测试时还有一个小技巧:先执行cmd.exe /c set > C:\temp\env.txt,查看SYSTEM环境变量是否正常。如果PATH都乱了,说明进程环境被限制,这时需要额外修复环境变量或换一种启动参数。
写在最后的一点经验
从Rotten Potato到JuicyPotato,再到后来的PrintSpoofer、GodPotato,这个家族的核心思想一直没有变:找到Windows信任的高权限进程,借用它的令牌,完成权限跨越。理解了这个本质,你会发现所有变种工具都只是换了不同的“中间人”而已。
我个人的体会是,做安全研究时千万不要只停留在“会用工具”的层面。JuicyPotato为什么需要CLSID?为什么有时报Access denied?为什么换了系统就失效?把这些为什么都弄清楚,你才能真正判断一个提权工具在什么场景下能用、怎么改才能绕过硬性的环境限制,而不是拿个exe盲目乱试。
如果你是在做企业安全建设,我更建议把精力放在检测和加固上。烂土豆的利用痕迹其实很清晰,只要日志规范、监控到位,完全能够在攻击者拿到SYSTEM权限之前拦下来。技术本身没有黑白之分,关键看谁在用、用来做什么。