前阵子帮一家制造企业做内网安全排查,登进他们的域控一看,一个离职快两年的运维部门主管账号居然还挂在“域管理员”组里,密码策略因为长期没登录而被跳过。我当场就绷不住了——这哪是账号,分明是给攻击者留的后门钥匙。后来顺着这个账号一翻,发现类似的“僵尸账号”远不止一个,有的还连着 ERP 系统和堡垒机的高权限通道。
这类问题在行业里有个很形象的名字:孤儿账户,也叫僵尸账号。它指的是员工离职、转岗或外包团队撤场后,对应账号没有被及时禁用或删除,继续在企业身份系统里残留。如果只是普通权限还好,最怕的是那些挂在高权限组里的账号,黑客拿到它就像捡到一张没人报失的门卡,进出都畅通无阻。这篇文章我从排查思路、实操方法到长效治理,把我踩过的坑和验证过有效的手段都摊开讲一遍,适合安全运维、系统管理员、以及任何对账号安全有心无力的人参考。
1. “僵尸账号”到底是什么?先从一次排查说起
1.1 孤儿账户的真实画像
我记得当时在这家制造企业做账号专项排查,第一步不是直接去域控里捞数据,而是找人事那边要了一份近三年的离职名单。名单拿到手后和 AD 里的有效账号一比对,结果让我吃了一惊,离职名单上的 47 个人里,还有 9 个账号处于启用状态,其中 3 个竟然还在“域管理员”或者“本地管理员”这类敏感组里。换句话说,这些账号和数据完全没有人维护,却保留着大量系统权限。
这里是关键:孤儿账户不是简单的“残留账号”,它有几个共同特征。第一,长期没有登录记录,密码失效时间被跳过,因为没人用所以也不会有人发现异常。第二,没有关联到真实在职员工,没有办法通过部门交接或者申请流程来追踪。第三,权限通常没有随人员离职而降级,反而因为入职年限久、历史职责重,往往留在权限金字塔的高层。第四,没有任何人在运营层面为这个账号负责,它就像一栋大楼里没有登记在册的空房间,门锁还完好,但谁都能撬开。
说白了,“僵尸账号”在技术层面是身份管理失效的产物,但它的根源往往在流程层面。很多企业连离职名单都不是及时同步到 IT 的,人事流程和安全流程各走各的路,账号也就不可能及时被清理。做排查时如果只盯技术手段,容易越查越乱,因为头绪太多;我习惯先把人、账号、权限这三层关系拉通,后面所有操作才有抓手。
1.2 为什么攻击者专门盯着这种账号下手
攻击者突破一个企业的安全防线,不一定非要正面硬刚防火墙或者挖 0day,很多时候他们更喜欢去捡“没人管的钥匙”。为什么孤儿账户会成为头号突破口?因为它在整个安全体系里是一个几乎完美的攻击跳板。
正常账号有主人盯着,登录时间异常、登录地点漂移、半夜跑批命令,都会被安全运营发现。孤儿账户反正没人注意,黑客用它做什么都不会引发警觉,这是“无主”带来的天然隐身性。更麻烦的是权限往往还很高,很多高权限账号是早期系统搭建时建立的,员工离职后权限没有跟着业务一起收走,于是黑客拿到这个账号后,直接从普通用户权限跳到管理员权限。还有一点是凭证不会被重置,账号长期不被使用,没人想起去改密码,密码可能还是几年前的弱密码,或者已经泄露到各种公开的数据库里。
从攻击者视角看,一个账号同时具备“长时间无人使用、权限高、密码可能泄露”这三个条件,那几乎就是明牌了。他们用这种方式进入企业内网,拿到域管权限后再做凭证转储,接着横向移动到服务器、数据库甚至备份系统。整个过程可能持续几个月都没人发现,因为一开始登录用的就是一个被所有人遗忘的账号。所以我在做安全建设时有个原则:宁可少给权限,也必须盯住权限;宁可账号删晚了,也不能让高权限账号永久失联。
2. 高权限孤儿账户为什么是“头号突破口”
2.1 权限继承让账号的实际权力比看起来更大
很多运维朋友有一个误区,认为只要不把账号直接放进“域管理员”组,它就没什么危险。这个想法特别危险,因为企业目录系统里的权限往往有嵌套关系和继承机制。一个账号可能只被加进了“服务器操作员”这个普通管理组,但这个组本身又继承了另一层委派权限,或者它所在的 OU 上挂了一条“重置所有用户密码”的委派规则。你看到的是表面一个组,实际拿到的是几十台服务器的控制权。
这种权限上的“倍增效应”,在离职账号身上会被放大。为什么?因为离职账号往往是在系统上线的特殊时期创建的,当时业务紧急、人员调整频繁,为了图省事经常直接授予大范围权限,而后来没有人重新评估过这些权限是否还合理。加上账号本身没有活跃用户去关心权限变化,安全部门也很难追踪“谁在什么时候给谁授权”,攻击者钻进这个缝隙后就拥有了合法的管理员身份。合法身份是做安全对抗里最值钱的东西,因为它不会触发告警,至少不会触发通常意义上的登录异常告警。
我记得在一次模拟演练中,安全团队用一个残留高权限账号成功登录了运维审计系统,随后从那里导出了几十台核心服务器的账号密码表。整个过程没有利用任何漏洞,纯粹就是“账号权限残留”加上“凭据随处存放”。这件事之后我们内部形成一个共识:账号权限排查不能只看账号是不是启用,要看它的有效权限、嵌套组关系、委派条目和历史申请记录。
2.2 没人盯着,失活账号的监管真空
“无人维护”这四个字看起来轻飘飘,放在账号安全里就是致命的监管空白。每一个活跃账号都对应一个真实的人,人有异常行为可以被 HR 问询,有设备指纹、刷卡记录、办公地点做交叉验证,但孤儿账户对应的“人”已经不存在了,这就是它最被攻击者喜欢的地方——没有任何行为基线。
行为基线是什么意思?一个正常用户每天登录时间大概在几点、常用哪台机器、习惯访问哪些服务器,这些都是可以基于历史记录建立的。安全设备会基于这些数据判断当前登录是否异常。孤儿账户没有这种基线,因为它几乎不登录,一旦被黑客启用并开始操作,系统完全没有历史数据的参照,很难判断这个账号的行为是“第一次出现”还是“第一次异常出现”。更麻烦的是,账号长期不用还会绕过很多密码策略,比如强制改密周期、登录失败次数锁定等等。
从实际案件里总结出来的规律,黑客拿到失活账号后的操作路径通常是:第一步登录内网,第二步枚举域内其他账号和权限组,第三步找到更高权限的凭据,第四步关掉安全的日志或者针对性的隐藏自己。整个过程里,失活账号扮演的是一个“合法身份”跳板,它给了攻击者在潜伏期内不被重点关注的保护伞。我们后来在部署账号监控时专门增加了一条规则:任何启用状态但超过 90 天没有登录的账号,一旦出现登录行为,立刻产生中高危告警并同步给安全负责人。这个规则上线后效果非常显著,因为正常账号很少出现这种模式,而攻击者偏偏一定触发这种模式。
2.3 一个典型的入侵路径推演
纸上谈兵讲这么多,不如把一条真实出现过的入侵路径串起来讲。某家企业的域控日志里曾经留下过这样的痕迹:攻击者用一个小写字母加数字组合的密码,成功登录了一个停用状态下被重新启用的账号,而这个账号居然是“域管理员”组的成员。背后的事实是,这个账号对应的员工两年前离职,但当时只是从邮箱系统里删除了账号,AD 里的同一个身份因为不知道是谁维护,反而留了下来。系统里甚至存在两条记录,一条标记已删除,另一条继续有效,管理界面上看到的是“已清理”,实际上后门还开着。
拿到这个账号后,攻击者第一晚只做了一件事:查询域内所有管理员的最后登录时间。这个查询本身不敏感,权限允许范围内就可以执行。第二天晚上,他用同一账号建立了一条计划任务,定时把域控上的安全日志事件 4732、4733 循环删除。之后才真正开始横向移动,从域控管理员会话里提取了更高权限服务的凭证,往服务器管理组、数据库管理组、甚至备份系统里埋了几个新账号。等我赶到现场做应急时,最早的入侵痕迹已经被清掉了,只剩下他遗漏在某个备份服务器上的一段 PowerShell 历史记录。
这个案例里最让人后背发凉的细节是:攻击者从头到尾用的所有跳板,都是企业自己残留的合法账号,绕过了绝大多数基于签名和特征检测的安全设备。所以我才反复强调,账号治理不是合规应付,它是防线里最容易被人忽视、却最容易被人利用的一环。安全建设的优先级里,把账号生命周期管好,性价比远高于盲目上各种高价检测设备。
3. 怎么把藏起来的孤儿账户挖出来?说点能直接落地的排查方法
3.1 从身份源开始:把离职名单和账号清单对齐
我想先纠正一个习惯性错误:很多运维拿到项目第一反应是去域控里查“最后登录时间超过 X 天的账号”,然后把它们一批批禁用。这样做方向不错,但容易误伤服务账号、共享账号,还会漏掉那些最近刚被攻击者激活利用的“僵尸账号”。更稳妥的打法,是先建立身份源对照关系,把人事系统中的离职名单、外包合同结束日期、内部转岗记录,与 AD 账号、企业应用账号、数据库账号做交叉比对。
具体实操上,我会先整理三份清单。第一份是员工在职名单,从 HR 系统导出来,包含工号、姓名、部门、入职日期、离职日期。第二份是账号清单,从 AD 和各个应用系统导出,包含账号名、创建时间、最后登录时间、所属组、启用状态。第三份是权限清单,重点导出“域管理员”“企业管理员”“架构管理员”等敏感组里的成员,以及各大系统本地管理员组里的账号。把这三份清单放到一张透视表里,用员工工号作为关联键,一眼就能找出“账号存在但员工已经离职”的匹配项。
这里要特别说明,人员信息会涉及隐私边界,排查时我只用工号和账号做关联,不去额外读取员工的薪资、绩效这些无关数据。权限上也需要提前和行政、人事部门确认,避免安全排查触碰到不该碰的数据。这一步做扎实,后面所有的人工复核和处置动作都能省下大量时间。
3.2 用最后登录时间做第一轮筛选
在身份源对照之外,还需要从“活跃度”维度做一轮筛选。AD 里有一个用户属性叫 lastLogonTimestamp,它会记录账号最后一次登录域的时间,但它有两个先天缺陷:一是默认有 14 天的精度损失,二是域控制器之间同步可能会有延迟。所以我不建议用这个字段做精确判断,但用来做粗筛非常合适。可以直接在域控上跑一条 PowerShell,把超过 90 天没登录的用户提取出来,再结合启用状态一起看。
# 在域控上使用 ActiveDirectory 模块执行 $cutoffDate = (Get-Date).AddDays(-90) # 提取超过90天未登录且处于启用状态的用户,排除部分基础服务账号名 $inactiveUsers = Get-ADUser -Filter { (Enabled -eq $true) -and (lastLogonTimestamp -lt $cutoffDate) } -Properties Name, SamAccountName, lastLogonTimestamp, MemberOf, Department foreach ($user in $inactiveUsers) { $lastLogon = [datetime]::FromFileTime($user.lastLogonTimestamp) [PSCustomObject]@{ 账号名 = $user.SamAccountName 姓名 = $user.Name 部门 = $user.Department 最后登录 = $lastLogon.ToString('yyyy-MM-dd HH:mm') 所属组数 = @($user.MemberOf).Count } } | Sort-Object 最后登录 | Format-Table -AutoSize跑完这条命令之后,拿到的列表只是候选清单,还不能直接禁用。需要做人工分类:排除掉那些明确标记为服务账号、系统内置账号、应急备用账号的条目;对剩下的账号再查一遍最后一次修改密码的时间和创建时间。如果一个账号超过 90 天没登录、创建时间又很早,还挂在敏感组里,基本可以列进重点核查名单。后面要做的就是把这份名单提交给对应的业务负责人,确认账号是否还需要保留。这个步骤千万不要跳过,因为你不可能了解每个账号的业务背景,盲禁容易把正在跑批的任务打断。
3.3 查有效权限:别被表面清单骗了
排查孤儿账号最见功底的地方,是如何准确判断账号的实际权限。只看“MemberOf”属性只能知道直接成员关系,但 AD 里还有嵌套组、受保护组、委派权限、GPO 应用这些间接权限。同一个账号,可能表面上只在“研发部成员”组里,实际上因为研发部组里套着一个服务器管理员组,它就能登录域内所有研发服务器并重启服务。这种间接权限,靠肉眼盯组名单很容易漏。
我建议在排查阶段引入一个查询“有效权限”的步骤。Windows 的 ADUC 管理工具里自带“属性编辑器”和“安全”标签,可以查看账号对某个 OU 或对象的有效权限;但账号量大的时候,手工点效率太低。更实用的方式是用 PowerShell 调Get-ADPrincipalGroupMembership递归展开所有组关系,再用dsacls查看指定对象的 ACL 继承情况。
# 展开某个账号所属的所有组,包括嵌套组 $account = Get-ADUser -Identity "zhangsan" -Properties MemberOf $allGroups = @() $pending = @($account.MemberOf) while ($pending.Count -gt 0) { $groupDN = $pending[0] $pending = $pending[1..($pending.Count-1)] $group = Get-ADGroup -Identity $groupDN -Properties MemberOf $allGroups += $group.Name $pending += $group.MemberOf } $allGroups | Sort-Object -Unique如果团队里有预算或者已经部署了身份治理平台,那直接用平台自带的权限分析报表会更省事。没有也没关系,用上面的命令把结果导成 CSV,再和企业内部的资产清单合并,重点盯那些“组里有敏感关键字、账号归属人已经离职、最后登录时间超过半年”的组合。这一步跑完,基本能还原攻击者眼里的画面,知道哪些账号是可以直接通往核心系统的金钥匙。
3.4 工具选型:没有预算怎么先跑起来
一讲到账号治理,很多企业第一反应是买商业产品,但现实中预算往往没那么快批下来。所以我把工具方案分成三个梯度,按成本从低到高排列。
第一梯度是纯手工脚本,适合账号数量在几百至一千左右的场景。上面的 PowerShell 命令再加一个输出 CSV 的循环,就能支撑一次比较完整的排查。劣势是没法做到持续自动化、实时告警,所以只适合作为“体检”手段,不适合作为“长期心电监护”。第二梯度是开源或免费工具,比如用或平台里的密码审计模块做弱口令检查,用系统自带的安全基线扫描工具检查敏感组变更。这部分能实现周级粒度的批量核查,已经能覆盖大部分中小企业的需求。第三梯度才是商业身份治理平台,适合账号规模上万、组织架构复杂的场景,它可以自动拉取 HR 数据、自动发起离职流程、自动回收权限并留痕审计。
我个人的建议是:先别急着上第三梯度,很多企业连“账号责任人字段”都没有维护好,买平台回来也是空壳。先把前两个梯度跑熟,积累出准确的责任人清单和资产清单,再谈上平台。否则平台上线后,还要花大量时间补数据,项目容易烂尾。
4. 定位到孤儿账户之后怎么处置?流程和长效治理同样重要
4.1 先冻结再处置,别一删了之
当我筛出重点账号后,第一反应肯定是“把这账号禁用了”。这里我要提个醒:禁用账号只是第一步,而且禁用不等于删除,删除也不等于安全。实际处置动作一定要按顺序来,不能跳步。
推荐的处理顺序是:冻结、重置密码、移除高权限组、转移业务归属、隔离观察、最终归档或删除。冻结就是禁用账号,这一步能立刻阻断攻击者利用凭证登录;随后立刻重置密码,把原来可能泄露的旧凭证变成无效;接着移除它所在的高权限组,把权限从根上收掉;再去找业务负责人确认账号对应的业务是否还在运行,如果在运行,需要先把任务迁移到新账号;观察一到两个业务周期,确认没有报障后,再把这个账号移动到“已禁用”的 OU 里留档。
为什么不能上来就删除?因为很多企业跑批任务、定时同步、系统间接口还在用这些“看起来没人用”的账号。我曾经遇到过一家企业,清理账号时直接删了一个定时任务关联账号,结果当天夜间批量任务大面积失败,生产系统的第二天业务报表全乱了。从那以后我定了一条死规矩:清理动作必须有观察期,没有业务确认的账号一概先禁后观,不做物理删除。
4.2 降权、转移、清权限:不是所有账号都得删
处理过程中会碰到一个更棘手的问题:有些“僵尸账号”关联的业务其实还在运转,只是维护它的员工转岗或离职了。这种情况如果简单禁用,等于把业务也停摆,所以不能一刀切。我的处置字典里有一组常用动作:账号保留、权限降级、所有权转移。
首先判断账号是否还有业务依赖,找系统负责人或是应用 Owner 去确认,邮件里写清楚“这个账号关联的应用是否仍在使用”“是否有替代账号”。如果确认仍在用,那就走“所有权转移”流程:把账号纳入现有运维人员名下,更新联系人和负责人字段,并且把密码改掉、绑定新的双因素认证。接下来是“权限降级”:把原来挂着的域管组、本地管理员组全部移除,只保留完成任务所需的最小权限。如果业务后续确认不再使用,就再走禁用和归档流程。
这里想强调一个容易被忽视的点:权限降级之后一定要复查继承关系。移除直接组成员容易,但嵌套组里的权限可能在下一层被重新带回来。复查方式可以用第 3.3 小节里的展开命令,反复确认账号的有效权限已经降到预期范围。处置记录要留痕,包括谁批准的、什么时候做的、权限变更前后的差异清单,这样后续被审计时才不会被质疑。
4.3 把治理动作固化到流程里
企业账号治理靠一次专项排查是不够的,真正的关键是把它变成常态化流程。我个人在项目里推过的机制有两个,一个是“季度账号健康度检查”,另一个是“离职账号自动触发稽核”。
季度检查的内容比较固定:比对 HR 离职名单与账号清单;筛出 90 天以上未登录的启用账号;检查敏感组成员是否和在职名单一致;抽查账号权限是否有不合理增长。这套检查的产出是一张清单,按风险从高到低排好,提交给对应的业务负责人逐项确认。只要坚持两个季度,很多历史包袱就会被清理掉,新产生的残留也会明显减少。
这里整理了一份我常用的季度复核清单,可以直接抄作业:
| 检查项 | 数据来源 | 判定标准 | 处置动作 |
|---|---|---|---|
| 离职账号残留 | HR 系统 + AD | 离职日期早于核查日且账号仍启用 | 禁用 + 移出敏感组 |
| 长期未登录账号 | AD lastLogonTimestamp | 超过 90 天未登录且非服务账号 | 列为候选清理名单 |
| 敏感组成员变更 | 域控安全日志 | 组内成员变化未走审批流程 | 回滚变更 + 复核权限 |
| 服务账号密码状态 | 密码管理平台 | 超过密码有效期周期 | 强制重置并关联负责人 |
| 特权账号责任人 | 人员信息库 | 责任人字段为空 | 挂到运维负责人名下 |
另一个机制是在企业微信或 OA 流程里加一道“离职审批锁”:HR 在系统里发起离职流程时,自动通知 IT 管理员在一个工作日内完成账号禁用、邮箱转发、应用权限回收。这个流程看起来简单,但很多企业做不到,因为审批流里的 IT 节点经常被人事部门漏掉。把话说透一点:流程断点的问题,靠工具很难完全兜住,需要从上到下明确账号回收的时限和责任人。只要有一个人明确负责“最后的删除”动作,整个体系就不会滑向失控。
5. 顺手整理的常见问题和排错记录
5.1 最后登录时间不准?问题可能出在这
很多朋友跑完 PowerShell 后反馈,明明账号昨天还在用,查询结果却显示好几个月没登录。这个不奇怪,lastLogonTimestamp 的复制策略本身有延迟,而且它不是实时更新,域控之间同步没完成时,查询到的值就可能不是最新数值。更精确的字段是 lastLogon,它不缓存、直接从目标域控读取,但缺点是只代表“你当前查询的这台域控”视角,多域控环境下需要汇总所有域控的值再取最大值。
如果账号数量大,需要更高精度,可以用Get-ADUser加-Server参数逐台域控跑,再做并集。精度和性能的平衡要按实际规模取舍,账号五千以上的环境不建议每台域控全量扫,会拖慢目录服务的响应。折中方案是:先用 lastLogonTimestamp 做粗筛,缩小范围后再对候选账号用 lastLogon 做精确确认。
还有一类情况是账号虽然没登录过域,但一直在用本地账号登录服务器。这不属于 AD 账号范畴,需要把排查范围扩大到每台服务器的本地账户。比较省力的做法是先扫描资产清单,把开放了 3389 或 SSH 端口的主机找出来,再集中检查本机管理员组名单,避免漏掉本地管理员这层风险。
5.2 如何避免误杀服务账号和应急账号
清理行动里最怕误伤,尤其是一些名字看起来像“僵尸账号”、其实承担着无人值守任务的账号。我遇到过数据库同步账号三个月没登录,实际上每天都在通过服务账户接口跑数,只是因为不按交互式登录算所以时间一直不更新。这种账号一旦被批量禁用,第二天就有业务告警轰炸。
我的处理习惯是建立一份“例外账号清单”,把服务账号、系统内置账号、灾难恢复备用账号、计划任务关联账号提前标记好,并且给它们挂上明确的负责人。清单里要写清楚账号用途、允许的登录方式、是否纳入 90 天未登录清理范围。有了这份白名单,批量筛选命令在执行时就加上Where-Object { $_.Name -notin $excludeList },把误伤概率降到最低。
如果还是不小心误伤了服务账号,也不用慌,处置时不要直接删除账号,走禁用流程还能随时启用。这也是我前面强调“先禁后删”的重要原因。应急账号也要单独考虑,某些核心系统需要保留紧急本地管理员账号,这种账号可以长期不登录,但必须放在企业级密码保险箱里,密码定期轮换,并且只能在紧急审批流程下取用。它虽然也满足“长期未登录”的特征,但不属于需要清理的对象,反而值得重点保护。
5.3 几个容易翻车的细节点
账号治理实际操作中,有个很容易踩坑的点是对内置账号和默认组的处理。比如域内置的 Administrator、Guest,以及“Domain Admins”“Enterprise Admins”“Schema Admins” 这些默认高权限组,改造时不能照普通账号的流程来。内置账号的很多属性不允许直接禁用,组策略默认也会忽略部分操作,强行修改可能导致域功能异常。稳妥做法是启用审计和监控,持续盯住这些对象的变更行为,而不是直接把它们纳入清理名单。
另一个坑是权限委派记录。有的人清理账号时只查了组成员关系,没查对象 ACL 上的单条委派授权,清理后看似干净,实际访问权限还留着。例如某个离职员工的账号已经从组里移除,但共享文件夹或 OU 上单独给了它“写入”或“完全控制”权限,只要账号还存在,它就依然能访问这些资源。所以处置动作的最后一定要做一次“有效权限”复查,拿账号重新对敏感路径做权限查询,确认所有路径都已切断。
再补一个细节,远程桌面服务的用户配置文件也可能残留。账号删除后,服务器上对应 sID 打头的配置文件文件夹如果不清,会积累大量垃圾文件,而且新账号恰好拿到相同 sID 映射时还可能串到旧配置,引发权限异常。清理时最好顺带检查终端服务器上的 Profile 目录,一并归档或删除,避免后续莫名其妙的“分配配置文件错误”问题。
我做了这么多年安全巡检,最直观的感受是:企业的安全防线从来不是毁在那些高深的攻击手法上,而是毁在日常流程的缝隙里。孤儿账户就是最典型的缝隙之一,它小到平时没人提,大到攻击者可以直接拿到域管权限。每次处理完这类问题,我都会在企业内部强调一句话:权限是有生命周期的,账号跟着人走,人走了,权限必须跟着收。希望这篇文章能帮你把自己身边的“僵尸账号”揪出来,收掉它们,防线才能真正闭合。