1. 为什么安全评估绕不开Windows内网提权做了这么多年Windows环境的安全评估和加固,我越来越觉得提权这块知识是所有系统管理员和运维同学绕不过去的坎。很多人觉得“提权”就是攻击者的专属话题,实际恰恰相反,一个不清楚内网中有哪些提权路径的管理员,很难真正把服务器的安全基线做好。所谓Windows内网提权,简单说就是在已经拿到一个受限账号的前提下,借助系统本身的漏洞或配置疏忽,把权限从普通用户提升到管理员甚至SYSTEM级别的过程。无论你是负责补丁管理、基线加固,还是做上线前的安全自测,理解这些路径都直接决定了你能不能在问题被利用之前把它堵上。这里我先把边界说清楚。我讲的“实战演示”是在合法授权的安全评估与自查场景中进行的,全程基于模拟环境,核心目的是帮大家看懂风险点长什么样、怎么被触发、以及对应的修复手段。没有授权的测试行为在任何环境下都是违规违法的,这一点做安全相关工作的同学务必时刻记住。从我这几年处理过的各种实际案例来看,Windows内网里的提权风险大致能分成两大阵营:一类是系统或软件漏洞,比如内核漏洞、驱动程序漏洞,这类问题靠打补丁解决;另一类是配置错误,比如服务权限过大、路径写得有歧义、计划任务被篡改,这类问题靠基线管理和权限收敛解决。有意思的是,绝大多数实际出事的场景都是第二类,配置类问题。原因不复杂,漏洞利用往往有门槛,需要匹配的系统版本和可利用代码,而配置错误就像是门锁没换还用默认钥匙,只要有人进去逛一圈就能发现。这篇文章我会从原理讲到实际操作思路,再落到具体的排查与加固方法上。不堆概念,不念手册,尽量把我踩过的坑和验证过的经验直接摆出来。2. 提权的本质与核心分类2.1 纵向提权与横向移动的区别先理清一个容易混淆的概念。内网里所谓的“提权”其实包含两种完全不同的场景,虽然经常被放在一起说,但解决思路天差地别。纵向提权,也叫权限提升,指的是从低权限账户(比如普通域用户、本地Users组成员)往更高权限账户(比如本地管理员、SYSTEM账户)跨越。这种场景通常发生在已经拿下一台机器、但只有个普通权限的时候。目标是突破系统自身的访问控制,拿到更高级别的操作能力。横向移动则不是“升高”而是“平移”——用已经控制的一个账户访问内网中其他机器。横向移动的核心往往不在于漏洞本身,而是凭据复用、弱口令、哈希传递这类问题。虽然横向移动经常和提权配合着用,但它的目标不是提升某台机器上的权限等级,而是扩大影响范围。这篇文章聚焦的是纵向提权,也就是在同一台机器上把权限等级拉高的过程。搞清楚这个边界,后面看各种演示就不会晕。2.2 漏洞型提权和配置错误型提权的本质差异漏洞型提权依赖的是软件层面的缺陷。比如某个内核驱动的函数没有对传入句柄做完整校验,导致普通程序能够以内核态执行代码;或者某个服务存在缓冲区溢出,攻击者可以直接夺取该服务进程的权限。这类问题的特点是:修复起来相对明确,打补丁就行,但依赖具体版本和补丁情况,存在“我打了补丁你不知道,你没打补丁我能测出来”的信息差。配置错误型提权就完全不一样了。它不需要什么高深的利用技巧,纯粹是系统在安装、部署、运维过程中留下了不安全的默认设置或人为疏忽。我遇到过的最典型的例子是:某台服务器装完第三方软件之后,安装目录的权限被设置成Everyonne完全控制,普通用户可以在安装目录里放任意文件,甚至替换掉服务启动时加载的EXE或DLL。这种问题你说它是漏洞吗?不是。但它的危害程度完全不亚于一个远程代码执行漏洞,因为利用难度几乎为零。从防御角度看,配置类风险比漏洞类风险更值得花精力去梳理。因为不像补丁可以一扫了之,配置问题需要一条一条去核对,靠的是细功夫。3. 配置错误提权:最常被忽视的高危风险3.1 服务权限配置不当:普通用户却能控制SYSTEM服务Windows服务是配置错误提权里最经典、也最多发的场景。一个服务一旦被配置为“允许普通用户修改其配置”,那么这台机器的管理员权限基本就形同虚设了。常见的情况是这样的:某个服务的可执行文件路径指向了一个目录,而这个目录对Eve是“完全控制”。此时普通用户虽然不能直接改服务本身,但可以把该路径下的EXE文件替换成自己准备好的程序,然后触发服务重启,新程序就会以SYSTEM权限启动。还有更直接的:服务本身的DACL(Discretionary Access Control List)被改宽了。理论上,服务控制管理器(SCM)中的服务对象默认只允许SYSTEM和管理员组修改配置,但在实际运维中,有些软件为了“方便”安装或更新,会把当前用户或者Everyone加进完全控制列表。我见过某商业化运维软件安装之后,把自身的服务权限放开给Eve,结果任何域用户登进去都可以直接改服务的binPath,再重启该服务,瞬间获得SYSTEM权限。排查这一类问题,我经常用一条命令把服务信息和权限拉出来看:# 查看服务以什么账户运行、路径是什么 wmic service get name,startname,pathname,displayname # 结合AccessChk查看每个服务的权限是否对普通用户过宽 accesschk.exe -uwcqv "Users" *.exeAccessChk是Sysinternals套件里专门用于查看对象权限的工具,实测下来比手动翻SCM配置要直观得多。当你看到某个服务对“Users”组有SERVICE_ALL_ACCESS这种权限时,不用犹豫,直接把它记进整改清单。3.2 未引用的服务路径:空格引发的“路径劫持”未引用的服务路径是另一个我见到过很多次的老问题。它本质上不是因为某个权限配错了,而是因为路径写法不规范,加上Windows在执行程序时对路径解析的特定顺序导致的。假设某个服务的ImagePath被设置成这样的形式:C:\Program Files\Vendor App\Service.exe注意,这个路径没有被双引号包裹。Windows在解析路径时,会把路径按空格切分,然后依次尝试,它会先尝试解析C:\Program.exe,如果找不到,再尝试在C:\Program Files\Vendor目录下找App\Service.exe之类的组合。于是问题就来了:如果C:\根目录对普通用户是可写的,用户就可以在根目录放一个名为Program.exe的文件,然后想办法重启服务,这个恶意程序就会被当作服务程序加载执行,同样以服务配置对应的权限运行。如果服务本身是SYSTEM账户,那恶意程序就是SYSTEM权限。这类问题在国产软件和老旧企业级软件里尤其常见。很多安装包没有用标准格式引号包裹目标路径,安装目录又是常见的带空格的目录,一抓一个准。排查命令也简单:wmic service get name,displayname,pathname | findstr /v "C:\Windows\\" | findstr """把里面不带引号且路径带空格的记录捞出来核对就行。修复方法更加简单,在服务的ImagePath路径两端加上英文双引号,问题直接消失。这里有一个实操心得:加引号不只是为了规避这个提权问题,更是为了避免服务启动时的各种意外行为。路径中含空格的程序如果没加引号,在系统重启或服务异常拉起时可能会出现加载失败,甚至加载错程序,这类问题我在线上环境都见过。所以处理这个配置问题要当成一次性把所有服务的路径规范化来扫,不要只针对有风险的那几个单独改。3.3 计划任务与注册表自启动项:被篡改的运行身份计划任务和注册表启动项是容易被忽略的另一类配置面。Windows的计划任务可以指定运行账户、运行等级和可执行文件路径,如果某个任务的配置允许普通用户修改,或者任务的执行目录权限过宽,那普通用户就能通过替换任务程序获得高权限运行的机会。计划任务不像服务那样有直观的权限属性让你直接查,需要结合具体任务的XML配置来看。在评估过程中我会重点关注两类任务:一类是系统安装第三方软件时自动注册的任务,这类任务经常以SYSTEM或Administrator身份运行,但任务的创建者对执行文件的保护几乎没有做,执行文件位于普通用户可以写入的目录。另一类是用户自己创建的自动化任务,为了图方便勾选了“不管用户是否登录都要运行”,并选择了“使用最高权限运行”,然后执行体放在了一个权限敞开的位置。排查计划任务的基线命令如下:# 列出所有计划任务及其运行身份 schtasks /query /fo LIST /v | more拿到清单后,核对每一条以高权限运行的任务,看它的执行程序路径是否在可被篡改的目录下,以及任务本身的安全选项是否允许普通用户更改。如果有可疑项,直接看任务的XML定义:schtasks /query /tn "任务名" /xml注册表自启动项的原理与之类似。常见的位置是HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run和HKEY_CURRENT_USER\...\Run。如果HKLM下的Run键对于普通用户是可写的,那么下次该用户登录时,写入的启动程序就会以该用户的权限运行。有些场景下,如果启动程序的路径还指向了一个普通用户可写的目录,那就安全了。我处理过的一个案例是某设备的配套驱动程序在Run键里指向了一个驱动工具目录,而该目录对Everyone完全控制,任何人登录都会加载这个目录里的程序,等于给了所有使用者一个后续执行入口。注册表权限的排查比服务更隐蔽,因为默认摸不到,要把目标键的权限子项逐个展开。用regedit定位到目标键后,右键->权限,核对Users或Authenticated Users是否有写入权限。或者直接通过PowerShell走一层ACL检查:Get-Acl -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" | Format-List看到“Users”或者“Everyone”带“Write”或“FullControl”的,就是需要处理的对象。3.4 可写路径与DLL加载:文件系统权限的连锁反应最后再补一个经常被忽略的配置类问题:可写路径导致的DLL劫持。Windows程序在加载DLL时遵循一套搜索顺序,从“程序所在目录”到“系统目录”,再到“当前工作目录”,再到“PATH环境变量中的目录”。如果某个程序的启动方式带有可控的工作目录,或者程序本身的依赖DLL不在系统目录而在当前目录,那么一个可写的工作目录就有可能变成劫持加载的入口。我印象很深的一个案例是某老旧的打印管理系统,安装目录是C:\PrintServer\,这个目录对普通用户完全可写。程序启动时会在首目录加载某个第三方日志模块,日志模块的DLL文件却可以被直接替换。当时为了验证风险,我只写了个简单的DLL,加载后做一条日志记录并尝试以当前进程权限创建测试文件。不出意外,进程以SYSTEM身份运行,测试文件直接落在了系统临时目录里。整个过程不需要任何漏洞利用,纯粹是文件系统权限和DLL搜索机制叠加出来的结果。这类问题的排查思路是找出所有“高权限进程启动时可能加载的DLL”所在的路径,再进行可写权限检查。实际操作中,我会优先把服务类的exe跑起来之后,通过Process Monitor抓一下它加载了哪些非系统目录的DLL,再逐一检查对应目录权限。这个操作用PCHunter或Process Monitor都能做到,实测下来Process Monitor的过滤规则更适合这种场景,直接按进程名过滤,把Result为“NAME NOT FOUND”的项筛出来看,往往能发现意外惊喜。修复方面就一句话:程序安装目录和所有子目录的权限只保留SYSTEM、管理员和必要的服务账户,普通用户一律只给读取和执行。别小看这一步,它能一次性封堵掉服务替换、DLL劫持、以及自启动程序篡改的一大批问题。4. 漏洞型提权:补丁管理与漏洞利用的博弈4.1 内核提权漏洞的基本逻辑配置错误之外,漏洞型提权是另一个核心方向。Windows内核提权漏洞的经典模式是:系统的某个内核函数在处理特定对象时,没有对调用的权限或传入的参数做完整校验,导致低权限进程可以借助该函数影响内核状态,最终获得任意代码执行的能力。整个过程对普通用户来说像一个黑盒,但这并不妨碍我们从补丁管理的角度来理解和处置它。对于安全评估来说,关注漏洞型提权的重要思路是“识别可被利用的特征”。在拿到一台机器的权限后,比较直接的办法是通过系统版本、已安装补丁列表与公开漏洞库进行比对,找出缺少的补丁。这个过程有点像在盘点一台机器的“免疫缺口”,补丁管理做得越差,暴露面就越大。常用的信息采集思路如下:# 查看系统版本和补丁信息 systeminfo wmic qfe list brief /format:table把输出的KB编号与近期公开的Windows提权漏洞对应列表比对,如果发现某个漏洞利用代码公开已久,而目标机器对应的补丁没安装,那这台机器的风险等级就直接拉高了。4.2 为什么不建议在真实环境里跑漏洞利用代码网上流传的各种Windows内核漏洞利用代码,很多是PoC性质,稳定性参差不齐。一个成熟的评估人员不会在客户的生产环境里直接跑这类利用工具。原因很简单:内核漏洞利用一旦触发BSOD(蓝屏),影响的就不只是一个进程,而整台服务器都会宕机,这是任何评估工作都承担不起的后果。退一步说,即便不考虑稳定性,直接执行漏洞利用代码也是“过度授权”的行为。评估的目的通常是发现并证明风险存在,而不是真的攻陷一台机器。用系统版本和补丁状态来判定风险,已经足够支撑整改建议了,不需要走完整利用链来证明“能得手”。这个原则我保持至今,也是在带新人时反复强调的一条底线。4.3 漏洞利用的防御视角:补丁之外还需要什么如果没有打上最新补丁,这类漏洞型提权几乎是防不住的,因为它利用的是系统自身的缺陷。补丁管理之外能做的有限,但仍然有几件事值得做:第一,缩小低权限账户的可利用空间。很多内核漏洞利用代码在触发前需要做一些前置操作,比如创建特定类型的对象句柄、加载特定的驱动映射权限。如果使用标准账号的配置上做了限制(比如禁止加载未签名驱动、限制访问内核设备对象),可以在一定概率上干扰利用过程。但这些属于“深水区”加固,普通企业环境实施成本很高。第二,启用漏洞利用缓解机制。Windows的“Exploit Protection”和“攻击面减少(ASR)”规则可以针对常见的内核利用手法做阻断,例如阻止通过特殊方式注入代码。虽然不一定对每个漏洞都有效,但实测下来确实能挡住一部分利用链的前置操作。第三,也是最重要的:建立可落地的补丁节奏。不在Windows Update弹出来之后第一时间热更,而是按“每周固定维护窗口批量更新”的方式来管理,同时做好更新前的兼容性验证,这几乎就是漏洞型提权问题的终极解法。我见过很多机构不是不更新,而是更新策略形同虚设,定了每周一次实际变成季度甚至半年度一次,等出问题时才想起来。5. 实战演示:一次授权环境下的提权风险自测5.1 场景设定与初始条件下面用一个模拟环境来演示整个风险自测的流程。环境里有一台Windows Server 2019,安装了三方打印管理系统,对外服务账户是普通域用户。初始假设是:评估人员通过某种合法途径(比如一次弱口令自查中发现该普通用户的密码被重置为强度很弱的默认值)拿到了这个普通账户,可以在本机登录并执行常规操作。这个场景非常贴合实际,因为内网中真正被突破的案例里,很大比例都是从某个低权限账户开始的,随后通过配置问题一路提权到管理员甚至域管。模拟环境的初始条件如下:条件说明系统Windows Server 2019登录账号域普通用户目标验证本机提权路径并输出加固建议软件三方可信服务软件(有未引用路径问题)5.2 Step 1:信息收集与风险面绘制登录后的第一件事是快速把系统概况和可能的高权限运行对象摸一遍。我习惯分成三个方向进行:服务层面,重点看是否存在未引用的路径以及以高权限运行的非系统服务。输出之后可以写个小循环脚本,把所有不带引号且路径含空格的exe服务标红。系统层面,用systeminfo确认补丁状态和系统版本,作为后续漏洞比对的基础。敏感文件层面,检查安装目录、临时目录、公共目录的ACL权限,看是否有Everyone或Users可写的情况。这个环节我不推荐上来就跑一堆扫描工具,一方面动静大,另一方面容易误伤。人工配合轻量命令去摸,往往能得到更清晰的风险面。5.3 Step 2:服务路径问题的验证在模拟环境里,我很快就注意到了打印管理服务,它的路径是这样的:C:\Print Service\PrintService.exe路径没有加引号,c盘根目录下又有Users可写的情况。这个组合几乎就是教科书级的未引用服务路径问题。为了验证风险,我先确认了根目录确实允许普通用户创建文件:# 尝试在C盘根目录创建测试文件 echo test > C:\test_write.tmp能创建成功,说明问题成立。接着我写了一个无害的测试程序,命名为Print.exe,放到C盘根目录,然后通过SCM尝试重启目标服务:sc stop PrintService sc start PrintService我用的测试程序不做任何破坏性动作,只在程序启动时往临时目录里写一个标识文件,记录启动时的账户名。执行完之后去临时目录看,文件已经生成了,归属账户为SYSTEM。到这里,提权路径已经完整验证。需要特别说明的是,这次验证我没有替换原始服务程序,也没有做任何持久化动作,完事之后立即清理了测试文件并恢复了服务状态。整个过程的目的是“证明风险存在”,而不是“利用风险获取更高的系统权限”,这个度非常重要。5.4 Step 3:输出风险报告与修复建议验证完成后,我给模拟环境输出的修复建议是标准的三板斧:给服务路径加双引号,从根源上消除路径解析歧义。收紧C盘根目录ACL,把Users组的写权限移除。最后把服务程序安装目录的权限收敛为仅SYSTEM和管理员可写。三条同时做,才能保证问题彻底闭环。只修其中一条的话,比如路径加了引号但目录权限仍然过宽,可能堵住了一个入口,却留下另一个可以被替换安装目录里其他程序文件的旁路风险。这份报告我在真实项目中已经使用过很多次,逻辑基本不变:写明问题位置、说明影响、给出修复操作,再预估修复后的残余风险。安全评估的价值不在于炫技,而是让运维同学能够拿着报告直接执行修复。6. 如何系统性排查与加固Windows内网提权风险6.1 建立基线:从服务、目录、任务三个维度入手很多管理员问我,到底该怎么系统性地排查提权风险,而不是每次等到出事才亡羊补牢。我的答案是:把提权风险拆成可量化的基线项目,定期自查。这里分享一个我在多个项目中用过的简化版基线清单:检查项检查方法预期结果服务路径是否带引号遍历所有服务ImagePath所有路径要么加双引号,要么不含空格服务权限是否过宽用AccessChk检查Users对服务的权限不允许普通用户修改配置或启动/停止关键服务安装目录ACL检查非系统软件的安装目录权限普通用户仅有读取和执行权限计划任务运行身份筛出以高权限运行的任务执行文件不被普通用户可写注册表自启动项检查HKLM Run与RunOnce键权限普通用户无写入权限系统补丁比对系统版本与补丁缺失情况高优先级漏洞补丁已安装按这个清单做一轮下来,一般能覆盖到大多数常见提权路径。这里要提醒一点:不要在巡检的时候只盯Windows目录和系统文件,真正的问题往往出在第三方软件安装之后留下的“遗产”上。6.2 自动化巡检思路与常用工具人工巡检虽然可靠,但多台机器的时候效率很低。我通常会把上述检查项做成PowerShell脚本,配合AD域环境下下发执行,回传结果集中分析。大致思路是写一个模块化的巡检脚本,每个检查项做成一个函数,输出结构化的JSON数据,再汇集到中央服务器做比对。工具层面,除了前面提到的AccessChk和Process Monitor,PowerShell的Get-Acl和Get-Service是日常用得最多的组合。Sysinternals里面还有一个叫AutoLogon和Autoruns的工具,用来排查自启动项非常高效,可以一键列出所有启动点,再逐一对照是否有可疑项。Autoruns比手动翻注册表要省力得多,输出列表也更结构化。自动化跑完之后,我习惯把高风险项单独拉出来做一次人工复核,因为自动化脚本给出的判断是基于规则的,未必能理解实际业务逻辑。比如某个服务路径没加引号,但那个目录里根本不存在可以预写的入口点,实际风险就是“存在隐患但未利用条件”,这时候人工判断就比脚本的红色警报要准确。6.3 加固落地的正确顺序加固不是把权限一收就完事,操作顺序错了很容易把自己锁在门外,或者影响业务正常运行。我总结的落地顺序是这样的:先从“无害改动”开始,例如给服务路径加双引号,这类改动不会破坏业务,顶多让服务启动更规范,风险极低。然后是“影响面较大但仍可控”的改动,例如调整安装目录权限,改的时候要提前备份原有ACL,并准备恢复方案。最后才动的是“高影响”改动,例如对系统服务权限的收缩、注册表键权限的收紧,这类操作需要挑业务低峰期执行,且必须做好先回滚预案。还有一个很多人容易忽略的点:加固完成之后,必须重新执行一轮巡检脚本,确认之前的风险项已经消失,而不是被“隐藏”了。有时候权限没改干净,出现意外的情况是改了一半,原有配置被还原。所以巡检、加固、再巡检这个闭环,要养成习惯。7. 一条被低估的提权路径:AlwaysInstallElevated前面聊的都是“服务、目录、任务”这些常规面,其实还有一个我经常发现被漏掉的配置项,值得单独拿出来说说,它就是AlwaysInstallElevated。这个配置在组策略或注册表里指定的意思是:任何用户安装Windows Installer包(MSI)时,都以系统最高权限来执行安装过程。如果机器上同时设置了用户级和机器级的AlwaysInstallElevated,普通用户就可以利用它来获取SYSTEM权限。这个问题的黑暗之处在于,它不属于常规的服务、目录、任务检查清单,很容易在巡检时被忽略。排查命令也简单:reg query HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated两条命令返回的数值如果都是1,那实际上等同于把安装器的高权限大门敞开了。测试MSI打包一个可执行程序,然后以普通用户身份运行安装包,系统会以SYSTEM权限执行包内程序,提权就是这么直接。这个配置项被不少人当成“方便管理员部署软件”的内部工具来用,实际上它带来的风险远大于便利。加固方法是在组策略里把这两个值设为“未配置”,并确保不是通过Local Machine Policy强行覆盖的。我之所以把它放在最后,是因为它属于那种“平时想不起来、一旦被人利用就是半个域管权限”的典型项。做加固巡检的时候,这条务必加进你的清单,别省。8. 个人经验:内网提权风险这件事,防守比进攻重要得多做了这些年安全评估,最大的感受是:内网提权这件事,防守端能做的事情远比很多人想象的多,而真正被攻破的案例里,绝大多数都不是因为对方用了多么高深的内核漏洞,而是因为配置问题像漏水的管道一样,太容易被捡漏。每次我写出一份提权风险报告,客户的第一反应往往是“原来还有这种问题”,可见这类风险在日常运维中多么容易被遗漏。如果你是一个系统管理员或者运维负责人,我建议你把文章中提到的服务路径未加引号、服务权限过宽、安装目录权限过大、计划任务被篡改、AlwaysInstallElevated这几类配置问题,做成一份自助检查清单,每季度跑一遍脚本,输出一份风险快照。把排查变成例行工作,远比等到被攻破之后再应急要划算得多。安全评估最怕的不是发现问题,而是问题明明存在却没人去看。
1. 为什么安全评估绕不开Windows内网提权做了这么多年Windows环境的安全评估和加固,我越来越觉得提权这块知识是所有系统管理员和运维同学绕不过去的坎。很多人觉得“提权”就是攻击者的专属话题,实际恰恰相反,一个不清楚内网中有哪些提权路径的管理员,很难真正把服务器的安全基线做好。所谓Windows内网提权,简单说就是在已经拿到一个受限账号的前提下,借助系统本身的漏洞或配置疏忽,把权限从普通用户提升到管理员甚至SYSTEM级别的过程。无论你是负责补丁管理、基线加固,还是做上线前的安全自测,理解这些路径都直接决定了你能不能在问题被利用之前把它堵上。这里我先把边界说清楚。我讲的“实战演示”是在合法授权的安全评估与自查场景中进行的,全程基于模拟环境,核心目的是帮大家看懂风险点长什么样、怎么被触发、以及对应的修复手段。没有授权的测试行为在任何环境下都是违规违法的,这一点做安全相关工作的同学务必时刻记住。从我这几年处理过的各种实际案例来看,Windows内网里的提权风险大致能分成两大阵营:一类是系统或软件漏洞,比如内核漏洞、驱动程序漏洞,这类问题靠打补丁解决;另一类是配置错误,比如服务权限过大、路径写得有歧义、计划任务被篡改,这类问题靠基线管理和权限收敛解决。有意思的是,绝大多数实际出事的场景都是第二类,配置类问题。原因不复杂,漏洞利用往往有门槛,需要匹配的系统版本和可利用代码,而配置错误就像是门锁没换还用默认钥匙,只要有人进去逛一圈就能发现。这篇文章我会从原理讲到实际操作思路,再落到具体的排查与加固方法上。不堆概念,不念手册,尽量把我踩过的坑和验证过的经验直接摆出来。2. 提权的本质与核心分类2.1 纵向提权与横向移动的区别先理清一个容易混淆的概念。内网里所谓的“提权”其实包含两种完全不同的场景,虽然经常被放在一起说,但解决思路天差地别。纵向提权,也叫权限提升,指的是从低权限账户(比如普通域用户、本地Users组成员)往更高权限账户(比如本地管理员、SYSTEM账户)跨越。这种场景通常发生在已经拿下一台机器、但只有个普通权限的时候。目标是突破系统自身的访问控制,拿到更高级别的操作能力。横向移动则不是“升高”而是“平移”——用已经控制的一个账户访问内网中其他机器。横向移动的核心往往不在于漏洞本身,而是凭据复用、弱口令、哈希传递这类问题。虽然横向移动经常和提权配合着用,但它的目标不是提升某台机器上的权限等级,而是扩大影响范围。这篇文章聚焦的是纵向提权,也就是在同一台机器上把权限等级拉高的过程。搞清楚这个边界,后面看各种演示就不会晕。2.2 漏洞型提权和配置错误型提权的本质差异漏洞型提权依赖的是软件层面的缺陷。比如某个内核驱动的函数没有对传入句柄做完整校验,导致普通程序能够以内核态执行代码;或者某个服务存在缓冲区溢出,攻击者可以直接夺取该服务进程的权限。这类问题的特点是:修复起来相对明确,打补丁就行,但依赖具体版本和补丁情况,存在“我打了补丁你不知道,你没打补丁我能测出来”的信息差。配置错误型提权就完全不一样了。它不需要什么高深的利用技巧,纯粹是系统在安装、部署、运维过程中留下了不安全的默认设置或人为疏忽。我遇到过的最典型的例子是:某台服务器装完第三方软件之后,安装目录的权限被设置成Everyonne完全控制,普通用户可以在安装目录里放任意文件,甚至替换掉服务启动时加载的EXE或DLL。这种问题你说它是漏洞吗?不是。但它的危害程度完全不亚于一个远程代码执行漏洞,因为利用难度几乎为零。从防御角度看,配置类风险比漏洞类风险更值得花精力去梳理。因为不像补丁可以一扫了之,配置问题需要一条一条去核对,靠的是细功夫。3. 配置错误提权:最常被忽视的高危风险3.1 服务权限配置不当:普通用户却能控制SYSTEM服务Windows服务是配置错误提权里最经典、也最多发的场景。一个服务一旦被配置为“允许普通用户修改其配置”,那么这台机器的管理员权限基本就形同虚设了。常见的情况是这样的:某个服务的可执行文件路径指向了一个目录,而这个目录对Eve是“完全控制”。此时普通用户虽然不能直接改服务本身,但可以把该路径下的EXE文件替换成自己准备好的程序,然后触发服务重启,新程序就会以SYSTEM权限启动。还有更直接的:服务本身的DACL(Discretionary Access Control List)被改宽了。理论上,服务控制管理器(SCM)中的服务对象默认只允许SYSTEM和管理员组修改配置,但在实际运维中,有些软件为了“方便”安装或更新,会把当前用户或者Everyone加进完全控制列表。我见过某商业化运维软件安装之后,把自身的服务权限放开给Eve,结果任何域用户登进去都可以直接改服务的binPath,再重启该服务,瞬间获得SYSTEM权限。排查这一类问题,我经常用一条命令把服务信息和权限拉出来看:# 查看服务以什么账户运行、路径是什么 wmic service get name,startname,pathname,displayname # 结合AccessChk查看每个服务的权限是否对普通用户过宽 accesschk.exe -uwcqv "Users" *.exeAccessChk是Sysinternals套件里专门用于查看对象权限的工具,实测下来比手动翻SCM配置要直观得多。当你看到某个服务对“Users”组有SERVICE_ALL_ACCESS这种权限时,不用犹豫,直接把它记进整改清单。3.2 未引用的服务路径:空格引发的“路径劫持”未引用的服务路径是另一个我见到过很多次的老问题。它本质上不是因为某个权限配错了,而是因为路径写法不规范,加上Windows在执行程序时对路径解析的特定顺序导致的。假设某个服务的ImagePath被设置成这样的形式:C:\Program Files\Vendor App\Service.exe注意,这个路径没有被双引号包裹。Windows在解析路径时,会把路径按空格切分,然后依次尝试,它会先尝试解析C:\Program.exe,如果找不到,再尝试在C:\Program Files\Vendor目录下找App\Service.exe之类的组合。于是问题就来了:如果C:\根目录对普通用户是可写的,用户就可以在根目录放一个名为Program.exe的文件,然后想办法重启服务,这个恶意程序就会被当作服务程序加载执行,同样以服务配置对应的权限运行。如果服务本身是SYSTEM账户,那恶意程序就是SYSTEM权限。这类问题在国产软件和老旧企业级软件里尤其常见。很多安装包没有用标准格式引号包裹目标路径,安装目录又是常见的带空格的目录,一抓一个准。排查命令也简单:wmic service get name,displayname,pathname | findstr /v "C:\Windows\\" | findstr """把里面不带引号且路径带空格的记录捞出来核对就行。修复方法更加简单,在服务的ImagePath路径两端加上英文双引号,问题直接消失。这里有一个实操心得:加引号不只是为了规避这个提权问题,更是为了避免服务启动时的各种意外行为。路径中含空格的程序如果没加引号,在系统重启或服务异常拉起时可能会出现加载失败,甚至加载错程序,这类问题我在线上环境都见过。所以处理这个配置问题要当成一次性把所有服务的路径规范化来扫,不要只针对有风险的那几个单独改。3.3 计划任务与注册表自启动项:被篡改的运行身份计划任务和注册表启动项是容易被忽略的另一类配置面。Windows的计划任务可以指定运行账户、运行等级和可执行文件路径,如果某个任务的配置允许普通用户修改,或者任务的执行目录权限过宽,那普通用户就能通过替换任务程序获得高权限运行的机会。计划任务不像服务那样有直观的权限属性让你直接查,需要结合具体任务的XML配置来看。在评估过程中我会重点关注两类任务:一类是系统安装第三方软件时自动注册的任务,这类任务经常以SYSTEM或Administrator身份运行,但任务的创建者对执行文件的保护几乎没有做,执行文件位于普通用户可以写入的目录。另一类是用户自己创建的自动化任务,为了图方便勾选了“不管用户是否登录都要运行”,并选择了“使用最高权限运行”,然后执行体放在了一个权限敞开的位置。排查计划任务的基线命令如下:# 列出所有计划任务及其运行身份 schtasks /query /fo LIST /v | more拿到清单后,核对每一条以高权限运行的任务,看它的执行程序路径是否在可被篡改的目录下,以及任务本身的安全选项是否允许普通用户更改。如果有可疑项,直接看任务的XML定义:schtasks /query /tn "任务名" /xml注册表自启动项的原理与之类似。常见的位置是HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run和HKEY_CURRENT_USER\...\Run。如果HKLM下的Run键对于普通用户是可写的,那么下次该用户登录时,写入的启动程序就会以该用户的权限运行。有些场景下,如果启动程序的路径还指向了一个普通用户可写的目录,那就安全了。我处理过的一个案例是某设备的配套驱动程序在Run键里指向了一个驱动工具目录,而该目录对Everyone完全控制,任何人登录都会加载这个目录里的程序,等于给了所有使用者一个后续执行入口。注册表权限的排查比服务更隐蔽,因为默认摸不到,要把目标键的权限子项逐个展开。用regedit定位到目标键后,右键->权限,核对Users或Authenticated Users是否有写入权限。或者直接通过PowerShell走一层ACL检查:Get-Acl -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" | Format-List看到“Users”或者“Everyone”带“Write”或“FullControl”的,就是需要处理的对象。3.4 可写路径与DLL加载:文件系统权限的连锁反应最后再补一个经常被忽略的配置类问题:可写路径导致的DLL劫持。Windows程序在加载DLL时遵循一套搜索顺序,从“程序所在目录”到“系统目录”,再到“当前工作目录”,再到“PATH环境变量中的目录”。如果某个程序的启动方式带有可控的工作目录,或者程序本身的依赖DLL不在系统目录而在当前目录,那么一个可写的工作目录就有可能变成劫持加载的入口。我印象很深的一个案例是某老旧的打印管理系统,安装目录是C:\PrintServer\,这个目录对普通用户完全可写。程序启动时会在首目录加载某个第三方日志模块,日志模块的DLL文件却可以被直接替换。当时为了验证风险,我只写了个简单的DLL,加载后做一条日志记录并尝试以当前进程权限创建测试文件。不出意外,进程以SYSTEM身份运行,测试文件直接落在了系统临时目录里。整个过程不需要任何漏洞利用,纯粹是文件系统权限和DLL搜索机制叠加出来的结果。这类问题的排查思路是找出所有“高权限进程启动时可能加载的DLL”所在的路径,再进行可写权限检查。实际操作中,我会优先把服务类的exe跑起来之后,通过Process Monitor抓一下它加载了哪些非系统目录的DLL,再逐一检查对应目录权限。这个操作用PCHunter或Process Monitor都能做到,实测下来Process Monitor的过滤规则更适合这种场景,直接按进程名过滤,把Result为“NAME NOT FOUND”的项筛出来看,往往能发现意外惊喜。修复方面就一句话:程序安装目录和所有子目录的权限只保留SYSTEM、管理员和必要的服务账户,普通用户一律只给读取和执行。别小看这一步,它能一次性封堵掉服务替换、DLL劫持、以及自启动程序篡改的一大批问题。4. 漏洞型提权:补丁管理与漏洞利用的博弈4.1 内核提权漏洞的基本逻辑配置错误之外,漏洞型提权是另一个核心方向。Windows内核提权漏洞的经典模式是:系统的某个内核函数在处理特定对象时,没有对调用的权限或传入的参数做完整校验,导致低权限进程可以借助该函数影响内核状态,最终获得任意代码执行的能力。整个过程对普通用户来说像一个黑盒,但这并不妨碍我们从补丁管理的角度来理解和处置它。对于安全评估来说,关注漏洞型提权的重要思路是“识别可被利用的特征”。在拿到一台机器的权限后,比较直接的办法是通过系统版本、已安装补丁列表与公开漏洞库进行比对,找出缺少的补丁。这个过程有点像在盘点一台机器的“免疫缺口”,补丁管理做得越差,暴露面就越大。常用的信息采集思路如下:# 查看系统版本和补丁信息 systeminfo wmic qfe list brief /format:table把输出的KB编号与近期公开的Windows提权漏洞对应列表比对,如果发现某个漏洞利用代码公开已久,而目标机器对应的补丁没安装,那这台机器的风险等级就直接拉高了。4.2 为什么不建议在真实环境里跑漏洞利用代码网上流传的各种Windows内核漏洞利用代码,很多是PoC性质,稳定性参差不齐。一个成熟的评估人员不会在客户的生产环境里直接跑这类利用工具。原因很简单:内核漏洞利用一旦触发BSOD(蓝屏),影响的就不只是一个进程,而整台服务器都会宕机,这是任何评估工作都承担不起的后果。退一步说,即便不考虑稳定性,直接执行漏洞利用代码也是“过度授权”的行为。评估的目的通常是发现并证明风险存在,而不是真的攻陷一台机器。用系统版本和补丁状态来判定风险,已经足够支撑整改建议了,不需要走完整利用链来证明“能得手”。这个原则我保持至今,也是在带新人时反复强调的一条底线。4.3 漏洞利用的防御视角:补丁之外还需要什么如果没有打上最新补丁,这类漏洞型提权几乎是防不住的,因为它利用的是系统自身的缺陷。补丁管理之外能做的有限,但仍然有几件事值得做:第一,缩小低权限账户的可利用空间。很多内核漏洞利用代码在触发前需要做一些前置操作,比如创建特定类型的对象句柄、加载特定的驱动映射权限。如果使用标准账号的配置上做了限制(比如禁止加载未签名驱动、限制访问内核设备对象),可以在一定概率上干扰利用过程。但这些属于“深水区”加固,普通企业环境实施成本很高。第二,启用漏洞利用缓解机制。Windows的“Exploit Protection”和“攻击面减少(ASR)”规则可以针对常见的内核利用手法做阻断,例如阻止通过特殊方式注入代码。虽然不一定对每个漏洞都有效,但实测下来确实能挡住一部分利用链的前置操作。第三,也是最重要的:建立可落地的补丁节奏。不在Windows Update弹出来之后第一时间热更,而是按“每周固定维护窗口批量更新”的方式来管理,同时做好更新前的兼容性验证,这几乎就是漏洞型提权问题的终极解法。我见过很多机构不是不更新,而是更新策略形同虚设,定了每周一次实际变成季度甚至半年度一次,等出问题时才想起来。5. 实战演示:一次授权环境下的提权风险自测5.1 场景设定与初始条件下面用一个模拟环境来演示整个风险自测的流程。环境里有一台Windows Server 2019,安装了三方打印管理系统,对外服务账户是普通域用户。初始假设是:评估人员通过某种合法途径(比如一次弱口令自查中发现该普通用户的密码被重置为强度很弱的默认值)拿到了这个普通账户,可以在本机登录并执行常规操作。这个场景非常贴合实际,因为内网中真正被突破的案例里,很大比例都是从某个低权限账户开始的,随后通过配置问题一路提权到管理员甚至域管。模拟环境的初始条件如下:条件说明系统Windows Server 2019登录账号域普通用户目标验证本机提权路径并输出加固建议软件三方可信服务软件(有未引用路径问题)5.2 Step 1:信息收集与风险面绘制登录后的第一件事是快速把系统概况和可能的高权限运行对象摸一遍。我习惯分成三个方向进行:服务层面,重点看是否存在未引用的路径以及以高权限运行的非系统服务。输出之后可以写个小循环脚本,把所有不带引号且路径含空格的exe服务标红。系统层面,用systeminfo确认补丁状态和系统版本,作为后续漏洞比对的基础。敏感文件层面,检查安装目录、临时目录、公共目录的ACL权限,看是否有Everyone或Users可写的情况。这个环节我不推荐上来就跑一堆扫描工具,一方面动静大,另一方面容易误伤。人工配合轻量命令去摸,往往能得到更清晰的风险面。5.3 Step 2:服务路径问题的验证在模拟环境里,我很快就注意到了打印管理服务,它的路径是这样的:C:\Print Service\PrintService.exe路径没有加引号,c盘根目录下又有Users可写的情况。这个组合几乎就是教科书级的未引用服务路径问题。为了验证风险,我先确认了根目录确实允许普通用户创建文件:# 尝试在C盘根目录创建测试文件 echo test > C:\test_write.tmp能创建成功,说明问题成立。接着我写了一个无害的测试程序,命名为Print.exe,放到C盘根目录,然后通过SCM尝试重启目标服务:sc stop PrintService sc start PrintService我用的测试程序不做任何破坏性动作,只在程序启动时往临时目录里写一个标识文件,记录启动时的账户名。执行完之后去临时目录看,文件已经生成了,归属账户为SYSTEM。到这里,提权路径已经完整验证。需要特别说明的是,这次验证我没有替换原始服务程序,也没有做任何持久化动作,完事之后立即清理了测试文件并恢复了服务状态。整个过程的目的是“证明风险存在”,而不是“利用风险获取更高的系统权限”,这个度非常重要。5.4 Step 3:输出风险报告与修复建议验证完成后,我给模拟环境输出的修复建议是标准的三板斧:给服务路径加双引号,从根源上消除路径解析歧义。收紧C盘根目录ACL,把Users组的写权限移除。最后把服务程序安装目录的权限收敛为仅SYSTEM和管理员可写。三条同时做,才能保证问题彻底闭环。只修其中一条的话,比如路径加了引号但目录权限仍然过宽,可能堵住了一个入口,却留下另一个可以被替换安装目录里其他程序文件的旁路风险。这份报告我在真实项目中已经使用过很多次,逻辑基本不变:写明问题位置、说明影响、给出修复操作,再预估修复后的残余风险。安全评估的价值不在于炫技,而是让运维同学能够拿着报告直接执行修复。6. 如何系统性排查与加固Windows内网提权风险6.1 建立基线:从服务、目录、任务三个维度入手很多管理员问我,到底该怎么系统性地排查提权风险,而不是每次等到出事才亡羊补牢。我的答案是:把提权风险拆成可量化的基线项目,定期自查。这里分享一个我在多个项目中用过的简化版基线清单:检查项检查方法预期结果服务路径是否带引号遍历所有服务ImagePath所有路径要么加双引号,要么不含空格服务权限是否过宽用AccessChk检查Users对服务的权限不允许普通用户修改配置或启动/停止关键服务安装目录ACL检查非系统软件的安装目录权限普通用户仅有读取和执行权限计划任务运行身份筛出以高权限运行的任务执行文件不被普通用户可写注册表自启动项检查HKLM Run与RunOnce键权限普通用户无写入权限系统补丁比对系统版本与补丁缺失情况高优先级漏洞补丁已安装按这个清单做一轮下来,一般能覆盖到大多数常见提权路径。这里要提醒一点:不要在巡检的时候只盯Windows目录和系统文件,真正的问题往往出在第三方软件安装之后留下的“遗产”上。6.2 自动化巡检思路与常用工具人工巡检虽然可靠,但多台机器的时候效率很低。我通常会把上述检查项做成PowerShell脚本,配合AD域环境下下发执行,回传结果集中分析。大致思路是写一个模块化的巡检脚本,每个检查项做成一个函数,输出结构化的JSON数据,再汇集到中央服务器做比对。工具层面,除了前面提到的AccessChk和Process Monitor,PowerShell的Get-Acl和Get-Service是日常用得最多的组合。Sysinternals里面还有一个叫AutoLogon和Autoruns的工具,用来排查自启动项非常高效,可以一键列出所有启动点,再逐一对照是否有可疑项。Autoruns比手动翻注册表要省力得多,输出列表也更结构化。自动化跑完之后,我习惯把高风险项单独拉出来做一次人工复核,因为自动化脚本给出的判断是基于规则的,未必能理解实际业务逻辑。比如某个服务路径没加引号,但那个目录里根本不存在可以预写的入口点,实际风险就是“存在隐患但未利用条件”,这时候人工判断就比脚本的红色警报要准确。6.3 加固落地的正确顺序加固不是把权限一收就完事,操作顺序错了很容易把自己锁在门外,或者影响业务正常运行。我总结的落地顺序是这样的:先从“无害改动”开始,例如给服务路径加双引号,这类改动不会破坏业务,顶多让服务启动更规范,风险极低。然后是“影响面较大但仍可控”的改动,例如调整安装目录权限,改的时候要提前备份原有ACL,并准备恢复方案。最后才动的是“高影响”改动,例如对系统服务权限的收缩、注册表键权限的收紧,这类操作需要挑业务低峰期执行,且必须做好先回滚预案。还有一个很多人容易忽略的点:加固完成之后,必须重新执行一轮巡检脚本,确认之前的风险项已经消失,而不是被“隐藏”了。有时候权限没改干净,出现意外的情况是改了一半,原有配置被还原。所以巡检、加固、再巡检这个闭环,要养成习惯。7. 一条被低估的提权路径:AlwaysInstallElevated前面聊的都是“服务、目录、任务”这些常规面,其实还有一个我经常发现被漏掉的配置项,值得单独拿出来说说,它就是AlwaysInstallElevated。这个配置在组策略或注册表里指定的意思是:任何用户安装Windows Installer包(MSI)时,都以系统最高权限来执行安装过程。如果机器上同时设置了用户级和机器级的AlwaysInstallElevated,普通用户就可以利用它来获取SYSTEM权限。这个问题的黑暗之处在于,它不属于常规的服务、目录、任务检查清单,很容易在巡检时被忽略。排查命令也简单:reg query HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated两条命令返回的数值如果都是1,那实际上等同于把安装器的高权限大门敞开了。测试MSI打包一个可执行程序,然后以普通用户身份运行安装包,系统会以SYSTEM权限执行包内程序,提权就是这么直接。这个配置项被不少人当成“方便管理员部署软件”的内部工具来用,实际上它带来的风险远大于便利。加固方法是在组策略里把这两个值设为“未配置”,并确保不是通过Local Machine Policy强行覆盖的。我之所以把它放在最后,是因为它属于那种“平时想不起来、一旦被人利用就是半个域管权限”的典型项。做加固巡检的时候,这条务必加进你的清单,别省。8. 个人经验:内网提权风险这件事,防守比进攻重要得多做了这些年安全评估,最大的感受是:内网提权这件事,防守端能做的事情远比很多人想象的多,而真正被攻破的案例里,绝大多数都不是因为对方用了多么高深的内核漏洞,而是因为配置问题像漏水的管道一样,太容易被捡漏。每次我写出一份提权风险报告,客户的第一反应往往是“原来还有这种问题”,可见这类风险在日常运维中多么容易被遗漏。如果你是一个系统管理员或者运维负责人,我建议你把文章中提到的服务路径未加引号、服务权限过宽、安装目录权限过大、计划任务被篡改、AlwaysInstallElevated这几类配置问题,做成一份自助检查清单,每季度跑一遍脚本,输出一份风险快照。把排查变成例行工作,远比等到被攻破之后再应急要划算得多。安全评估最怕的不是发现问题,而是问题明明存在却没人去看。