Kali Linux——网络安全测试基础

357 阅读45分钟

安全测试是一个广泛的术语,涵盖了许多不同的内容。通常,渗透测试是通过网络远程进行的。然而,并非所有的安全测试都是渗透测试。有时,开发团队可能希望对应用程序进行测试,包括Web应用程序。这些Web应用程序可能包含多个网络服务。有时,你可能不仅仅在测试网络应用程序,还会测试设备。无论是应用程序还是设备,都可能需要进行压力测试,以确保它们能够处理不同类型的流量,甚至是大量的流量。

如果你想进行任何形式的基于网络的安全测试,了解网络协议栈的定义是至关重要的。定义协议的一种方式,特别是它们的交互方式,是使用开放系统互联(OSI)模型。通过使用OSI模型,我们可以将通信分解为不同的功能元素,清楚地看到在网络包创建过程中,哪些信息被添加到网络包中。此外,你还可以看到从系统到系统在功能元素之间的交互。

压力测试不仅仅是生成大量流量并发送到应用程序或设备。在某些情况下,你可能通过发送不符合预期的数据来给应用程序或设备施加压力。即使是运行在有限用途设备上的应用程序(比如互联网物联网设备,如恒温器、锁、开关灯)也会对接收的数据类型和结构有所预期。发送与预期不符的数据可能导致应用程序失败。这是需要了解的内容。这也是一种压力测试,因为你在给应用程序的逻辑施加压力。

这也是理解通信协议构建方式为何重要的原因之一。进行网络安全测试需要了解通信模型的各层如何协同工作。一旦你掌握了这些内容,你就可以开始思考如何进行安全测试。当然,了解什么是安全测试也很有帮助,我们可以从这里开始,然后再深入了解通信协议栈的工作原理。

安全测试

当许多人听到“安全测试”这个术语时,他们可能会想到渗透测试,渗透测试的目标是进入系统并获得尽可能高的权限。然而,安全测试并不完全是为了“突破防线”。事实上,你可以认为,大多数安全测试并不是渗透测试。保护系统和软件的领域如此广泛,许多部分与传统意义上的渗透测试无关。在我们开始讨论如何使用Kali Linux进行网络安全测试之前,我们应该先了解安全的定义,这样你能更好地理解在这个背景下测试的意义。

当专业人士,尤其是认证机构,谈论安全时,他们通常会提到一个叫做“三位一体”的概念。有些人可能会增加其他元素,但信息安全的核心包括三个基本原则:保密性、完整性和可用性。任何可能影响系统或软件的这三个方面中的任何一个,都可能影响该软件或系统的安全性。安全测试应该或必须考虑到这些方面,而不是渗透测试可能提供的有限视角。

如你所知,这个三位一体通常表现为一个等边三角形。三角形是等边的,因为这三个元素被认为具有相等的重要性。此外,如果任何一个元素丧失,你就不再拥有一个三角形了。在图2-1中,你可以看到常见的表现形式,三角形的三边长度相等。每一个元素都被视为信息被认为可靠和可信的关键因素。如今,由于企业和个人如此依赖数字存储的信息,确保信息的可用性、必要时的保密性和完整性是至关重要的。

image.png

大多数企业依赖于秘密运营。人们也有自己的秘密:如社会安全号码、密码、税务信息、医疗信息以及各种其他数据。企业需要保护其知识产权。它们可能拥有许多商业机密,如果这些信息泄露出去,可能会对企业产生负面影响。无论这些信息是什么,保持其机密性就是保密性。任何时候,当没有授权的人能够访问某个信息时,保密性就被破坏了。这是许多数据盗窃事件中主要受到影响的因素,从Target到美国人事管理办公室,再到Equifax和Sony。当消费者信息被盗时,这些信息的保密性就被泄露了。现代的勒索软件攻击也会影响保密性。攻击者会偷取数据,并威胁将其公开发布。

一般来说,我们期望存储的数据在检索时是一样的。数据的损坏或篡改可能由多种因素引起,这些因素不一定是恶意的。仅仅因为我们谈论的是安全, 并不总意味着我们在讨论恶意行为。当然,之前提到的案例是恶意的。然而,坏掉的或故障的内存可能导致磁盘上的数据损坏。我说这话是基于个人经验。同样,故障的硬盘或其他存储介质也可能导致数据损坏。当然,在某些情况下,恶意和故意的行为会导致数据损坏或不正确。当信息被篡改时,无论原因是什么,这就是完整性的失败或泄露。完整性完全是指某物处于你合理期望的状态。再考虑一下勒索软件。当数据被攻击者加密时,它失去了完整性,因为它不再是用户上次访问时的状态。

最后,让我们考虑可用性。如果我把你的电脑插头从墙上拔掉,它很可能会掉到地板上,甚至可能砸到我的头,那么你的电脑就变得不可用了(当然,我们讨论的是台式机系统,而不是有电池的设备)。类似地,如果你的网络电缆的夹子掉了,导致连接器无法稳固插入墙面插座或网络接口卡,你的系统就无法在网络上使用。这可能会影响你,当然,也会影响你完成工作的能力,但如果其他人需要你电脑上的数据,也会影响到他们。任何时候,如果发生服务器故障,那就是对可用性的影响。如果攻击者能够使服务或整个操作系统失败,即使是暂时的,那也是对可用性的影响,这可能对业务产生严重后果。这可能意味着消费者无法访问广告中的服务。也可能意味着大量的人力和其他资源投入到维持服务运行和可用性的工作中,就像银行遭遇的大规模、持续且长期的拒绝服务攻击一样。虽然这次拒绝服务的攻击未能成功,但在抵抗过程中对业务造成了影响。再说一次,勒索软件的威胁是,如果你没有解密密钥来解锁加密数据,那就是一个可用性问题。如果数据无法读取,它就不可用,至少在可用的形式下是不可用的。

这引出了CIA三位一体的局限性问题。Donn Parker提出了信息安全的三个额外属性:控制性、真实性和效用性。控制性指的是拥有权。如果我拥有某个资源,那我就掌控它。真实性指的是验证。某个物品是否是它应有的样子?这包括材料的来源,包括电子邮件。数字签名是验证真实性的一种方式。最后,效用性指的是某物是否有用。这可能是一个更好的术语,用来描述勒索软件威胁行为者加密的数据。技术上讲,这些文件是可用的;只是它们目前的形式并不太有用。

测试任何与这些元素相关的内容都属于安全测试,无论测试采取何种形式。当涉及到网络安全测试时,我们可能会测试服务的脆弱性、加密强度和其他因素。在我们谈论网络测试时,我们将首先查看一套压力测试工具。我们还会查看其他一些已知可能导致网络故障的工具。虽然许多操作系统网络协议栈中的漏洞可能早已修复,但你有时可能会遇到更轻量、脆弱的设备,这些设备可能连接到网络。这些设备可能更容易受到此类攻击。这些设备包括打印机、VoIP电话、恒温器、冰箱以及越来越多连接到网络的设备。

网络安全测试

我们生于网络,亦死于网络。你个人的多少信息目前是直接存储在设备上,或者至少通过互联网可以访问,无论这些信息存储在你本地网络的设备上、你雇主的企业网络中,还是存储在互联网上,通常被称为云端?当我们生活在期望一切通过网络可以访问的环境中时,确保我们的设备能够承受攻击就显得至关重要。

监控

在进行任何测试之前,我们需要讨论监控的重要性。如果你在为公司或客户进行测试,理想情况下,除非被要求,否则你不应故意关闭任何服务或系统。然而,无论你多么小心,总是有可能发生一些意外,导致服务或系统出现故障。因此,与拥有系统的人沟通至关重要,这样他们就可以监控系统和服务的状态。企业不希望影响到客户,因此通常希望员工随时准备在必要时重启服务或系统。

注意
一些公司可能会希望测试他们的运营团队,这意味着他们期望你尽可能地渗透并使系统和服务崩溃,而不会造成长期或永久性损害。这通常被称为红队演练(Red Teaming)。在这种情况下,你不会与其他人沟通,除非是雇佣你的管理层。然而,在大多数情况下,公司会希望保持其生产环境的正常运行。如果运营团队或其管理层参与其中,目的是测试他们是否能检测到入侵行为,这种测试被称为紫队演练(Purple Teaming)。运营团队是蓝队,攻击团队是红队。将两者结合,就是紫队。

如果运营团队参与其中,他们通常会希望有一些监控措施。这可能包括查看日志,这是一般推荐的做法。然而,日志并不总是可靠的。毕竟,如果你能够使服务崩溃,服务可能没有足够的时间将任何有用的信息写入日志就已经失败了。不过,这并不意味着你应该忽略日志。请记住,安全测试的目的是帮助提升你所服务公司或客户的安全性。日志可能对于获取有关进程失败前的线索至关重要。服务未必会完全失败,可能只是没有按预期方式运行。在这种情况下,日志非常重要,可以帮助你了解应用程序在做什么。

可能会有看门狗(watchdog)程序在运行。看门狗有时用于确保进程持续运行。如果进程失败,PID将不再出现在进程表中,系统会知道重启该进程。即使你不希望进程被重启,监控进程表,查看进程是否失败,也是一个指示,表明某些问题可能已经发生。

在老旧的init系统初始化中,你可以通过编辑/etc/inittab文件指定崩溃后应重启的进程。在现代的systemd系统初始化软件中,你可以配置服务在崩溃时自动重启。这个设置可以在配置文件中通过Restart=来指定。你可以将此参数设置为always,或许设置为on-failure。不过,并非所有应用程序都是服务。你可以通过创建systemd配置文件,将任何应用程序转化为服务,并通过systemctl命令启动或停止服务。你可能不希望通过这种方式操作,不过你可以使用类似Python脚本的方法,当进程崩溃时自动重启它。示例2-1展示了一个Python脚本,在进程失败时生成消息,并重新启动它。你只需在命令行提供可执行文件名称。

示例2-1:进程失败时自动重启的Python代码

import sys
from datetime import datetime
import subprocess

cmd = sys.argv[1]
retcode = 1
while retcode != 0:
    prog = subprocess.run(cmd)
    retcode = prog.returncode
    if retcode != 0:
       print("Program failed at ", datetime.now())

失控的进程可能会开始消耗大量的处理器资源。因此,查看处理器和内存的使用情况至关重要。你可以使用开源的监控工具,也可以使用商业软件,或者在Windows或macOS系统中使用操作系统自带的监控工具。一个流行的监控程序是Nagios Core。在我的一台系统上安装了Nagios Core。Nagios有商业版,而Nagios Core依然是免费的,并且在许多分发版仓库中可以找到,包括Ubuntu和Kali。图2-2展示了Nagios Core的“服务”页面,显示了它所运行主机上服务的状态。在没有任何额外配置的情况下,Nagios会监控进程数量、处理器使用率以及SSH和HTTP服务器的服务状态。

image.png

如果你没有得到运营团队的配合,或由于某些原因无法直接访问正在测试的系统,你可能需要能够至少远程跟踪服务的状态。当你使用我们将在这里讨论的某些网络测试工具时,它们可能会停止接收到正在测试的服务的响应。这可能是因为服务失败,也可能是监控出现问题,或者可能是为了防止网络滥用而部署的安全机制。手动验证服务是否已停止是非常重要的。

提示
当你在进行测试时,发现服务失败了,确保尽可能详细地记录下失败发生的具体位置。仅仅告诉客户或雇主服务失败并没有太大帮助,因为他们可能不知道如何修复它。保持详细的记录将在报告时帮助你,如果他们需要重新创建这个问题以解决它,你可以准确告诉他们当服务失败时你在做什么。具体的时间点可能帮助他们在日志中找到相关信息,从而帮助定位根本问题。

手动测试可以使用像netcat这样的工具,甚至是telnet客户端。当你使用这些工具连接到服务端口时,你将能够知道该服务是否有响应。当然,这需要你测试的是网络服务,而不是本地应用程序。进行这种手动验证,特别是如果从不同的系统进行,以排除被阻止的可能性,可以帮助排除误报。最终,许多安全测试都归结为排除由于使用不同工具而产生的误报。监控和验证至关重要,确保你向雇主或客户呈现的信息是有效且可操作的。记住,你的目标是帮助他们提升安全防护,而不仅仅是指出哪里出了问题。

层次结构

正如《怪物史莱克》中的驴子所说,层次结构很重要。实际上,史莱克说的是“怪物有层次”,而驴子则说“蛋糕有层次”,不过史莱克把怪物比作洋葱,而蛋糕比洋葱更好。而且,我仍然能听到埃迪·墨菲作为驴子说“蛋糕有层次”。当然,这些都不是重点,除了蛋糕。蛋糕可能是重点——因为当我们谈论网络和系统之间的通信时,我们通常会谈论层次。如果你想象一个有七层薄薄蛋糕的蛋糕,你或许能想象我们如何看待网络。更重要的是,为了想象最佳的过程,你需要想象两片蛋糕。两片蛋糕总比一片蛋糕好,对吧?

图2-3展示了OSI模型七层的简单表示,以及每一层如何与远程系统上的相同层进行通信。你可以想象每层之间的分界线就像是糖霜,或许还有果酱,这样能让它更有趣。果酱帮助层与层之间粘合,因为它是粘性的。你与每个系统通信时的每一层都是完全相同的,因此,当你从一片蛋糕发送信息到另一片蛋糕时,它是从发送蛋糕的匹配层传递到接收蛋糕的对应层。

image.png

让我们这样思考。最底层的第一层是物理层,所以我们可以把它看作开心果层。我们的开心果(物理)层就是我们连接到网络的地方,或者在这个案例中,它是蛋糕放置的盘子。就像蛋糕一样,物理层和网络之间没有任何东西。你把网络接口插上电缆,然后将另一端连接到插口。这就是物理层的全部内容。在我们的蛋糕中,开心果层直接放在盘子上,中间没有任何东西。

接下来的层次是“牛奶糖层”(dulce de leche,类似焦糖牛奶糖)。这一层是我们的数据层。这个层使用介质访问控制(MAC)地址进行寻址。该地址包括3个字节,属于供应商(有时称为组织唯一标识符,或OUI)。另外3个字节是你网络接口的唯一标识符。这两个部分加在一起就是6字节的MAC地址。任何在你本地网络上的通信必须发生在这一层。如果我想从我的牛奶糖层(dulce de leche)和你的牛奶糖层(dulce de leche)进行通信(因为谁除了牛奶糖层会理解牛奶糖层呢),我需要使用MAC地址,因为它是你和我网络接口都能理解的唯一地址。这个地址物理上嵌入在接口中,这就是它有时被称为物理地址的原因。在示例2-2中,你可以看到来自ifconfig程序输出的MAC地址,显示在第二列中。

示例2-2. MAC地址

ether 52:54:00:11:73:65  txqueuelen 1000  (Ethernet)

接下来我们遇到的层次是“香草饼干层”(Nilla Wafer),这是我们的网络层。在香草饼干层(网络层),我们使用IP地址进行寻址。这也是让我们能够超越本地网络的地址。MAC地址永远不会超出本地网络,而IP地址可以。因为我们可以与不同的“蛋糕店”进行通信,它们的蛋糕和我们的一模一样,都是通过IP地址来实现的,所以这一层就承担了路由的功能。IP地址是路由地址,它让我们能够从一家蛋糕店获得到另一家蛋糕店的“路线指引”。示例2-3展示了一个IP地址,它由4个字节组成,每个字节是8位长,有时称为“八位组”(octets)。这是一个版本4的IP地址。版本6的IP地址是16字节(128位)长,用十六进制表示。就像之前的示例一样,这些数据来自ifconfig的输出。你可以看到IPv4和IPv6地址。

示例2-3. IP地址

inet 192.168.1.253  netmask 255.255.255.0  broadcast 192.168.1.255
inet6 fe80::20c:29ff:fee2:e2c5  prefixlen 64  scopeid 0x20<link>
inet6 fd23:5d5f:cd75:40d2:20c:29ff:fee2:e2c5  prefixlen 64  scopeid 0x0<global>
inet6 fd23:5d5f:cd75:40d2:2627:83d5:9f9b:59ec  prefixlen 64  scopeid 0x0<global>
inet6 2601:18d:8b7f:e33a::d9  prefixlen 128  scopeid 0x0<global>

我们的蛋糕中的第四层是“茶莓层”(teaberry,运输层)。是的,这会是一个味道奇特的蛋糕,但请继续关注。并且,如果你不知道茶莓是什么,应该去了解一下。茶莓口香糖非常好吃。所以,茶莓层给我们提供了端口。这是一种寻址形式。可以这样理解:一旦你到达蛋糕店,你需要知道你要找哪个货架。这和端口是一样的。一旦你找到了蛋糕店(通过IP地址),你就需要找到蛋糕店中的货架,这就是你的端口。端口会连接到一个正在运行的服务(程序),并将其绑定到该货架(端口)。有一些知名端口,特定的服务会在这些端口上运行。这些端口是注册的,尽管服务(例如,Web服务器)可以绑定到不同的端口并在该端口上监听,但这些知名端口是大家熟知的,因此它们常常被用作标准。

在第5层,它变得有些挑战性,因为这一层并不总是被很好地理解。第五层是“草莓层”,因为我们的蛋糕需要一些水果,就算只是水果味道的添加剂。这个层次就是会话层。会话层的作用是协调长期通信,确保一切同步。你可以把它理解为会话层确保你和我在吃蛋糕时保持同步(沟通),就像我们一起吃蛋糕时要同步开始和结束。如果我们需要停下来喝水,会话层会确保我们一起喝水。如果我们想喝牛奶而不是水,会话层会确保我们完全同步,以便我们同时开始和结束,基本上在吃蛋糕时看起来一样——因为这关乎外观。

这让我们来到了花生酱层,因为没有花生酱的蛋糕是什么蛋糕呢?尤其是在我们的蛋糕中已经有了果酱。这一层就是表示层。表示层负责确保所有东西看起来合适、正确。例如,表示层会确保蛋糕上没有到处都是屑屑,确保你放进嘴里的东西确实看起来像蛋糕。

最后,我们有了杏仁酒层(amaretto)。这就是应用层。最终,这一层是最接近食客(用户)的层。它将表示层输出的内容以用户期望的方式呈现给用户,使其可以被消耗。蛋糕类比中的一个重要元素是,当你用叉子切下一口蛋糕时,你是从杏仁酒层切到开心果层的。那就是你如何把蛋糕叉到叉子上的。可是当它被吃掉时,首先进入你嘴里的却是开心果层。我们发送和接收数据消息也是这样。它们从应用层开始构建,然后一路传递下来。当它们被接收时,它们从物理层开始被“消费”,每一层的头部会被去掉,以揭示下一层。

在进行网络测试时,我们可能会在蛋糕的不同层次上工作。这就是为什么理解每一层的作用非常重要。你需要了解每一层的期望,这样你才能判断你所看到的行为是否正确。我们将在接下来的过程中处理跨多个层次的测试,但通常我们将看的每个工具会针对特定的一层。网络通信是关于消耗整个蛋糕的,但有时我们需要将精力集中在某一特定层次上,确保它单独品尝时也没有问题,即使我们最终需要吃掉整个蛋糕才能获取那一层的味道。

压力测试

某些软件,甚至硬件,可能很难应对巨大的负载。在硬件的情况下,例如专门构建的设备或属于物联网(IoT)类别的设备,可能有几个原因导致它无法承受大量的流量。网络接口中内置的处理器可能性能不足,因为整个设备的设计并没有预期会处理大量流量。应用程序可能写得很差,即使它内嵌在硬件中,一个设计不佳的应用程序仍然可能引发问题。因此,安全测试人员需要确保他们负责的基础设施系统在发生不良事件时不会崩溃。

很多人可能会将压力测试与洪水攻击(Flooding attack)联系在一起。然而,压力测试还有其他方式。一种方式是向应用程序发送它可能无法处理的意外数据。针对这种攻击有一些技术可以应对,因此我们将主要关注压垮系统的情况,至于模糊测试(fuzzing attacks),即故意生成无效数据,我们将在后面讨论。尽管如此,在某些情况下,嵌入式设备中的网络栈可能无法处理看起来不符合预期的流量。

警告 你需要确保自己正在测试的系统——特别是当可能导致损坏或中断时,几乎我们讨论的每个话题都有这个潜力——是你拥有或获得许可进行测试的系统。测试任何不是你拥有或没有授权进行测试的系统,至少是不道德的,甚至可能是非法的。无论测试看起来多么简单,都有可能引发损害。永远要确保获得书面许可!

最终,由压力测试引发的任何故障都是可用性问题。如果系统崩溃,任何人都无法访问任何东西。如果应用程序失败,服务将无法提供给用户。你正在进行的就是拒绝服务攻击(DoS)。因此,在执行这些攻击时,必须小心。正如前面提到的,它们确实有伦理影响,同时也可能导致实际的损害,包括对面向客户的服务造成严重中断。稍后我们将进一步讨论这一点。一种简单的压力测试方法是使用像 hping3 这样的工具。这个强大的工具可以在命令行上构造数据包。基本上,你告诉 hping3 你希望字段设置为什么,它就会按照你想要的方式创建数据包。

这并不是说你总是需要指定所有字段。你可以指定你需要的内容,hping3 会按正常方式填充 IP 和传输头部的其他字段。hping3 可以通过不等待任何响应,甚至不使用任何等待周期来进行洪水攻击。该工具将尽可能快地发送尽可能多的流量。你可以在示例 2-4 中看到该工具的输出。

示例 2-4. 使用 hping3 进行洪水攻击

kilroy@rosebud:~$ sudo hping3 --flood -S -p 80 192.168.86.1
HPING 192.168.86.1 (eth0 192.168.86.1): S set, 40 headers + 0 data bytes
hping in flood mode, no replies will be shown
^C
--- 192.168.86.1 hping statistic ---
75425 packets transmitted, 0 packets received, 100% packet loss
round-trip min/avg/max = 0.0/0.0/0.0 ms

注意 在 Kali 的早期版本以及 BackTrack 版本中,你是以 root 用户登录的。在安装过程中并没有创建单独的用户账户。这意味着默认情况下,所有操作都是以 root 用户身份进行的,这是一个严重的安全隐患。现在,Kali 要求你创建一个常规用户账户。就像其他 Linux 发行版一样,要执行需要管理员权限的任务(例如 hping3 的数据包构造),你需要使用 sudo 命令。

当我运行这个命令时,我是通过远程连接到我的 Kali 系统的。一开始我就试图停止它,因为我已经得到了期望的输出。然而,系统以最快的速度把数据包传输到网络中(并且得到回应),这让我很难发送 Ctrl-C 给 Kali 系统,意味着 hping3 没有停止,而是继续在网络中发送大量数据包(幸运的是,我在本地网络上进行测试,而不是试图测试其他人的系统)。操作系统和网络都在处理这些请求,所以长时间没有回应。

在示例 2-4 中,我使用 hping3 向端口 80 发送 SYN 消息,这是一种 SYN 洪水攻击。在这个例子中,我不仅在测试系统如何应对网络栈(操作系统)的洪水,依靠硬件和操作系统的能力来响应流量,还在测试传输层。

操作系统需要为传输控制协议(TCP)连接保留一小块内存。多年前,可用于这些初始消息(称为半开连接)的槽位数量并不大。当时的预期是,连接的系统行为良好,能够完成连接,此时就交给应用程序来管理。一旦可用于接收半开连接的槽位耗尽,新的连接,包括来自合法客户端的连接,都不会被接受。如今,大多数系统在应对 SYN 洪水攻击时更有能力。操作系统会处理这些传入的半开连接,并使用各种技术将其清理掉,包括缩短连接半开状态的超时时间。

这个测试使用 SYN 消息(-S)向端口 80(-p 80)发送。目的是在三次握手的第二阶段收到 SYN/ACK 消息。我不需要指定协议,因为通过仅仅发送 SYN 消息就可以实现,TCP 是唯一使用 SYN 消息的协议。最后,我告诉 hping3 启用洪水模式(--flood)。其他命令行标志也可以实现相同的功能,通过指定消息发送的间隔时间(即等待发送下一条消息的时间)。这种方式更容易记住,同时也非常明确。

注意 程序 hping 已经过几个版本,如你从版本号中的数字 3 可以猜到。这款工具在多个 Linux 发行版中都可以找到。在某些系统中,你可以使用 hping 命令来调用它,而在其他系统中,你可能需要指定版本号(例如 hping2 或 hping3)。

hping3 也非常适用于数据包构造测试,在这种测试中,你创建一些在现实世界中本不该存在的数据包,看看目标系统是否能够处理这些数据包。几乎所有你能想到的数据包篡改攻击都可以通过这个工具来实现。例如,有一种被称为 LAND 攻击(“局域网拒绝服务攻击”)的拒绝服务攻击。在这种攻击中,你向一个设备发送一个 SYN 消息,源地址与目标地址相同。如果接收端的网络栈无法识别这个情况,系统就会向目标(即自己)发送 SYN/ACK 响应,这可能会导致网络接口上出现无休止的消息循环。在 1990 年代末,许多操作系统容易受到这种攻击,并可能导致操作系统崩溃。虽然这种漏洞在大多数操作系统中已得到修复,但仍然有一些服务被发现存在脆弱性。此外,许多组织仍然使用过时的操作系统,这些操作系统可能仍然容易受到攻击,某些设备也可能容易受到影响,具体取决于嵌入式操作系统。尽管如此,示范 LAND 攻击(如示例 2-5 所示)展示了 hping3 能够做的一些操作。你还会看到数据包捕获显示了源地址和目标地址相同的一些消息。该工具本身不会产生输出,因为永远不会收到回应。你在此看到的 -a 标志会伪造源地址。

示例 2-5. 使用 hping3 进行 LAND 攻击

┌──(kilroy@badmilo)-[~]
└─$ sudo hping3 -S -p 80 192.168.1.1 -a 192.168.1.1
HPING 192.168.1.1 (eth0 192.168.1.1): S set, 40 headers + 0 data bytes

18:20:58.843654 IP 192.168.1.1.2258 > 192.168.1.1.http: Flags [S], seq 337020249, ↩ win 512, length 0
18:20:59.844076 IP 192.168.1.1.2259 > 192.168.1.1.http: Flags [S], seq 2123048602, ↩ win 512, length 0
18:21:00.844382 IP 192.168.1.1.2260 > 192.168.1.1.http: Flags [S], seq 1588084076, ↩ win 512, length 0
18:21:01.844912 IP 192.168.1.1.2261 > 192.168.1.1.http: Flags [S], seq 1297206682, ↩ win 512, length 0
18:21:02.845663 IP 192.168.1.1.2262 > 192.168.1.1.http: Flags [S], seq 1143979736, ↩ win 512, length 0
18:21:03.846443 IP 192.168.1.1.2263 > 192.168.1.1.http: Flags [S], seq 1068622138, ↩ win 512, length 0
18:21:04.847084 IP 192.168.1.1.2264 > 192.168.1.1.http: Flags [S], seq 894688939, ↩ win 512, length 0

所有的 TCP 标志都可以根据你的需求进行更改。然而,你不仅限于简单的 TCP SYN 消息。你也可以发送用户数据报协议(UDP)消息。示例 2-6 展示了一个 UDP 消息。在这个例子中,源端口被设置为 0,这是一个不应使用的端口,在正常的流量中很少会看到这个端口。你还会看到我们将源地址设置为随机值。然而,这一点需要非常小心。请记住,当你发送带有随机源地址的消息时,针对你发送的消息的回应会被发送到那个随机的源地址。如果你正在监视网络流量,你会看到这些回应来自互联网上的主机,这些回应被发送到你消息的目标。你开始感到困惑了吗?只要小心你发送的网络流量,因为如果你以任何方式连接到互联网,这些流量就会出去,可能会引发问题。

示例 2-6. 使用 hping3 发送 UDP 消息

──(kilroy@badmilo)-[~]
└─$ sudo hping3 --udp --rand-source --baseport 0 --destport 53 192.168.1.15
HPING 192.168.1.15 (eth0 192.168.1.15): udp mode set, 28 headers + 0 data bytes
ICMP Port Unreachable from ip=192.168.1.15 name=UNKNOWN status=0 port=8 seq=8
ICMP Port Unreachable from ip=192.168.1.15 name=UNKNOWN status=0 port=9 seq=9
ICMP Port Unreachable from ip=192.168.1.15 name=UNKNOWN status=0 port=11 seq=11

通过使用像 hping3 这样的工具在网络栈的底层进行测试,可能会发现系统上的问题,尤其是在较脆弱的设备上。不过,向网络栈更高层看,Kali Linux 提供了许多可以针对不同服务的工具。当你想到互联网时,第一个想到的服务是什么?Spotify?Facebook?Twitter?Instagram?这些都通过 HTTP 提供服务,所以你通常在与 Web 服务器交互。不出所料,我们也可以测试 Web 服务器。这与 Web 服务器上运行的应用程序不同,后者是完全不同的事情,我们会在后面深入探讨。在此之前,我们要确保 Web 服务器本身能够正常运行。

Kali 附带了针对其他协议的测试工具,包括会话发起协议(SIP)和实时传输协议(RTP),这两者都用于 IP 电话(VoIP)。SIP 使用一组类似 HTTP 的协议命令在服务器和终端之间进行交互。当终端希望发起呼叫时,它会发送一个 INVITE 请求。为了将该 INVITE 发送给接收者,它需要通过多个服务器或代理发送。由于 VoIP 是企业中至关重要的应用,因此确定网络中的设备是否能够承受大量请求是至关重要的。

SIP 可以使用 TCP 或 UDP 作为传输协议,尽管早期版本的协议更倾向于使用 UDP 作为传输协议。因此,一些工具,尤其是旧版工具,倾向于使用 UDP。现代实现不仅支持 TCP,还支持传输层安全(TLS),以确保标头不可被读取。需要注意的是,SIP 基于 HTTP,这意味着所有的标头和其他信息都是基于文本的,不像 H.323(另一种 VoIP 协议)那样是二进制的,通常需要进行协议解码才能可视化读取。工具 inviteflood 使用 UDP 作为传输协议,无法切换到 TCP。然而,这样的好处是可以更快地发起洪水攻击,因为不需要等待连接的建立。在示例 2-7 中,你可以看到 inviteflood 的运行。这并不是 Kali Linux 的默认安装工具,所以你需要在使用之前先进行安装。你会注意到版本中的旧日期,这仍然是最新版本。

示例 2-7. SIP 邀请洪水攻击

kilroy@rosebud:~$ sudo inviteflood eth0 kilroy dummy.com 192.168.86.238 150000

inviteflood - Version 2.0
              June 09, 2006

source IPv4 addr:port   = 192.168.86.35:9
dest   IPv4 addr:port   = 192.168.86.238:5060
targeted UA             = kilroy@dummy.com

Flooding destination with 150000 packets
sent: 150000

我们可以分析一下命令行中发生了什么。首先,我们指定了 inviteflood 应该使用的网络接口来发送消息。接下来是用户名。由于 SIP 是一种 VoIP 协议,这个用户名可能是一个电话号码。在此例中,我的目标是一个配置了用户名的 SIP 服务器。用户名后面跟着域名。根据目标服务器的配置,这个域名也可能是一个 IP 地址。如果你不知道用户的域名,可以尝试使用目标系统的 IP 地址。在这种情况下,你会看到命令行上的值重复一次,因为目标就是命令行中的下一个值。最后是发送请求的数量。那 150,000 个请求只花了几秒钟就发送完毕,这意味着服务器能够支持每秒大量的请求。

在继续讨论其他问题之前,我们需要谈谈 IPv6。虽然它不一定被用作从你的网络到任何其他网络的传输协议,但它是可以使用的。例如,如果你连接到 Google,很可能还是通过 IPv4 进行连接。我特别提到 Google,因为它通过其域名系统(DNS)服务器发布了一个 IPv6 地址。Google 并不是唯一一家这样做的公司,但它肯定是最早的一批公司之一。然而,除了能够通过互联网发送 IPv6,您可能在本地网络中使用 IPv6。即使 IPv6 到现在已经接近 30 年的历史,它与 IPv4 的运行时间还不一样——IPv4 花了几十年才排除一些最严重的漏洞。虽然操作系统供应商(如微软和 Linux 团队)在开发和测试中投入了大量时间,但一些设备可能在 IPv6 实现上仍然存在问题。

Kali 包含了两个 IPv6 测试工具套件。每个套件都包含了相当丰富的工具,因为最终 IPv6 不仅仅是地址的变化。一个完整的 IPv6 实现包括地址配置、主机配置、安全性、多播、大数据报、路由处理以及其他一些差异。由于这些是不同的功能区域,因此需要多个脚本来处理它们。

IPv6 在本地网络中的行为已经发生了变化。它不再使用地址解析协议(ARP)来识别本地网络中的邻居,而是通过新的互联网控制消息协议(ICMP)消息来替代和增强这一功能。IPv6 附带了邻居发现协议,用于通过提供本地网络的详细信息来帮助系统连接到网络。ICMPv6 被增强了路由请求和路由广告消息,以及邻居请求和邻居广告消息。这四种消息帮助系统在网络上定位,并获取所有相关的信息,包括本地网关和该网络上使用的域名服务器。

我们将能够测试一些功能,以确定在负载下系统的表现如何,但我们也可以通过操控消息的方式使目标系统发生异常行为。工具 na6、ns6、ra6 和 rs6 都专注于通过使用前面提到的不同 ICMPv6 消息向网络发送任意消息。虽然大多数系统会根据其已知的信息和配置提供合理的网络信息,但这些工具允许我们向网络注入可能损坏的消息,看看系统在接收到这些消息时如何表现。除了这些程序外,该工具包还提供了 tcp6,可以用来向网络发送任意 TCP 消息,从而有可能进行基于 TCP 的攻击。

注意
这里提到的工具包含在 ipv6toolkit 包中,但在 Kali Linux 的默认安装中并未预装。

另一个可以用于压力测试的工具是 t50。它支持多种协议,包括 TCP、UDP、RIP、IGMP、OSPF 等多个协议。除了能够发送特定协议的消息外,t50 还支持洪水模式,尽管并非所有协议都支持洪水攻击。示例 2-8 展示了 t50 支持的协议列表,以及如何使用 t50 来洪泛 IGMP 版本 1 消息。

示例 2-8. 使用 t50 洪泛 IGMP 消息

┌──(kilroy@badmilo)-[~]
└─$ sudo t50 -l
T50 Experimental Mixed Packet Injector Tool v5.8.7b
Originally created by Nelson Brito <nbrito@sekure.org>
Previously maintained by Fernando Mercês <fernando@mentebinaria.com.br>
Maintained by Frederico Lamberti Pissarra <fredericopissarra@gmail.com>

[INFO]  List of supported protocols (--protocol):
         1 - ICMP       (Internet Control Message Protocol)
         2 - IGMPv1     (Internet Group Message Protocol v1)
         3 - IGMPv3     (Internet Group Message Protocol v3)
         4 - TCP        (Transmission Control Protocol)
         5 - EGP        (Exterior Gateway Protocol)
         6 - UDP        (User Datagram Protocol)
         7 - RIPv1      (Routing Internet Protocol v1)
         8 - RIPv2      (Routing Internet Protocol v2)
         9 - DCCP       (Datagram Congestion Control Protocol)
         10 - RSVP      (Resource Reservation Protocol)
         11 - IPSEC     (Internet Security Protocl (AH/ESP))
         12 - EIGRP     (Enhanced Interior Gateway Routing Protocol)
         13 - OSPF      (Open Shortest Path First)
┌──(kilroy@badmilo)-[~]
└─$ sudo t50 192.168.1.1 --protocol IGMPv1 --flood -B
T50 Experimental Mixed Packet Injector Tool v5.8.7b
Originally created by Nelson Brito <nbrito@sekure.org>
Previously maintained by Fernando Mercês <fernando@mentebinaria.com.br>
Maintained by Frederico Lamberti Pissarra <fredericopissarra@gmail.com>

[INFO] Entering flood mode...[INFO] Performing stress testing...
[INFO] Hit Ctrl+C to stop...
[INFO] PID=46521
[INFO] t50 5.8.7b successfully launched at Tue Jun 13 18:53:35 2023

[INFO] (PID:46521) packets:    302395 (8467060 bytes sent).
[INFO] (PID:46521) throughput: 52449.93 packets/second.

无论你进行什么样的压力测试,记录尽可能多的笔记都是很重要的,这样你可以在发生故障时提供详细的信息。监控和日志记录在这里至关重要。

拒绝服务工具

拒绝服务攻击(Denial-of-Service,DoS)不同于压力测试。使用这两类工具的目标可能不同。压力测试通常由开发工具执行,用于提供性能指标。它用于确定程序或系统在压力下的功能表现——无论是体积压力还是格式错误消息的压力。然而,两者之间有一条微妙的界限。在某些情况下,压力测试可能会导致应用程序或操作系统的故障,从而引发拒绝服务攻击。但是,压力测试也可能仅仅导致 CPU 或内存的峰值使用。这些也是有价值的发现,提供了改善程序的机会。CPU 或内存的峰值使用是错误,错误应该被消除。我们在这一节中讨论的是专门为使服务瘫痪而开发的程序。

Slowloris 攻击

与旨在填满部分连接队列的 SYN 洪水类似,也有一些攻击会对 web 服务器造成类似影响。应用程序的资源并不是无限的。通常,应用服务器接受的连接数量是有限的,这取决于应用的设计,并非所有 web 服务器都易受这些攻击影响。这里需要注意的一点是,嵌入式设备通常在内存和处理器资源方面有限。想想任何具有 web 服务器用于远程管理的设备——无线接入点、宽带调制解调器/路由器、打印机。这些设备拥有 web 服务器以便简化管理,但这些设备的主要目的是提供无线接入、宽带调制解调器/路由器或打印服务。这些设备的资源主要用于其预定功能。

这些设备是进行此类测试的一个地方,因为它们通常不会预期有大量连接。这意味着像 Slowloris 这样的攻击可能会让这些服务器下线,从而拒绝其他尝试连接的用户的服务。Slowloris 攻击旨在保持对 web 服务器的大量连接。与洪水攻击的区别在于,Slowloris 是一种慢性攻击,而非洪水攻击。攻击工具通过长时间间隔发送少量数据,保持连接的打开状态。只要攻击工具继续发送少量的部分请求(这些请求永远不会完成),服务器将维持这些连接。

不过,Slowloris 不是唯一一种针对 web 服务器的攻击。另一种是 Apache Killer,它会发送重叠的字节块。web 服务器在尝试将这些块组合在一起时,最终会耗尽内存,无法正确工作。这是 Apache 1.x 和 2.x 版本中的一个漏洞。

Kali 提供了一个名为 slowhttptest 的程序。使用 slowhttptest,你可以对目标发起四种 HTTP 攻击。第一种是慢头部攻击,通常称为 Slowloris(如前所述)。第二种是慢体攻击,通常称为 R-U-Dead-Yet。还可以使用 range 攻击,即 Apache Killer,和慢读取攻击。这些攻击与之前讨论的洪水攻击本质上是相反的,它们通过发送有限数量的网络消息来实现拒绝服务。在示例 2-9 中,我在 Kali 系统上运行了默认的慢头部攻击(Slowloris)对 Apache 进行攻击。没有任何流量离开我的系统,你可以看到,在第 26 秒后,测试结束时没有可用的连接。当然,这只是一个基本的 web 服务器配置,线程不多。如果是具有多个 web 服务器的 web 应用程序来管理负载,它可能会存活更长时间。

示例 2-9. slowhttp 测试输出

┌──(kilroy@badmilo)-[~]
└─$ slowhttptest -H -u http://192.168.1.15

        slowhttptest version 1.8.2
 - https://github.com/shekyan/slowhttptest -
test type:                        SLOW HEADERS
number of connections:            50
URL:                              http://192.168.1.15/
verb:                             GET
cookie:
Content-Length header value:      4096
follow up data max size:          68
interval between follow up data:  10 seconds
connections per seconds:          50
probe connection timeout:         5 seconds
test duration:                    240 seconds
using proxy:                      no proxy

Tue Jun 13 19:05:51 2023:
slow HTTP test status on 10th second:

initializing:        0
pending:             0
connected:           50
error:               0
closed:              0
service available:   YES

在这个例子中,你可以看到,在第 10 秒时,所有 50 个连接都已经建立且处于连接状态。测试持续了 240 秒,整个过程中没有出现错误或断开连接的情况。

这里的目标 Apache 服务器使用多个子进程和多个线程来处理请求。Apache 配置中设定了连接上限:默认情况下,有 2 个服务器、64 个线程的线程限制、每个子进程 25 个线程,以及最多 150 个请求工作线程。当 slowhttptest 工具将连接数最大化时,系统上的 Apache 进程数为 54 个。这意味着有 53 个子进程和 1 个父进程。为了处理这些请求所需的连接数,Apache 启动了多个子进程,并且每个子进程都有多个线程。启动了这么多进程,意味着这台服务器需要处理大量的连接。考虑到这台 Apache 服务器在本文写作时完全是最新版本,尽管这些攻击已经存在多年,但显然这些类型的攻击依然能够成功。当然,正如前面所提到的,这完全取决于被测试网站的架构。

基于 SSL 的压力测试

另一种资源性攻击不涉及带宽,而是针对处理器的利用率,重点在于加密的处理需求。长期以来,电子商务网站使用安全套接字层(SSL)或 TLS 来保持客户端与服务器之间的加密连接,以确保所有通信的隐私。虽然现在仍然常常称其为 SSL/TLS,但自 2015 年以来,SSL 已经被弃用。TLS 使用的时间比 SSL 更长。因此,在这里我们将其称为 TLS,因为这是我们使用的加密协议。如今,许多服务器使用 TLS。比如,如果你尝试在 Google 上搜索,你会发现它默认就是加密的。同样,许多其他大型网站,如 Microsoft 和 Apple,也默认加密所有流量。如果你尝试通过 http:// 访问这些网站,而不是 https://,你会发现服务器会自动将连接转换为 https。

不过,TLS 的问题是加密需要处理能力。现代处理器完全能够跟上正常的加密负载,特别是因为现代加密算法在处理器利用率方面通常很高效。然而,任何使用 TLS 的服务器都会有一定的处理开销。首先,服务器发送的消息通常更大,这意味着加密这些较大的消息所需的处理能力要比加密客户端发送的小消息更多。另外,客户端系统可能一次只发送少量消息,而服务器需要加密许多并发客户端的消息,而这些客户端可能会同时与服务器建立多个连接。负载的主要来源是创建用于加密会话的密钥。

Kali 中有能力针对过时的服务和功能进行攻击。问题在于,这些早已被取代的程序仍然在很多地方被使用。因此,能够测试它们仍然非常重要。一个此类服务就是 SSL 加密。虽然 SSL 已经不应再被使用,并且已被更安全的技术所取代,但这并不意味着你不会遇到仍在使用 SSL 的服务器。程序 thc-ssl-dos 就是针对使用 SSL 的服务器进行攻击的。SSL 应该已经被淘汰,因为其存在许多漏洞,而这些漏洞在 SSL 之后的技术中已经得到解决,但这并不意味着你不会遇到它。

示例 2-10 显示了 thc-ssl-dos 在一个配置了 SSL 的服务器上的运行。然而,由于 SSL 的问题已经存在了很长时间,因此其底层库通常会禁用 SSL。尽管是在一个较旧的安装上运行,程序仍未能够完成 SSL 握手。然而,如果你发现一个确实配置了 SSL 的服务器,你就能测试它是否容易受到拒绝服务攻击的影响。

示例 2-10. 使用 thc-ssl-dos 工具进行 SSL DoS 攻击

root@rosebud:~# thc-ssl-dos -l 100 192.168.86.239 443 --accept
     ______________ ___  _________
     __    ___/   |   \ _   ___ \
       |    | /    ~    /    \  /
       |    | \    Y    /\     ____
       |____|  ___|_  /  ______  /
                     /          /
            http://www.thc.org

          Twitter @hackerschoice

Greetingz: the french underground

Waiting for script kiddies to piss off................
The force is with those who read the source...
Handshakes 0 [0.00 h/s], 1 Conn, 0 Err
SSL: error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol
#0: This does not look like SSL!

这次失败突出了进行安全测试时的一大挑战:发现漏洞可能很困难。利用已知漏洞同样也可能很困难。这也是现代攻击常常使用社会工程学的原因,利用人类的信任倾向和行为习惯来进行利用——实际上,利用技术漏洞往往比操控人类更为复杂。这并不意味着这些非人类的问题不可能存在,因为漏洞的发现和公告依然是经常发生的事情。可以参考 Bugtraq 和 CVE(公共漏洞和暴露)项目,了解更多证据。

DHCP 攻击

动态主机配置协议(DHCP)有一个名为 DHCPig 的测试程序,这也是一种消耗资源的攻击,旨在耗尽 DHCP 服务器的可用资源。这种攻击有时被称为 DHCP 饥饿攻击,其目的是使其他用户无法从 DHCP 服务器中获取 IP 地址。由于 DHCP 服务器分配 IP 地址和其他网络配置,如果企业的员工无法获取 IP 地址,那将会是一个问题。虽然 DHCP 服务器通常会分配带有长租期的地址(即客户端可以在不重新请求的情况下使用地址的时间),但很多 DHCP 服务器配置了较短的租期。短租期在用户频繁切换网络的情况下很重要。由于用户不断进入和退出网络,有时停留时间很短,如果客户端保持租期不释放,也会消耗服务器资源。问题在于,当客户端有短租期时,像 DHCPig 这样的工具可以在客户端获取租期之前抢占到这些即将过期的租期,导致客户端无法获取 IP 地址,无法在网络上进行任何操作。

实际应用中,DHCP 饥饿攻击可以帮助攻击者控制网络流量。攻击者发起 DHCP 饥饿攻击,消耗所有可用的 IP 地址。同时,攻击者启动自己的 DHCP 服务器,可能将系统指向攻击者控制的 DNS 服务器。也可能将默认路由器指向攻击者的系统,使得所有的网络流量都会经过攻击者的系统。示例 2-11 显示了如何使用 DHCPig 工具消耗本地 DHCP 服务器上的所有可用租期。

示例 2-11. 使用 dhcpig

┌──(kilroy@badmilo)-[~]
└─$ sudo dhcpig -l eth0
[ -- ] [INFO] - using interface eth0
[DBG ] Thread 0 - (Sniffer) READY
[DBG ] Thread 1 - (Sender) READY
[--->] DHCP_Discover
[ ?? ]          waiting for first DHCP Server response
[ ?? ]          waiting for first DHCP Server response

如果你需要完全的程序化方式来创建网络数据包,可以使用工具 Scapy。这个库提供了对网络协议的访问,你可以自定义数据包的格式。虽然你可以使用 Scapy 编写 Python 脚本,但也可以通过命令行接口使用 Scapy 工具。你可以在 Scapy 工具中实时编写 Python 代码,这意味着代码在你编写时就会执行。示例 2-12 展示了如何使用 Scapy 在 IP 数据包上构建 TCP 段,因为 Scapy 允许你分层构建消息。一旦数据包定义完毕,你可以通过不同的方式发送它。示例 2-12 中展示了其中的三种发送方法。

示例 2-12. 使用 Scapy

>>> p = IP(dst="192.168.1.1", ttl=2, id=15)/TCP(seq=RandInt(), sport=RandShort(), dport=RandShort())
>>> send(p)
.
Sent 1 packets.
>>> sr(p)
Begin emission:
Finished sending 1 packets.
*
Received 1 packets, got 1 answers, remaining 0 packets
(<Results: TCP:1 UDP:0 ICMP:0 Other:0>, <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>)
>>> sr1(p)
Begin emission:
Finished sending 1 packets.
*
Received 1 packets, got 1 answers, remaining 0 packets
<IP  version=4 ihl=5 tos=0x0 len=40 id=40192 flags= frag=0 ttl=128 proto=tcpchksum=0xcbf3 src=192.168.1.1 dst=192.168.79.138 |<TCP  sport=849 dport=26901seq=2570441854 ack=913161814 dataofs=5 reserved=0 flags=RA window=64240chksum=0x1425 urgptr=0 |<Padding  load='\x00\x00\x00\x00\x00\x00' |>>>

由于我们使用的是编程接口,因此我们不必仅仅使用数字值来填充字段。相反,我们可以生成随机值。此示例中使用了随机的 TCP 源端口和目标端口,这可能不会是一个有价值的包,但它演示了如何生成随机值。我们可以生成完整的整数,也可以生成短整数,这对于需要 16 位字段大小(如端口值)来说是必要的。如示例所示,我们可以控制协议中的所有字段。虽然此示例没有显示以太网层,但如果需要,我们可以添加。你也可以设置 MAC 地址为目标系统,但修改 IP 地址以查看系统如何处理它。

在发送和接收方面,你可以直接发送包,如第一个发送示例所示。你也可以使用 sr 函数,它意味着“发送和接收”,但你不会得到响应的详细信息。最后,sr1 是用于在发送后查看完整包细节的函数。这假设你会收到响应,具体取决于你创建的数据包类型。通过 send 函数,你还可以添加一个循环参数,让 Scapy 继续发送数据包,直到你按下 Ctrl-C 停止它。对于我们创建的数据包 p,你可以使用 send(p, loop=1) 来循环发送。

由于我们完全控制数据包及其参数,我们还可以修改源和目标 IP 地址,如示例 2-13 所示,重新创建之前提到的 LAND 攻击。在这个例子中,我们没有收到响应,因为源 IP 地址不是我们的地址,而接收系统的网络栈将其发送回拥有该 IP 地址的系统。

示例 2-13. 使用 Scapy 进行 LAND 攻击

>>> p = IP(dst="192.168.1.1", src="192.168.1.1")/TCP(dport=80)
>>> sr1(p)
Begin emission:
Finished sending 1 packets.
...................................................................................
...................................................................................
...................................................................................
...................................................................................
...................................................................................
...................................................................................
..................................................^C
Received 548 packets, got 0 answers, remaining 1 packets

你当然也可以使用 Scapy 发送合法的消息,它支持许多协议。由于 HTTP 是常见的协议,我们可以使用 Scapy 创建并发送 Web 请求。示例 2-14 展示了两种方法。第一种方法是直接使用套接字与要发送的 HTTP 文本,文本被添加到数据包创建语法中,换句话说,文本直接作为负载添加。第二种方法是加载 HTTP 层,以便我们可以使用 HTTP 方法。我们将使用 HTTP/HTTPRequest 创建一个请求,这会自动创建我们需要的所有必要数据。我们没有为 HTTPRequest 添加任何参数,但我们可以使用 Method(默认为 GET)和 Path 来指定要从服务器检索的资源。

示例 2-14. 使用 Scapy 发送 HTTP 消息

>>> p = IP(dst="192.168.1.15")/TCP()/"GET / HTTP/1.1\rHost:192.168.1.15\r\n"
>>> reply = sr1(p)
Begin emission:
Finished sending 1 packets.
*
Received 1 packets, got 1 answers, remaining 0 packets
>>> print(reply)
IP / TCP 192.168.1.15:http > 192.168.79.138:ftp_data SA / Padding
>>> load_layer("http")
>>> request = HTTP()/HTTPRequest()
>>> socket = TCP_client.tcplink(HTTP, "192.168.1.15", 80)
>>> answer = socket.sr1(request)
Begin emission:
Finished sending 1 packets.

这仅仅是 Scapy 功能的一小部分。正如之前所说,Scapy 为我们提供了以编程方式脚本化网络消息的简便方法,让我们能够完全控制协议的各个元素。你可以通过 Scapy 很容易地发送损坏的消息。例如,你不必使用 HTTP 服务器已知的标准请求。你可以创建自己的请求方法,看看服务器如何响应它。当涉及到像 IP 或 TCP 这样的基于二进制的协议时,协议明确由字节序列构成,你将会在发送内容上受到限制。你不能在目标地址中发送 AAAAAAAAAAA。首先,IPv4 的目标地址只有 4 个字节,而我们尝试发送 11 个字节,这样目标地址就会被填充为 41414141,翻译成 65.65.65.65。这样做并不会得到太多有用的信息。每个字节的值只能在 0 到 255 之间,因此如果你想玩弄这些基于二进制的协议,你需要稍微发挥一些创造力。

加密测试

我们已经能够加密互联网连接上的流量超过20年了。加密,像很多与信息安全相关的技术一样,是一个不断变化的目标。当Netscape在1995年发布SSL的第一个版本时,已经有一个版本因为被发现的问题而被丢弃。第二个版本没有持续太久,就因为发现的问题,迫使在1996年发布了第三个版本。SSLv2和SSLv3都因为它们在处理加密方面存在的问题而被判定为禁止使用。

加密的网络流量遵循的过程并不像简单地将信息加密后发送那么简单,尽管这只是整体过程的一部分。加密依赖于密钥。任何加密过程中最敏感的部分始终是密钥。被加密的消息只有在能够解密时才有价值。如果我给你发送一条加密消息,你就需要密钥才能解密它。这就是挑战的开始。

处理密钥有两种方式。第一种是非对称加密,它使用两个密钥,一个用于加密,一个用于解密。你可能也会听到它被称为公钥加密。其基本思想是每个人都有两个密钥——公钥和私钥。公钥是任何人都可以拥有的。实际上,它只有在每个人都能够访问他人的公钥时才能发挥作用。使用公钥加密一条消息意味着该消息只能通过使用私钥进行解密。这两个密钥在数学上是相关的,并且基于对大数的计算。看起来这种方法是合理的,对吧?非对称加密的问题在于它在计算上是困难的。

这就引出了对称加密。如你所料,这种加密方式使用一个密钥。相同的密钥用于加密和解密。对称密钥加密在计算上更容易。然而,对称加密有两个问题。第一个问题是,对称密钥使用的时间越长,它就越容易受到攻击。原因是攻击者可以收集大量密文(即将明文输入加密算法的结果),并开始对其进行分析,希望推导出密钥。一旦密钥被识别,任何用该密钥加密的流量都可以轻松解密。

第二个且更重要的问题是,一旦我们有了密钥,如何共享密钥呢?毕竟,只有我们双方都拥有密钥,这才有效。那么,如果我们不在物理接近的情况下,如何获得密钥呢?如果我们在物理上接近,我们是否还需要加密消息?我们或许曾经见过面,分享过密钥,但这意味着我们得一直使用这个密钥,直到下次见面并能够创建一个新密钥,这样我们俩才能都有它。我们越长时间不见,继续使用同一个密钥,就越容易遇到前面提到的第一个问题。

事实证明,两个数学家解决了这个问题,尽管他们并不是第一个解决它的人。他们只是第一个能够公开其工作的。那些先前的工作者属于政府机构,受限于不能与任何人分享他们的工作。Whitfield Diffie和Martin Hellman提出了让双方独立推导密钥的想法。本质上,我们两人都从一个共享的值开始。这个值可以安全地未加密分享,因为它的后续处理才是关键。我们俩都使用一个我们都知道的数学公式,应用一个秘密值。再次强调,它是否是公开的并不重要,重要的是这个秘密值。我们交换各自的计算结果,然后重新应用对方的计算结果,使用我们的秘密值。通过这种方式,我们将通过相同的数学过程,从一个起点出发,最终得到相同的密钥。

我们之所以要经过这一过程,是因为在实践中,所有这些机制都被使用。Diffie-Hellman密钥交换与公钥加密一起使用,以推导会话密钥,即对称密钥。这意味着会话使用的是一个计算上负担较小的密钥和算法来完成加密和解密大部分的客户端和服务器之间的通信。

如前所述,SSL已经不再作为加密协议使用。相反,TLS是当前使用的协议。TLS经历了几个版本,再次展示了加密技术的挑战。当前版本是1.3,而1.4版本在本文撰写时仍处于草案阶段。每个版本都引入了基于对协议破解的持续研究所做的修复和更新。

确定您正在测试的服务器是否使用过时协议的一种方法是使用像sslscan这样的工具。这个程序通过探测服务器来识别正在使用的任何加密算法。因为在与服务器进行握手时,它会提供一个支持的密码套件列表供客户端选择,所以这一点很容易确定。因此,sslscan只需要与服务器启动一个加密会话,就能获取所需的所有信息。示例2-15显示了测试配置了加密的Apache服务器的结果。

示例2-15. 对本地系统运行sslscan

┌──(kilroy@badmilo)-[~]
└─$ sslscan 192.168.1.15
Version: 2.0.16-static
OpenSSL 1.1.1u-dev  xx XXX xxxx

Connected to 192.168.1.15

Testing SSL server 192.168.1.15 on port 443 using SNI name 192.168.1.15

  SSL/TLS Protocols:
SSLv2     disabled
SSLv3     disabled
TLSv1.0   disabled
TLSv1.1   disabled
TLSv1.2   enabled
TLSv1.3   enabled

  TLS Fallback SCSV:
Server supports TLS Fallback SCSV

  TLS renegotiation:
Session renegotiation not supported

  TLS Compression:
Compression disabled

  Heartbleed:
TLSv1.3 not vulnerable to heartbleed
TLSv1.2 not vulnerable to heartbleed

  Supported Server Cipher(s):
Preferred TLSv1.3  256 bits  TLS_AES_256_GCM_SHA384        Curve 25519 DHE 253
Accepted  TLSv1.3  256 bits  TLS_CHACHA20_POLY1305_SHA256  Curve 25519 DHE 253
Accepted  TLSv1.3  128 bits  TLS_AES_128_GCM_SHA256        Curve 25519 DHE 253
Preferred TLSv1.2  256 bits  ECDHE-RSA-AES256-GCM-SHA384   Curve 25519 DHE 253
Accepted  TLSv1.2  256 bits  DHE-RSA-AES256-GCM-SHA384     DHE 2048 bits
Accepted  TLSv1.2  256 bits  ECDHE-RSA-CHACHA20-POLY1305   Curve 25519 DHE 253
Accepted  TLSv1.2  256 bits  DHE-RSA-CHACHA20-POLY1305     DHE 2048 bits
Accepted  TLSv1.2  256 bits  DHE-RSA-AES256-CCM8           DHE 2048 bits
Accepted  TLSv1.2  256 bits  DHE-RSA-AES256-CCM            DHE 2048 bits
Accepted  TLSv1.2  256 bits  ECDHE-ARIA256-GCM-SHA384      Curve 25519 DHE 253
Accepted  TLSv1.2  256 bits  DHE-RSA-ARIA256-GCM-SHA384    DHE 2048 bits
Accepted  TLSv1.2  128 bits  ECDHE-RSA-AES128-GCM-SHA256   Curve 25519 DHE 253
Accepted  TLSv1.2  128 bits  DHE-RSA-AES128-SHA            DHE 2048 bits
Accepted  TLSv1.2  128 bits  DHE-RSA-CAMELLIA128-SHA       DHE 2048 bits
Accepted  TLSv1.2  256 bits  AES256-GCM-SHA384
Accepted  TLSv1.2  256 bits  AES256-CCM8
Accepted  TLSv1.2  256 bits  AES256-CCM
Accepted  TLSv1.2  256 bits  ARIA256-GCM-SHA384

  Server Key Exchange Group(s):
TLSv1.3  128 bits  secp256r1 (NIST P-256)
TLSv1.3  192 bits  secp384r1 (NIST P-384)
TLSv1.3  260 bits  secp521r1 (NIST P-521)
TLSv1.3  128 bits  x25519
TLSv1.3  224 bits  x448
TLSv1.3  112 bits  ffdhe2048
TLSv1.3  128 bits  ffdhe3072
TLSv1.3  150 bits  ffdhe4096
TLSv1.3  175 bits  ffdhe6144
TLSv1.3  192 bits  ffdhe8192
TLSv1.2  128 bits  secp256r1 (NIST P-256)
TLSv1.2  192 bits  secp384r1 (NIST P-384)
TLSv1.2  260 bits  secp521r1 (NIST P-521)
TLSv1.2  128 bits  x25519
TLSv1.2  224 bits  x448

  SSL Certificate:
Signature Algorithm: sha256WithRSAEncryption
RSA Key Strength:    2048

Subject:  portnoy.washere.com
Issuer:   portnoy.washere.com

Not valid before: Jun 17 21:32:45 2023 GMT
Not valid after:  Jun 16 21:32:45 2024 GMT

sslscan将帮助确定服务器是否容易受到Heartbleed攻击,这是一种针对服务器/客户端加密的漏洞,可能导致密钥暴露给恶意用户。然而,更重要的是,sslscan将为我们提供支持的密码套件列表。在“Supported Server Cipher(s)”部分,经过编辑后会看到多个列,这些信息可能对你来说并不十分重要。第一列易于阅读,它指示协议和密码套件是否被接受,以及是否被优先选择。第一个优选的密码套件是针对TLS 1.3版本,使用256位的高级加密标准(AES)密钥。你会注意到,每个版本的TLS都有自己的优选密码套件。当前仅有两个版本的TLS在使用,因此只有两个优选密码套件。第二列是协议和版本。由于在基础库中删除了对SSL的支持,因此该服务器上没有启用SSL。接下来的列是密钥强度。

注意:
密钥大小只能在相同算法之间进行比较。Rivest-Shamir-Adleman(RSA)是一个非对称加密算法,其密钥大小是1,024的倍数。AES是一个对称加密算法,其密钥大小为128和256。并不意味着RSA在强度上比AES高几个数量级,因为它们使用密钥的方式不同。即使是比较同类型的算法(非对称与对称)也是误导性的,因为这些算法使用密钥的方式完全不同。

接下来的列是密码套件。你会注意到它叫做“密码套件”,因为它包含了多个用途不同的算法。以这个列表为例:DHE-RSA-AES256-GCM-SHA384。第一部分DHE表示我们正在使用临时Diffie-Hellman进行密钥交换。第二部分RSA是Rivest-Shamir-Adleman的缩写,这是三位开发该算法的人的名字。RSA是一个非对称密钥算法,用于验证双方身份,因为密钥存储在证书中,证书还包含了服务器的身份信息。如果客户端也有证书,则可以进行双向认证。否则,客户端可以基于证书中列出的主机名和客户端要访问的主机名来验证服务器身份。非对称加密还用于加密在客户端和服务器之间传输的密钥。

注意
在整个讨论中,我频繁使用“客户端”和“服务器”这两个词,理解它们的含义是非常有用的。在任何网络对话中,总是会有客户端和服务器。即使是在点对点(P2P)网络中,也会有客户端和服务器,分别表示发起流量的一方(客户端)和接收连接的一方(服务器)。这并不意味着实际的服务器就坐落在数据中心,或者某个服务专门监听客户端请求。相反,这意味着存在某个被消费的服务。客户端始终是发起对话的一方,而服务器始终是响应的一方。这使得我们能够清楚地“看到”这两方——谁发起了对话,谁响应了对话。

接下来的部分是对称加密算法。这表示AES算法提供的是256位密钥大小。值得注意的是,AES本身不是一个算法,而是一个标准。该算法有一个自己的名称。几十年来,曾经使用的标准是数据加密标准(DES),它基于由霍斯特·费斯特尔(Horst Feistel)及其同事在IBM开发的Lucifer密码。在1990年代,人们发现DES已显得有些过时,且很快将被攻破。因此,开展了对新算法的搜索,最终选定了Rijndael算法作为AES的基础。最初,AES使用128位的密钥,但直到最近,密钥强度才普遍提升到256位。

AES是用来加密会话的算法。这意味着会话密钥使用的是256位密钥。这个密钥是在会话开始时派生并共享的。如果会话持续足够长的时间,可能会重新生成会话密钥,以防止密钥派生攻击。正如之前所提到的,这个密钥被对话双方用来进行加密和解密。

GCM部分是Galois/Counter模式,这是一种块密码工作方式,用来提供数据的完整性和机密性。加密的数据会与一个标签相关联,这个标签是在数据加密时生成的。该标签用于验证数据和标签是否被篡改过。

最后,您会注意到SHA-384算法。它是使用384位长度的哈希值的安全哈希算法(SHA)。SHA是一种加密算法,用于验证数据是否发生了变化。您可能熟悉消息摘要5(MD5)算法,它有相似的作用。不同之处在于输出的长度。MD5输出的长度始终是32个字符,也就是128位(每个字节只有4位被使用)。这已被普遍替换为SHA-1或更高版本。SHA-1会生成40个字符,或者160位(同样,只有每个字节的4位被使用)。在我们的例子中,使用的是SHA-384,它会生成96个十六进制字符,因为它的长度是48字节,每个字节由两个十六进制字符表示。无论数据的长度如何,输出的长度始终是一样的。该值会从一方发送到另一方,用来判断数据是否发生了变化。如果即使只有一个比特发生变化,哈希值——即SHA或MD5算法的输出——也会发生变化。

所有这些算法共同工作,构成了TLS协议(以及之前的SSL协议)。为了实现有效的加密,并防止被破解,所有这些算法都是必要的。我们需要能够派生会话密钥。我们需要能够在生成会话密钥之前认证双方并共享信息。我们需要会话密钥和算法来加密并解密会话数据。最后,我们需要确保没有数据被篡改。您在示例中看到的是一套强加密套件。

如果在输出中看到像3DES这样的内容,那么这就是一个容易受到针对会话密钥攻击的服务器。这可能导致密钥被破解,从而使得密文被解密为明文,被非授权的第三方读取。此外,虽然之前简要提到过,像sslscan这样的工具可以验证所使用的协议是否易受已知漏洞的攻击。

您可能偶尔会在我们看到AES384的地方看到NULL。这意味着请求不使用加密。这背后有一些原因。您可能不太关心保护传输内容,但可能非常关心您知道自己在与谁交谈,并确保数据在传输过程中没有被篡改。因此,您可能请求不使用加密,以避免加密带来的开销,但您仍然能享受到所选密码套件的其他部分带来的好处。

关于加密的争论永无止境。即使现在,仍在进行研究,旨在识别加密算法和协议中的漏洞,并加以利用。随着更强的密钥开始被使用,新的算法被开发,您会在测试输出中看到套件的差异。

数据包捕获

在进行网络测试时,能够查看通过网络传输的内容是非常有用的。为了查看发送了什么,我们需要使用一个捕获数据包的程序。事实上,我们所做的实际上是捕获帧。我之所以这么说,是因为网络协议栈的每一层对于包含该层数据的捆绑包都有不同的术语。请记住,随着我们向下移动网络协议栈,头部信息会被一层层添加。因此,最后添加的头部是第2层头部。第2层的协议数据单元(PDU)是帧。当我们到达第3层时,我们谈论的是数据包。第4层根据所使用的协议有数据报或段。

多年前,捕获数据包是一项昂贵的操作,因为它需要一个特殊的网络接口,该接口能够进入混杂模式。之所以叫混杂模式,是因为默认情况下,网络接口只会查看MAC地址。网络接口知道它自己的地址,因为它附着在硬件上。如果一个传入帧的地址与MAC地址匹配,那么该帧会被转发到操作系统。同样,如果MAC地址是广播地址,该帧也会被转发到操作系统。在混杂模式下,所有传入的帧都会被接收。这意味着所有的帧,无论它们是否是为该特定系统发送的,都会被转发到操作系统。能够仅查看面向该接口的帧是很有价值的,但能看到通过网络接口传输的所有帧更加有价值。

现代网络接口通常不仅支持全双工和自动协商等特性,还支持混杂模式。这意味着我们不再需要协议分析仪(以前能够执行此类工作的硬件通常被称为协议分析仪),因为每个系统都可以充当协议分析仪。我们只需要知道如何捕获帧,然后深入查看它们,了解发生了什么。

使用 tcpdump

虽然其他操作系统也有其他数据包捕获程序,例如 Solaris 中的 snoop,但如今的事实标准数据包捕获程序,特别是在 Linux 系统上,如果您只能访问命令行,那就是 tcpdump。稍后我们会看一个 GUI 界面,但了解 tcpdump 的使用非常有价值。因为您不一定总能访问带有 GUI 的完整桌面。在许多情况下,您只能使用控制台或仅能通过 SSH 会话运行命令行程序。因此,tcpdump 将成为您的好朋友。例如,之前我用它来验证我们的 SIP 测试程序使用的协议确实是 UDP,而不是 TCP。使用 tcpdump 可以帮助您了解一个程序的运行情况,尤其是当它本身没有提供足够的调试信息时。

在我们开始查看选项之前,先看一下 tcpdump 的输出。通过查看输出了解发生了什么需要一些练习。当我们运行 tcpdump 而不加任何选项时,我们会看到经过的数据包的简短摘要。示例 2-16 是 tcpdump 输出的一个例子。

示例 2-16. tcpdump 输出

10:26:26.543550 IP binkley.lan.57137 > testwifi.here.domain: 32636+ PTR?
  c.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.f.f.ip6.arpa. (90)
10:26:26.555133 IP testwifi.here.domain > binkley.lan.57137: 32636 NXDomain
  0/1/0 (154)
10:26:26.557367 IP binkley.lan.57872 > testwifi.here.domain: 44057+ PTR?
  201.86.168.192.in-addr.arpa. (45)
10:26:26.560368 IP testwifi.here.domain > binkley.lan.57872: 44057* 1/0/0 PTR
  kilroyhue.lan. (99)
10:26:26.561678 IP binkley.lan.57726 > testwifi.here.domain: 901+ PTR?
  211.1.217.172.in-addr.arpa. (44)
10:26:26.583550 IP testwifi.here.domain > binkley.lan.57726: 901 4/0/0 PTR
  den16s02-in-f19.1e100.net., PTR iad23s26-in-f211.1e100.net., PTR
  den16s02-in-f19.1e100.net., PTR iad23s26-in-f211.1e100.net. (142)
10:26:26.585725 IP binkley.lan.64437 > testwifi.here.domain: 23125+ PTR?
  0.0.0.0.in-addr.arpa. (38)
10:26:26.598434 IP testwifi.here.domain > binkley.lan.64437: 23125 NXDomain
  0/1/0 (106)
10:26:26.637639 IP binkley.lan.51994 > 239.255.255.250.ssdp: UDP, length 174

输出的第一列是时间戳。这个时间戳并不是从数据包本身确定的,因为时间并不是作为任何头部的一部分传输的。我们得到的是从午夜以来的小时、分钟、秒和秒的小数部分。换句话说,这是一天中的时间,精确到小数秒。第二列是传输协议。我们不会看到第2层协议,因为它由网络接口决定,因此不需要特别说明。要了解第2层协议,您需要了解一些关于您的网络接口的信息。常见的第2层协议是以太网。

接下来的数据是对话的两个端点。这不仅包括 IP 地址,还包括端口信息。所以,binkley.lan 是第一个数据包的源地址,testwifi.here 是目的地址。默认情况下,tcpdump 会将 IP 地址转换为主机名。如果不希望它这么做,您可以在命令行中加上 -n 选项。这将加快数据包捕获的速度,并减少捕获的数据包数量,因为系统不会为每个数据包进行 DNS 查找。

您会注意到每个 IP 地址旁边都有另一个值。从源地址 binkley.lan.57137 来看,57137 是端口号。这是源端口,而在接收端,您可以看到 testwifi.here.domain。这意味着 testwifi.here 正在接收来自域名服务器端口的数据。再说一次,就像主机名与 IP 地址的转换一样,如果您不希望 tcpdump 对端口号进行查找,基于常见的端口号,您可以在命令行中加上 -n 选项,tcpdump 将直接显示数字信息。在这种情况下,.domain 转换为 .53,这是数字值。我们知道这是一个 UDP 消息,因为在目的地信息之后有这个提示。

主要内容:示例 2-16 展示的是 DNS 请求和响应。这是因为我们使用 tcpdump 进行反向 DNS 查找,以确定与 IP 地址相关联的主机名。tcpdump 输出的其余部分描述了数据包的内容。在 TCP 消息的情况下,您可能会看到设置在 TCP 头部的标志,或者您可能会看到序列号信息。

这次,我们将通过使用 -v 标志来查看更详细的输出。tcpdump 支持多个 -v 标志,根据您所需的详细程度来决定。我们还将使用 -n 标志,以查看没有地址查找的输出结果。示例 2-17 显示了更详细的输出。

示例 2-17. tcpdump 的详细输出

11:39:09.703339 STP 802.1d, Config, Flags [none], bridge-id
  7b00.18:d6:c7:7d:f4:8a.8004, length 35 message-age 0.75s, max-age 20.00s,
  hello-time 1.00s, forwarding-delay 4.00s root-id 7000.2c:08:8c:1c:3b:db,
  root-pathcost 4
11:39:09.710628 IP (tos 0x0, ttl 233, id 12527, offset 0, flags [DF], proto TCP (6),
  length 553) 54.231.176.224.443 > 192.168.86.223.62547: Flags [P.],
  cksum 0x6518 (correct), seq 3199:3712, ack 1164, win 68, length 513
11:39:09.710637 IP (tos 0x0, ttl 233, id 12528, offset 0, flags [DF], proto TCP (6),
  length 323) 54.231.176.224.443 > 192.168.86.223.62547: Flags [P.],
  cksum 0x7f26 (correct), seq 3712:3995, ack 1164, win 68, length 283
11:39:09.710682 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6),
  length 40) 192.168.86.223.62547 > 54.231.176.224.443: Flags [.],
  cksum 0x75f2 (correct), ack 3712, win 8175, length 0
11:39:09.710703 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6),
  length 40)

输出看起来基本相同,不同之处在于这次显示的是数字而不是主机名或端口名。这是因为在运行 tcpdump 时使用了 -n 标志。您仍然会看到每个会话的两个端点,通过 IP 地址和端口号来标识。使用 -v 标志时,您会获得更多的头部信息。您将看到校验和是否正确(或不正确)。此外,还会看到其他字段,包括生存时间(TTL)值和 IP 标识符。

即使我们切换到 -vvv 来获得最详细的输出,您也不会得到完整的数据包解码来进行分析。然而,我们可以使用 tcpdump 捕获数据包并将其写入文件。接下来我们需要讨论的是 快照长度(snap length) 。这就是快照长度,或每个数据包捕获的字节数。默认情况下,tcpdump 捕获 262,144 字节。您可能能够将该值设置为更低的值。将值设置为 0 表示 tcpdump 应该捕获最大尺寸。实际上,这告诉 tcpdump 将快照长度设置为默认值 262,144。

为了将数据包捕获写出,我们需要使用 -w 标志并指定一个文件。完成后,我们就得到了一个数据包捕获(pcap)文件,您可以将其导入到任何能够读取这些文件的工具中。稍后我们将介绍其中一个工具。

伯克利数据包过滤器 (BPF)

tcpdump 的另一个重要特性是 伯克利数据包过滤器(BPF) ,它将在接下来的操作中为我们提供极大帮助。BPF 是一组字段和参数,可以让我们限制捕获的数据包。在繁忙的网络中,抓取数据包可能会在短时间内占用大量磁盘空间。如果您提前知道自己要查找什么,可以创建一个过滤器,只捕获您需要查看的数据包。这不仅可以帮助您更高效地浏览捕获的数据,还能节省大量时间。

一个基本的过滤器是指定您想要捕获的协议。例如,您可以选择只捕获 TCP 或 UDP 数据包,也可以选择只捕获 IP 或其他协议。在示例 2-18 中,您可以看到只捕获 ICMP 数据包的情况。您会注意到,应用过滤器时,我只是将它加到命令行的末尾。结果是,tcpdump 只会显示 ICMP 数据包。所有的数据包仍然进入接口并被发送到 tcpdump,但它会决定是否显示这些数据包,或者如果您正在将数据包写入文件,则决定写入哪些数据包。

示例 2-18. 使用 BPF 的 tcpdump

root@rosebud:~# tcpdump icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
12:01:14.602895 IP binkley.lan > rosebud.lan: ICMP echo request, id 8203, seq 0,
  length 64
12:01:14.602952 IP rosebud.lan > binkley.lan: ICMP echo reply, id 8203, seq 0,
  length 64
12:01:15.604118 IP binkley.lan > rosebud.lan: ICMP echo request, id 8203, seq 1,
  length 64
12:01:15.604171 IP rosebud.lan > binkley.lan: ICMP echo reply, id 8203, seq 1,
  length 64
12:01:16.604295 IP binkley.lan > rosebud.lan: ICMP echo request, id 8203, seq 2,
  length 64

使用这些过滤器时,我可以利用布尔逻辑。也就是说,我可以使用逻辑运算符来开发复杂的过滤器。例如,假设我想要捕获 Web 流量,一个选择是使用 tcp and port 80,即捕获所有目标端口为 80 的 TCP 数据包。您会注意到,我没有指定源端口或目标端口。如果需要,我当然可以指定 src port 80dst port 80。但是如果我没有指定源或目标端口,我将会捕获到会话的两端。当消息通过目标端口 80 发出时,接收方系统会交换源和目标端口号,响应中的端口 80 就会变成源端口。如果我只捕获 src port 80,我就无法捕获到反向的消息。当然,这可能正是您所需要的,但也要记住这一点。您可能还需要指定一个端口范围来捕获多个端口,您可以使用 port-range 来捕获一系列端口,例如 80–88

BPF 语言提供了很强的能力。如果您需要非常复杂的过滤器,完全可以查找 BPF 的语法和示例,找到特定的过滤规则。我发现指定端口非常有用。此外,我通常知道要捕获流量的主机。在这种情况下,我可以使用 host 192.168.86.35 来只捕获来自该 IP 地址的流量。同样,我没有指定源或目标地址。如果需要,我可以通过 src hostdst host 来指定源或目标。如果不指定,我就会捕获会话的双向流量。

即使是对 BPF 的简单理解,也能帮助您聚焦于相关的数据。当我们开始查看数据包捕获时,您将理解数据包分析的复杂性,因为有大量的帧包含了需要仔细查看的详细信息。

Wireshark

当您拥有数据包捕获文件时,您可能会想进行一些分析。最好的分析工具之一就是 Wireshark。当然,Wireshark 本身也可以捕获数据包并生成 .pcap 文件,如果您想将捕获的内容存储以供后续分析,或者供他人分析,Wireshark 也能满足这个需求。然而,Wireshark 的主要优势在于它能够深入挖掘数据包的内容。与其花时间介绍 Wireshark 的界面或如何使用它来捕获数据包,不如直接开始使用 Wireshark 分析一个数据包。图 2-4 显示了一个 HTTP 数据包中的 IP 和 TCP 头部。

image.png

从这张图中,您可以看到 Wireshark 提供的细节比我们从 tcpdump 获得的要多得多。这是 GUI 工具相比命令行工具具有显著优势的一个方面。GUI 可以提供更多的空间,并且以更好的方式展示每个数据包头部中的信息。在 Wireshark 中,每个字段都被展示在单独的一行中,因此很容易理解每个字段的含义。您还会注意到,某些字段甚至可以进一步展开。例如,Flags 字段可以被展开查看详细内容。因为 Flags 字段实际上是由一系列比特组成的,您可以点击箭头(或三角形)展开该字段,从而看到每个位的具体值。当然,您也可以直接通过查看 Wireshark 展示的行来看到设置了哪些标志,因为 Wireshark 已经为我们完成了这些解析。在这个数据帧中,Don’t Fragment 位被设置了。

使用像 Wireshark 这样的工具的另一个优势是,我们可以更轻松地查看数据包的内容。通过找到一个感兴趣的帧(通常是它属于一个我们认为有价值的会话),我们只需要选择 Follow TCP Stream(跟踪 TCP 流)。除了只显示属于该会话的帧外,还会弹出一个窗口,展示所有帧的有效负载(Payload)的 ASCII 解码。您可以在图 2-5 中看到这一点。Wireshark 还会对输出进行颜色编码:红色表示客户端消息,蓝色表示服务器消息。您还会在窗口底部看到一个简短的总结,指示会话中客户端和服务器的消息比例。

image.png

Wireshark 具有与 tcpdump 相同的过滤功能。在 Wireshark 中,我们可以将过滤器应用为捕获过滤器,这意味着我们只会捕获符合过滤器条件的数据包;或者,我们也可以将过滤器应用为显示过滤器,作用于已经捕获的数据包。当涉及到过滤时,Wireshark 会提供很多帮助。当你开始在屏幕顶部的过滤框中输入时,Wireshark 会尝试自动补全。此外,它还会通过颜色编码来指示你输入的过滤器是否有效:当过滤器无效时,框会变成红色;当过滤器有效时,框会变成绿色。Wireshark 可以访问它所知的几乎所有协议的字段或属性。例如,我们可以根据 HTTP 响应码类型进行过滤。如果你生成了错误并想查看导致错误的会话,这样的过滤可能非常有用。

Wireshark 还会为我们进行大量分析。例如,分片的包会显示为彩色框,表示它们有问题。如果一个包的校验和不匹配,属于该包的帧将会被标记为黑色。如果协议中存在错误,例如包的格式不正确,帧会被标记为红色。同样,TCP 重置(RST)帧也会标记为红色。警告帧则会被标记为黄色,这通常是因为应用程序生成了一个不寻常的错误码。如果发生连接问题,你也可能会看到黄色标记。如果你想节省一些时间,可以使用 Analyze 菜单,选择 Expert Info 来查看所有已标记的帧。图 2-6 显示了此视图的示例。

image.png

Wireshark 具有如此多的功能,我们甚至还只是触及了表面。你可能会发现它的主要价值在于它以一种易于阅读的方式展示了每个协议的头部信息。这将帮助你在测试中遇到问题时,快速了解发生了什么。我还应该提到一个功能,那就是 Statistics 菜单。Wireshark 会提供你捕获的数据的图表和不同视图。一个这样的视图是协议层次结构,正如你在图 2-7 中所看到的。

image.png

协议层次结构视图在很多方面都很有用,其中之一是快速识别你不认识的协议。它还帮助你确定哪些协议是最常用的。例如,如果你认为自己在使用很多基于 UDP 的攻击,但 UDP 占发送的总消息数的比例很小,那么你可能需要进一步调查。

Wireshark 默认安装在 Kali Linux 上,所谓的“开箱即用”。然而,它也可以安装在其他操作系统上,如 Windows 和 macOS,以及其他 Linux 发行版。我不能过分强调这个工具的价值,尤其是在你熟练掌握使用后,它能够为你节省大量工作。能够完全解码应用层协议,并提供有关应用程序正在发生的事情的简要总结,这对于分析和排查问题来说是无价的。

加密流量的使用为几乎所有网站带来了挑战,但这并不是无法绕过的。你可以将加密密钥添加到 Wireshark 的 Preferences 中,但确保拥有正确的密钥以解码你想要的通信流需要很多工作。对于所有未加密的流量,你可以进行完整的协议解码,并且你始终可以查看报文头部,了解谁在与谁通信。

中毒攻击

我们面临的一个挑战是大多数网络都是交换式的。你连接的设备只会将消息发送到接收方所在的网络端口。过去我们使用的是集线器(hub)。集线器是广播设备,而交换机则是单播设备。任何进入集线器的消息都会被发送到集线器的所有端口,让各个端点根据 MAC 地址确定数据帧属于谁。集线器本身没有任何智能功能,它仅仅是一个中继器。

而交换机改变了这一切。交换机会读取第二层(Layer 2)头部来确定目标 MAC 地址,并且它知道拥有该 MAC 地址的系统所在的端口。交换机通过观察每个端口接收到的流量来确定这一点。源 MAC 地址会被附加到端口上,交换机会将这些映射信息存储在内容可寻址存储器(CAM)中。这样,交换机就不需要扫描整个表格,而是可以直接通过 MAC 地址查找相关信息。这个内容就是交换机用来查找端口信息的地址。

为什么这与本话题相关呢?因为有时你可能想要收集某个你没有直接访问权限的系统的信息。如果你拥有网络并且可以访问交换机,你可以配置交换机将一个或多个端口的流量转发到另一个端口。这将是一个镜像(mirroring)而非重定向。接收方将收到流量,但用于监控的设备或进行流量捕获分析的人员也会接收到数据包。

如果你无法合法访问到这些消息,可以通过欺骗攻击(spoofing)来获取它们:你假装成另一个人以获取流量。你可以通过几种方式来实现这一点,接下来我们将进一步探讨这些方式。

警告

虽然欺骗攻击是攻击者常用的手段,但除非它属于你测试范围的一部分,否则不应在你测试的网络上使用这种技术。使用这种技术可能会导致数据丢失。

ARP 欺骗

地址解析协议(ARP)是一个简单的协议。它的假设是,当你的系统需要在网络上通信,但只有 IP 地址而没有 MAC 地址时,它会向网络发送一个请求(who-has)。拥有该 IP 地址的系统会响应(is-at),并返回其系统的 MAC 地址。然后,你的系统就能知道目标系统的 MAC 地址,并将它已经持有的消息发送到正确的目标。

为了提高效率,你的系统会缓存该映射。事实上,它会缓存它看到的任何映射。ARP 假设只有当系统被请求时,它才会表明自己拥有某个 IP 地址。然而,实际上并非如此。如果我让我的系统发送一个 ARP 响应(is-at),宣称我拥有你的 IP 地址,并且任何试图访问该 IP 地址的流量都应发送到我的 MAC 地址,那么这些流量就会被发送到我这里。通过发送一个 ARP 响应,声明你的 IP 地址在我的 MAC 地址上,我就把自己置于了通信流的中间。

然而,这只是单向的。如果我通过我的 MAC 地址欺骗你的 IP 地址,那么我只能获得原本应该发给你的消息。为了获得通信的另一端,我需要欺骗其他地址。例如,我可以欺骗本地网关来捕获你和互联网之间的消息。这只解决了将消息发送给我的问题。为了使通信不被中断,我还需要将消息重新转发给目标,否则通信就会停止,因为没有人收到了他们期望的消息。这就要求我的系统将原始消息转发给预定的目标。

由于 ARP 缓存有过期时间,如果我不不断发送这些消息,最终缓存会超时,我就无法再接收到想要的消息。这意味着我需要持续不断地发送这些消息,这些消息被称为“无请求 ARP 消息”(Gratuitous ARP)。无请求 ARP 消息是指没有被请求,但仍然主动发送的消息。虽然这种行为有合法的应用场景,但并不常见。

虽然可以使用其他工具来实现这个攻击,我们可以使用 Ettercap。这个程序有两种工作模式。第一种是 curses 风格的界面,这意味着它在控制台中运行,但并不完全是命令行,它呈现的是基于字符的 GUI。另一种是完全基于 Windows 的 GUI。图 2-8 展示了 Ettercap 在选择目标主机并启动 ARP 欺骗攻击后的界面。为了启动欺骗攻击,我首先扫描主机,获取网络上的所有 MAC 地址。然后,我选择了两个目标并启动了 ARP 欺骗攻击。

选择两个目标的原因是确保获取对话的两边。如果我只欺骗一方,我只能获得对话的一半。我假设我想收集的是目标和互联网之间的通信。因此,我将目标设置为一台主机,将我的网络上的路由器设置为第二台主机。如果我需要获取网络上两台系统之间的流量,我会选择这两台系统。一台作为目标 1,另一台作为目标 2。示例 2-19 显示了 ARP 欺骗攻击在数据包捕获中的样子。你会看到两个 ARP 响应,其中 IP 地址属于我的目标。我还包括了我系统上的 ifconfig 输出的一部分,以便你看到捕获的 MAC 地址就是我的系统的 MAC 地址,因为 ARP 欺骗攻击是在我的系统上运行的。

image.png

示例 2-19. tcpdump 显示 ARP 欺骗攻击

17:06:46.690545 ARP, Reply rosebud.lan is-at 00:0c:29:94:ce:06 (oui Unknown), length 28
17:06:46.690741 ARP, Reply testwifi.here is-at 00:0c:29:94:ce:06 (oui Unknown), length 28
17:06:46.786532 ARP, Request who-has localhost.lan tell savagewood.lan, length 46
^C
43 packets captured
43 packets received by filter
0 packets dropped by kernel
root@kali:~# ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
        inet 192.168.86.227  netmask 255.255.255.0  broadcast 192.168.86.255
        inet6 fe80::20c:29ff:fe94:ce06  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:94:ce:06  txqueuelen 1000  (Ethernet)

一旦我启动了 ARP 欺骗攻击,我就可以使用 tcpdump 或 Wireshark 捕获整个对话。请记住,这种攻击仅在本地网络上有效。原因是 MAC 地址是第 2 层地址,因此它只在本地网络内流动,不会跨越任何第 3 层的边界(即从一个网络到另一个网络)。Ettercap 还支持其他第 2 层攻击,如 DHCP 欺骗和 ICMP 重定向攻击。这些攻击中的任何一种都可以确保你抓取到本地网络上其他系统的流量。

DNS 欺骗

捕获可能位于本地网络之外的流量的一个解决方案是使用 DNS 欺骗攻击。在这种攻击中,你通过干扰 DNS 查找来确保当目标系统尝试将主机名解析为 IP 地址时,目标系统会收到你控制的系统的 IP 地址。这种类型的攻击有时被称为缓存污染攻击。其原因在于,你可能会利用靠近目标的 DNS 服务器,通常是一个缓存服务器,这种服务器会代为从权威服务器查找地址并将其缓存一段时间,缓存时长由权威服务器决定。

一旦你获取了对缓存服务器的访问权限,你就可以修改其中的缓存,将目标系统导向你控制的服务器。你还可以通过编辑缓存来插入任何不存在的条目,这将影响使用该缓存服务器的任何人。这个过程的优点是可以在本地网络之外工作,但缺点是你需要攻破一个远程 DNS 服务器。

一种或许更简单,但仍然需要你处于本地网络中的方法是使用程序 dnsspoof。当一个系统向服务器发送 DNS 请求时,它期望从该服务器得到响应。请求中包含一个标识符,以防止攻击者发送盲目响应。然而,如果攻击者能够看到请求的发送内容,就可以捕获该标识符,并将其包含在带有攻击者 IP 地址的响应中。dnsspoof 由 Dug Song 多年前编写,在当时可能不太可能处于交换机网络上。如果你处于交换机网络中,你需要额外的步骤来捕获 DNS 消息,才能看到请求。这个程序并没有默认安装,但可以作为 dsniff 套件的一部分进行安装。

运行 dnsspoof 非常简单,尽管准备工作可能不太简单。你需要一个将 IP 地址映射到主机名的 hosts 文件。这个文件由单行条目组成,每行包含一个 IP 地址,后面是与该 IP 地址关联的主机名。准备好 hosts 文件后,你就可以运行 dnsspoof,如下所示:

示例 2-20. 使用 dnsspoof

┌──(kilroy@badmilo)-[~]
└─$ sudo dnsspoof -i eth0 -f myhosts udp dst port 53
dnsspoof: listening on eth0 [udp dst port 53]
192.168.1.253.39071 > 192.168.1.1.53:  45040+ A? www.bogusserver.com
192.168.1.253.34786 > 192.168.1.1.53:  39506+ A? www.bogusserver.com
192.168.1.253.55556 > 192.168.1.1.53:  12829+ PTR? 15.1.168.192.in-addr.arpa
192.168.1.253.46864 > 192.168.1.1.53:  40977+ PTR? 15.1.168.192.in-addr.arpa
192.168.1.253.41132 > 192.168.1.1.53:  58799+ PTR? 15.1.168.192.in-addr.arpa
192.168.1.253.33490 > 192.168.1.1.53:  4611+ PTR? 15.1.168.192.in-addr.arpa
192.168.1.253.53561 > 192.168.1.1.53:  31549+ PTR? 15.1.168.192.in-addr.arpa

你会注意到,在命令行的末尾,我包括了 BPF(Berkley Packet Filter)来专注于捕获的数据包。如果没有这个,输出将默认为仅显示 UDP 端口 53 的数据报,而这些数据报如果不是从你运行 dnsspoof 的系统发出的,就会被过滤掉。你可以像使用 tcpdump 一样使用 BPF 来查看更广泛的流量。我移除了没有捕获到本地系统流量的部分,并包含了自己的 BPF,以便在本地进行测试。你会看到,任何与 BPF 参数匹配的请求在到达时都会被打印出来。这些输出类似于你从 tcpdump 中看到的内容。

你可能会想,如果已经可以使用 Ettercap 或 arpspoof(另一个 ARP 欺骗工具,由 Dug Song 编写,并与 dnsspoof 一起包含在同一工具套件中),为什么还要多此一举使用 dnsspoof?使用 dnsspoof 的一个优势是,你不仅可以使用 ARP 欺骗将系统引导到另一个 IP 地址,还可以让目标系统认为它正在访问一个合法的地址。例如,你可以创建一个恶意的 Web 服务器,使其看起来像真实的服务器,但其中包含一些恶意代码来收集数据或感染目标。这并不是 DNS 欺骗的唯一目的,但它是一个常见的用途。

总结

通常,针对系统的攻击会发生在网络层面。虽然并非所有攻击都针对网络协议,但足够多的攻击是针对这些协议的,因此值得花时间了解网络元素和与各个层相关的协议。以下是本章的一些关键要点:

  • 安全测试的目标是发现保密性、完整性和可用性方面的缺陷。
  • 基于 OSI 模型的网络协议栈包括物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。
  • 压力测试可以揭示至少对可用性的影响。
  • 加密可以使网络连接变得难以观察,但弱加密可能暴露保密性问题。
  • 欺骗攻击可以提供一种观察和捕获远程来源网络流量的方式。
  • 使用 tcpdump 和 Wireshark 等工具捕获数据包可以帮助洞察应用程序发生的情况。
  • Kali 提供了对网络安全测试有用的工具。

有用资源

  • Dug Song 的 dsniff 页面
  • Ric Messier 的《TCP/IP》视频,由 Infinite Skills 发布,2013年
  • Craig Hunt 的《TCP/IP 网络管理》,第3版(O’Reilly, 2002)