作者 | Abdulrahman Alqabandi

翻译 | 核子可乐

策划 | 褚杏娟

没错,我是做“Bug 赏金”这行的,也有人说应该叫“漏洞奖励”计划,但我觉得还是 Bug 赏金听着比较带劲。在今天的文章中,我想跟大家聊聊在找 bug 这件事上,业余和专业的到底有什么区别。这些都是我的真实经历,包括种种遗憾、惊喜和建议,希望能给各位带来一点启示。最后要强调一点,本文完全是我的主观经验,可能跟您的真实经历有所出入。

我跟 bug 赏金工作的缘分始于 2015 年初,当时我刚收到人生中第一笔官方赏金,没想到在这行一干就干到了今天。我发现的第一个 bug 是 Office 365 Outlook 中的 XSS 漏洞;2015 年底,我又在火狐浏览器里发现了自己的第一个浏览器有效 bug。这段经历为我打开了新世界的大门,之后我开始收敛心神,专注于给浏览器“挑毛病”。

过去五年来,我完成了从大学辍学生到助理系统开发者、再到网络安全工程师的职场蜕变,最后拿到了如今这份微软浏览器漏洞研究员的职位。这样的发展简直如梦似幻,连我自己都很难相信。

走过的弯路

在参与 bug 赏金活动过程中,我个人走过的最大弯路其实来自思想意识。

我之前一直觉得给浏览器找 bug 的本事不足以让自己在网络安全领域找到一份像样的工作。必须承认,浏览器 bug 研究属于网安领域内的小众方向,这种小众性也加剧了我的错误判断。我早期接触过的其他 bug 赏金同行大多是从 Web 应用程序安全起步,并一路坚持下去的,所以对自己这种痴迷于浏览器安全研究的路线,我实在是没什么信心。

但事实上,浏览器安全属于一块小而美的利基市场,我完全有资格在主要浏览器开发商那里拿下相应的职位。如今,我已经在微软浏览器团队工作一年有余,日常工作经历早就让我放下了对自己的怀疑情绪。所以在这里,我想回顾一下当初的错误,聊聊怎么为如今这份工作提前做好准备。

当初的我从来没想到自己能进入浏览器安全团队,所以一直没考虑过怎样才能保护浏览器安全。当初我找寻 bug 的主要方法就是多阅读关于浏览器和安全设计的资料,然后手动测试自己的猜测。这种方法虽然极为耗时、效率低下,但挺适合我——整个过程有点像冥想,可以说非常沉浸。如果运气不好,我也能学到关于最新 Web API 的知识;如果运气好一点,那我没准就找到了一个有趣的安全漏洞。这简直就是双赢,唯一的问题就是花费的时间有点多。

像我这种野路子出身的选手,一直都没接触过 fuzzing;更丢人的是,当时我对 fuzzing 方法的认识都有错误——我一直觉得 fuzzing 就像是加密货币圈搞的挖币,fuzzer 这东西只要跑起来就会疯狂吃掉我的 CPU 资源,直到遇上问题并引发崩溃。虽然我也知道要吃浏览器安全这碗饭,fuzzing 这关早晚得过,但原先的舒适区实在太舒适了,容我再躺会儿……

总结:寻找适合自己的利基定位,把这种定位跟个人爱好结合起来。只要迈出兴趣与工作契合的第一步,你已经赢了。

只管找,不管修

作为 bug 赏金猎人,那时候我满脑子都是找 bug。发现漏洞之后,我只需要在提交时稍做说明就直接踏上了又一段的找寻之旅。这种只管找、不管修的风格让我的技能储备出了问题,我根本不理解很多 bug 到底是怎么引发的。

这里要给新入行的朋友一点建议:如果你和当时的我一样,总想抢在别人前面早点提交新 bug,那至少记得去看看修复补丁公布。

那时候的我自以为聪明,“我为什么要自找麻烦?又没人给钱。”这话对,但也不对。过度急功近利反而适得其反。

关注 bug 原理能让我们掌握编码规则,这样我们慢慢就能理解 Chromium 的代码库和它的工作特性,提升自己发现 bug 的能力。只要把握住造成 bug 的根本原因,我们就能以同样的思路发现其他同类实例,成功把一项发现变成多项发现。在拥有原理层面的洞察力之后,我们才能正确判断 bug 的严重程度,免得把高危漏洞误当成小问题。况且,我们不光可以找 bug,更可以修 bug。编写补丁也有钱拿,增加总体收入岂不美哉?

好在那时候我有个好习惯,就是总会关注 bug 报告和相应的 Twitter 讨论。我会认真阅读每一条评论,没准哪一条就能给我重大启发。我也不会回避反对意见,而是结合自己掌握的一手信息判断对方的意见靠不靠谱、有没有参考价值。在网友们眼中不可理喻的某些设计,其实内部员工立足后端的视角上非常合理,所以应该学会从多个角度审视问题。我要是能早点领悟到这一点就好了,真的很遗憾。

加入微软之后,我的工作就不单单是提交安全 bug 了,更要跟开发人员合作进行修复。有些 bug 好修,有些却非常难修。所以在提交 bug 时,我得小心谨慎,保证自己不只提出了有限的可重现案例,更能给出比较可靠的根本原因分析和初步修复建议。当然,我现在仍然参与各类 bug 赏金计划,并在注释里尽可能帮助开发者快速理解当前问题。

我们收到的大部分上报 bug 都不是真正的安全漏洞。很多问题是之前就报过的,有一些属于设计中的正常现象,有一些无法重现,也有部分根本就是胡说八道。但也有不少案例相对复杂,会引发不同群体之间的热烈讨论。这些对话中往往隐藏着有趣的见解,所以我特别喜欢阅读这方面内容。

很神奇,我发现自己在转型成专业人士之后,经常会在讨论中向其他人解释为什么对方发现的问题并不是 bug,而是设计特性——当初的我肯定想象不到这样的场景。也正是这种跨越两种身份的经历,让我更能理解 bug 上报者们的付出与思路。

Edge 浏览器正茁壮生长,bug 总会不可避免地出现。随着新功能 / 变更 / 改进的注入,代码量膨胀必然带来更多 bug。结合个人经验,我觉得 bug 的产生主要源自以下几个方面(这里主要谈开发与安全团队方面的问题):

  • 经验不足。新人们需要长时间学习才能理解 Edge 庞大代码库的来龙去脉。在学习过程中,大家肯定会犯错。

  • 没做出最坏的假设。在安全保障方面,偏执的性格往往能够发挥奇效,因为这类开发者总会做出最坏的假设,并向安全团队表达相应的担忧。无论大家有没有考虑到,最坏的情况真的有可能出现。

  • 缺乏安全教育。我在读计算机科学本科专业时,学校根本没有任何安全课程。虽然偶尔也会提一点编码最佳实践,但不是教学重点,内容也远远不够。所以在安全技能方面,我们基本上只能靠自己摸索。

安全教育可以说是 bug 赏金工作中的一项重点。在发现 bug 之后,我们首先得跟相关人员讨论漏洞情况、发生原因,以及如何通过自动测试防止今后再次出现。在微软,我们会接受强制性的安全培训、学习安全最佳实践并通过邮件掌握每周安全提示。总之,安全是每个人都必须承担的重要职责。

另外,我很早就发现找 bug 要比修 bug 简单得多。所以要想保护浏览器安全,首先得培养开发者的安全意识。如果没有开发环节的配合,最终安全根本无从谈起。安全是所有人的事,绝不只是安全团队的事。

我日常工作的另一部分,就是对计划引入 Edge 浏览器的新功能开展安全审查。乍听起来,这好像跟我之前的 bug 赏金经历没什么区别,但实际上完全不同。虽然两者目标相似,都是从功能中找 bug,但现在我需要查看 C++ 代码……当初我可不干这事。所以,我得先学会 C++,然后熟悉 Chromium-Edge 代码库。感谢同事们的帮助,我终于补上了这重要的一课。

总结:现在的积累和准备,都是为了未来能走得更稳、更快。所以要深挖代码,一定要深挖代码。

克服自我怀疑

自从干这行以来,我就总在跟自我怀疑做斗争。

记得当初第一次发现 bug 时,我的内心经历了非常复杂的斗争:“你是谁呀,就敢给那么大的公司找 bug?”但好在我尝试了,也成功了。在加入微软之后,我甚至很长一段时间都不敢相信这是真的。但确实是真的,而且我还挺称职~

现在有种说法,将这种怀疑情绪叫“冒名顶替综合症”,就是总觉得自己德不配位、没资格坐在目前的岗位上。其实没必要,我们应该压制住这种自我怀疑,并勇于尝试自以为做不到的事情,并用一个又一个意料之外的成果激励自己。

我在微软也有类似的感受,我身边的同事无论是知识储备还是业务水平都远高于我。但我还是留下来了,他们的优秀成了我提升自己的动力,我终要像他们一样优秀。

总结:大胆去做。无论看起来多么高大上的系统,我们都可以成为其中的一员。

回馈整个社群

大多数 bug 赏金猎人都是自学成才,我甚至怀疑可能这行里所有的人都没经受过系统训练。所以,大家只能从线上免费资源里总结经验。我们学习中用到的文章、博客、文献、幻灯片、演讲和视频等,都是作者们的心血与汗水。所以我们也该做出回馈,让这个开放社区更庞大、更健康、吸纳更多新鲜血液。

这绝不是什么亏本的买卖,给社群以回馈其实有很多好处。

首先,如果你是一位非英语母语的朋友,那回馈过程能提升你的写作技巧,特别是有效沟通安全问题的能力。这种沟通能力很重要,甚至可能成为支撑职业规划的关键。围绕特定主题开展写作,往往能让我们发现自己当初错过了的重要内容。

其次,这有助于提升你的互联网安全水平。我们的贡献可能在不经意间启发了某些人,他们发现的 bug 可能消除了某些隐患。

最后,这也可以为你建立声誉和认可度。你的贡献可能会让招聘方或合作方记住你,增强自身影响力永远没错。当然,很多朋友可能更希望在社交媒体上得到别人的肯定,而忽略了讨论本身的意义。我曾经也是这样,随时都要看自己“妙语”下的点赞数量又增加了多少。其实这没有意义,别用点赞数来衡量自己的价值。

我还有另一个先入为主的错误观念,就是像微软这样的大公司不会关心人们在网上写了什么。所以我之前对某些企业表达失望时,倾诉的对象主要是同行,毕竟那些“身居高位”的家伙才不会在乎。

但事实上,他们真的很在乎,而且会持续关注有价值的博客 / 推文。我亲眼目睹过他们如何认真对待反馈,并设定了相应的开发计划。所以别灰心,只要你的表达有价值,声音最终都会传到企业那边。

总结:参与进去,要说也要听,我们就是网络安全社区中的一分子。

调整好节奏

当初自己当“独行侠”的时候,花的都是个人时间。我想做就做,累了就休息,没什么压力。但拿着薪水正常上班可不是这样,所以我得在新岗位上找到新的平衡。

我是在新冠疫情期间加入微软的,所以跟很多朋友一样,我刚入职就开始居家办公。作为新员工,我得保证自己学到需要掌握的所有技能,而且尽最大努力适应这份新工作。居家办公确实是种新鲜体验,毕竟人在离开办公室后、大脑会暗示自己工作模式已经关闭。

所以混乱的居家办公状态让我不知不觉就工作得太久,甚至周末都在忙活,失去了正确的办公节奏。虽然公司内部一直在提醒大家关注工作和生活的平衡,但刚进大厂的我实在是兴奋,根本没听进去。

于是,问题很快就来了。我精神疲惫、睡眠不足、压力巨大。于是,我立刻采取行动,包括周末不再工作、给自己制定明确的工作日下班时间,并留出时间排解压力。我确保每天至少睡 7~8 个小时,还开始刻意少喝咖啡。这里最重要的就是休假,我第一次休假时强迫自己彻底忘掉工作,回来之后身心状态确实大为改观。倦怠感消退,我感觉自己又准备好迎接新的挑战了。

总结:调整好节奏,避免倦怠感。工作是马拉松,而不是百米短跑。

如何投身于浏览器安全领域?

这也是我被问到最多的问题之一。我当初选择这个方向只是因为我觉得浏览器 bug 很酷,找起来很带劲。加入这个领域没什么技巧可言,只能是多看相关资料、了解一切自己感兴趣的内容,总之知识储备最重要。

下面我分享一些自己当初入门学习时的链接:

  1. Chromium 代码库——包含几百万行代码,大家可以随时查看跟所关注功能相关的代码段。

  2. Chromium bug tracker——大家提交的安全 bug 都在这里,多听多看有助于提升自身水平。

  3. Chromium 常见问题解答——这里有很多安全误报,同样值得学习。

  4. Chromium 严重性指南——通过一个个示例,帮助我们理解如何确定安全 bug 的严重性。

  5. Mozilla bug tracker——影响火狐浏览器的 bug 都上报在这里。

  6. Mozilla 安全公告——获取最新的火狐安全 bug 补丁,甚至包括某些未公开 bug 的补丁。

浏览器 bug 主要可以分为两类:

  1. 内存相关 bug——包括内存释放后使用啦、缓冲区溢出之类,这类 bug 占 Chromium 全部安全 bug 中的七成(一般属于高严重性 bug)。

    a.前往‘about:crashes’就能看到已注册的崩溃列表。大家可以在 C:\Users\test\AppData\Local\Google\Chrome\User Data\Crashpad\reports 处找到转储的故障信息 (以 Chrome 为例)。获取 windbg 并打开故障转储,之后执行 !analyze -v

    b.大家也可以在这里查阅 Chromium ASAN builds,了解在正常构建时可能不会被触发的 bug 并实现更全面的堆栈跟踪。

    c.内存 bug 一般用 fuzzing 测试就能发现。

  2. 逻辑 bug——通用 XSS、SOP 绕过、UI 欺骗等都属于跟内存无关的逻辑 bug:

    d.常见的查找方法就是手动寻找并 / 或阅读源代码。

我好像天然就对逻辑 bug 更有兴趣。因为在接触安全领域之前,我主要研究 Web 技术,所以已经拥有一定的 HTML/JS/CSS/HTTP 知识背景。所以如果大家也对 Web 有所了解,那么不妨先从逻辑 bug 入手。另一方面,如果各位在逆向工程或自动化方面比较有经验,那用 fuzzing 测试查找内存损坏问题可能更好。当然,这两条线也可以共同推进。

只要掌握了足够的知识,脑袋里的各种猜测就会自己跳出来。接下里就是去验证这些猜测,虽然大部分会失败,但请千万不要放弃。最终,你会惊喜地发现安全 bug 就这么被找到了。

总结:学习、学习、再学习。有了想法就去试,失败了就再来一次。

全面自动化

Fuzzing 测试在软件应用的保护中至关重要,而自动化也是节约时间、增加结果产出的利器。

可能有些朋友还不太熟悉 fuzzing,也就是模糊测试:它是将伪随机生成的输入提供给进程(例如浏览器),之后检测进程是否崩溃的测试方法。微软在 fuzzer 方面投入了大量资金,这也是我加入以来需要补上的重要一课。

事实证明,我对 fuzzer 先入为主的观念完全是错误的。Fuzzing 本身博大精深,涉及多种不同的方法与学科。截至目前,我对 fuzzer 的体验虽然主要立足浏览器场景,但已经能体会到它的普适性潜力。这里我只列出自己新手用过的部分工具 /fuzzer/ 生成器:

  • Libfuzzer——这是一个进程内用于覆盖的 fuzzer。简单来讲,我们只需要设置一个函数,它就能轻松向该函数提供各种输入并返回覆盖信息(这里的覆盖,代表在特定输入期间接触过多少代码)。Libfuzzer 的使用要求我们访问浏览器源代码,并掌握一些关于 C++ 的知识。

  • Dharma——这是一款基于语法的 fuzzer,由我们的团队成员 Christoph Diehl 开发完成。在使用时,我们需要编写一个语法文件,向 Dharma 描述某个 JavaScript API 或者 HTML 元素的样子,之后 Dharma 就会生成一大堆可以在浏览器内运行的测试用例。虽然不涉及浏览器源代码,但 Dharma 的使用要求我们掌握 JS 和 HTML 知识,并对相关语法有一定了解。

  • Playwright/Puppeteer——通过 NodeJS API 控制浏览器。使用这款强大的工具,我们可以对浏览器进行各种控制,例如使用浏览器执行某些重复性任务。使用过程不涉及浏览器源代码,但要求我们稍稍掌握一点 NodeJS 知识。

  • Octo——Fuzzing 库。在 fuzzing 期间,我们有时需要生成随机字符串、随机数或者一些随机内容。这个库能帮助我们轻松获得这些随机内容,适用于 NodeJS 项目和带有捆绑包的浏览器版本。不涉及浏览器源代码,但要求我们最好掌握一点 NodeJS/JS 知识。

总结:早点进军自动化领域、扩大视野,别在舒适区躺着不动。

上报浏览器安全 bug

安全 bug 永远存在,我真心觉得会永远存在。而且就在当下、就在我们的屏幕背后,就潜藏着一个个 bug,快把它们找出来~

假如大家在 Edge 浏览器中发现了一个 bug,那么在上报之前请注意以下几点:

1. 它能在 Chrome 上重现吗?

  • 是——把 bug 上报给 Chromium,而非 Edge。

  • 否——上报给 Edge。

2. 它能在火狐上重现吗?

  • 是——将 bug 分别上报给 Edge 和 Mozilla。

  • 否——只上报给 Edge。

3. 你使用的是 Edge 的最新稳定版吗?

  • 前往‘edge://settings/help’并查看是否为最新。

  • 在报告中列出确切版本。

4. 说明你使用的操作系统(Windows、Mac、Linux、Android 或者 iPhone)。

5. 在报告上传一个最小化测试用例(请勿发送现场演示链接)。

6. 上传一段关于 bug 现象的短视频(我个人喜欢用 OBS)。

7. 找找其他浏览器中有没有对应的先例。

总结:Bug 就在那里,同志们冲啊!

写在最后

希望我的个人经历能给大家带来一点启示,特别是从我的错误中吸取教训。文中提出的当然只是些非常浅表的技巧,更有份量的知识还需要各位亲自挖掘。与其他学科一样,与 bug 的斗争同样艰难且需要深耕不辍。这就是我的 bug 赏金故事。

原文链接:

https://microsoftedge.github.io/edgevr/posts/bug-bounty-hunter-to-working-at-microsoft/