Apache Software Foundation 在 2010 年 4 月遭遇基础设施入侵。事后报告无法判定,攻击者究竟凭借针对 JIRA 的跨站脚本攻击,还是并行发起的密码猜测取得管理员权限。决定性的一步发生在此后:一次看似寻常的密码重置。[1]
此时,攻击者已经掌握 JIRA 管理员权限,把附件配置变成服务器端代码入口,并修改登录流程以记录密码。他们利用 JIRA 本身向基础设施管理员发送重置消息。一名管理员随后进入系统,将临时密码改回惯用密码,预先植入的记录器由此截获了这个密码;同一密码也能登录一个本地 shell 账号,而该账号拥有完整的 sudo 权限。Web 应用失陷后,攻击者没再利用其他提权漏洞,便在 brutus.apache.org 上取得了 root 权限。[1]
正因这条攻击链,事件值得逐层复盘。单个设计精巧的漏洞不足以概括这次事件,其结果也不足以证明每套 Apache 系统都已失守。五道边界相继失效,另一些边界仍然守住。问题跟踪主机已经失去可信度,三个应用数据库都须按已遭复制处置,缓存的 Subversion 凭据还把攻击者带上了第二台机器。然而,攻击者未能在那里提权,LDAP 基本未受影响,Apache 的源代码仓库也没有失陷。[1][4]
上方照片摄于攻击发生前五个月。[6] 照片里整齐排列的团队衬衫,也让安全模型中的“人”显出具体形状:这个志愿者基金会的日常运转,依赖成员穿梭于会议、浏览器、工单队列、shell 账号与共享服务之间。若把 2010 年的教训收束成“不要点击链接”,便会漏掉真正的问题。这套由人参与的系统里,每项日常操作——查看工单、上传附件、重置密码、使用缓存登录——都应停在自身的权限层,不得悄然继承下一层的权限。
一张工单变成了服务器端代码
4 月 5 日,一名攻击者使用一台已遭入侵的 Slicehost 服务器,在 Apache 的 JIRA 中创建了 INFRA-2591。工单正文里有一个伪装成错误报告的短网址。该网址会把访问者重新引向同一个 JIRA 实例,并携带一段跨站脚本载荷,用于窃取打开链接者的会话 cookie。基础设施团队有数名成员点开了链接,多个会话随之暴露,其中包括带有 JIRA 管理员权限的会话。与此同时,攻击者还对 login.jsp 发起了数十万次密码猜测。[1]
到 4 月 6 日,其中一条路线已经奏效;Apache 无法查明管理员权限究竟来自哪一种手段。攻击者利用 JIRA 管理权限,关闭了一个项目的通知,并修改附件上传目录。他们选中的位置既允许 JIRA 进程写入,又已配置为能够运行 JSP 文件。随后,新建工单成了上传渠道:一个 JSP 用于浏览和复制文件,其他 JSP 则以运行 JIRA 的账号维持后门访问。[1]
XSS 漏洞只是失守的一环。遭窃的应用管理员权限之所以演变为代码执行,是因为一项设置能把不受信任的附件指向允许代码运行的路径。JIRA 守护进程又以安装应用的角色账号运行,进程因而拥有异常宽泛的访问权。浏览器会话、应用配置、可写存储、能被服务器运行的文件与文件系统身份,由此连成一条不间断的权限通道。
Atlassian 的响应表明,仅写下“XSS 已修补”还不能为事件处置画上句号。其 4 月 16 日的公告描述了能够触及文件系统的 XSS 与提权漏洞,最终补丁取代了 Apache 初始报告提到的两个较早修复。公告覆盖 JIRA 3.13.4 至 4.1,并要求客户即使已经安装首批补丁,也要应用替代补丁。[2] 这项事实只描述当时的情况,不能外推到当前 JIRA。它也留下一条长期适用的事件响应规则:紧急修复总有具体版本与覆盖范围;某个补丁已经部署,不能据此推定响应期间发现的所有路线均已关闭。
登录页面成了凭据桥梁
到了 4 月 9 日早晨,攻击者已经安装一个 JAR,用来记录用户登录时输入的密码。随后,他们触发了 Apache 基础设施团队成员的密码重置。消息来自收件人平日管理的服务,把临时密码改回惯用密码,看起来只是从一次应用故障中恢复。[1]
其中一名管理员恢复使用的密码,也用于 brutus 上的本地账号,而该账号拥有完整的 sudo 权限。当时,SSH 仍然接受密码登录。Apache 团队曾试图关闭这项功能,但 UsePAM yes 通过 PAM 留下了一条认证路线。主机上还配置了 OPIE 一次性密码,强制使用范围尚未覆盖每一名超级用户。攻击者拿着在 JIRA 内截获的密码登录操作系统,继而取得 root 权限。[1]
复盘若只停在表面,很容易选错分析尺度。“密码复用”属实,只解释了攻击链的一段。复用之所以造成灾难性后果,是四项条件同时成立:失陷应用能够记录明文密码;同一密码既用于应用账号,也用于主机账号;主机仍接受这种密码远程登录;主机账号又能直接取得 root 权限。去掉其中任一环节,结果都会改变。
仅在最初的 JIRA 会话上增加多因素认证,仍不足以阻断所有路线。攻击者改变登录行为时,已经身处应用内部。现代控制措施需要同时保护管理员会话、分离应用与主机身份、阻止应用成为窃取凭据的渠道,并在操作系统边界要求经过独立认证的提权步骤。攻击会跨层移动,控制措施也须交叠覆盖各层。
root 止步于一台主机,秘密仍向外扩散
攻击者在 brutus 上取得 root 权限,意味着 Apache 已经无法信任这台机器。JIRA、Confluence 和 Bugzilla 都运行在这台机器上,因此响应团队假定三个数据库全都遭到复制。JIRA 与 Confluence 存储的是未加盐的 SHA-512 密码哈希,Bugzilla 使用加盐的 SHA-256 哈希。无论属于哪一种情况,Apache 都建议用户废止相关密码;凡是在 4 月 6 日至 4 月 9 日期间登录 JIRA 的用户,其密码一律视为已被篡改后的登录表单截获。[1]
攻击者还在用户主目录中发现了缓存的 Subversion 认证凭据。这些密码让他们得以登录 minotaur.apache.org,也就是 people.apache.org,Apache 的主要 shell 服务器。横向移动已经发生,权限范围却止于普通账号:受影响账号未能在 minotaur 上提权。其他主机和服务基本未受影响,LDAP 也在其中。Apache 的基础设施副总裁另向 Computerworld 表示,源代码仓库没有失陷,源代码均未受影响。[1][4]
服务隔离发挥了作用,效果只覆盖一部分。brutus 上的同机数据库随着 root 权限失守而暴露,缓存凭据也让主机之间仍有通路;与此同时,隔离阻止了这次 root 失陷自动扩张成整个基金会的管理权限。与其把隔离判作“成功”或“失败”的二元结果,更值得追问的是:一个节点失守后,哪些身份仍被接受,哪些秘密仍可使用,哪些数据存储仍然可信,又有哪些提权路线依旧畅通。
Apache 在 2009 年 8 月就收到过一次相关警告。一台遭入侵的 ApacheCon 主机暴露了某个备份账号的 SSH 密钥,攻击者随后用它进入 minotaur 上的一个非特权账号。他们在网站文档根目录植入 CGI 脚本,自动运行的 rsync 又把这些文件带到生产 Web 服务器。此前的报告指出,Subversion 数据存放在别处,源代码从未面临风险。[3] 两次事件的手法有别,也指向同一项系统性危险:凭据和自动化能够让权限跨越主机边界,即便最初的账号只是普通账号。
恢复意味着重建信任,清理文件远远不够
密码重置开始约六小时后,Apache 发现了入侵活动,并开始关闭服务。攻击者在 brutus 上持有 root 权限达数小时,单靠搜索已知恶意文件,已经无法重新建立对操作系统的信任。团队把服务迁移到另一台机器 thor.apache.org。JIRA 与 Bugzilla 在 4 月 10 日恢复运行;事件报告于 4 月 13 日发布,当时 Confluence 仍处于离线状态。[1]
补救清单逐一对应此前失效的连接。JIRA 由 root 重新安装,运行时则改用一个独立且权限受限的守护进程身份。在修复方案逐步完善期间,反向代理以临时排除规则封锁了高风险的 picker 路径。超级用户开始强制使用一次性密码。Apache 在 Subversion 的 [auth] 配置中,通过 store-passwords = no 全局关闭 SVN 密码缓存,并删除已有缓存。Fail2Ban 的覆盖范围也扩展到 JIRA 登录失败事件。[1]
每项措施各有明确任务。分离安装身份与运行身份,可以收窄文件系统权限。在另一台主机重建服务,是为应对原主机已不再可信。一次性凭据和 SSH 调整,减少了可以反复用于主机登录的凭据。关闭 SVN 密码存储,移除了横向移动所用的缓存。速率限制针对同期的密码猜测活动。任何一项单独拿出来,都无法概括为“全部修复”。
公开报告本身也是恢复工作的一部分。它列出了主机角色、日期、密码哈希属性、提权成功与受阻的位置、配置错误,以及仍在推进的改动。Bruce Schneier 当时称赞它是透明事件报告的范例。[5] 这份称赞来自报告的实际用途:其他运维人员可以把其中事实对应到具体控制措施。若只剩下公开承认入侵,披露便只是一场表演。
把事件读成一张权限图
对维护者而言,最清楚的现代演练方式,是隐去 2010 年的产品名称,重新画出这条攻击路线:
untrusted ticket → privileged browser session → application administration → executable storage → runtime identity → captured login secret → host elevation → cached cross-host credential
接着,要拿出证据,证明每个箭头都已被切断。工单内容能否在管理员浏览器的同源环境中运行?应用管理员能否选择允许代码运行的上传目录?服务运行身份是否拥有应用安装目录?恢复流程能否收集可重复使用的主机凭据?远程登录是否接受这类凭据?一个账号能否在缺少独立认证因素的情况下提权?主机取得 root 权限后,还有哪些凭据处于可读状态,又有哪些其他系统会接受它们?
这张图也保留了事件的影响范围。Apache 没有报告源代码遭到修改,同时期的独立报道也确认仓库处于失陷范围之外。[4] 攻击者抵达两台机器,不能据此断言隔离已经失败;发布版本未遭篡改,也不足以证明隔离完全成功。问题主机失守、用户凭据暴露、横向移动发生,另一些权限仍守在原有范围内——这些情况同时存在。
这些攻击者之所以能借一次密码重置取得 root 权限,是因为重置流程跨越了多个层级,而这些层原本按便利性来设计,安全边界并未被明确建立。这次复盘留下的长期教训,是明确标出这些层级。修补第一个漏洞会关闭一处入口;切断权限链,才会决定下一次入侵能够走多远。
来源
- Apache Software Foundation Infrastructure Team,“apache.org incident report for 04/09/2010”,2010 年 4 月 13 日——事件主时间线、受影响主机与凭据、遏制边界及补救措施。
- Atlassian,JRASERVER-21004,“XSS and Privilege Escalation Vulnerabilities in JIRA”,2010 年 4 月——受影响版本,以及取代初始修复的替代补丁。
- Apache Software Foundation Infrastructure Team,“apache.org incident report for 8/28/2009”——较早发生的 ApacheCon 主机入侵、备份 SSH 密钥、
minotaur访问、rsync传播及源代码仓库边界。 - Robert McMillan,“Apache project server hacked, passwords compromised”,Computerworld,2010 年 4 月 14 日——关于第二台主机访问与未受影响的源代码仓库的同期独立报道。
- Bruce Schneier,“Attack Against Apache.org”,2010 年 4 月 27 日——对事件报告透明度与教学价值的同期评价。
- Noirin Shirley,“DSC_0248”,Flickr,摄于 2009 年 11 月 5 日——本文所用 Apache 基础设施团队照片的来源页面。