被动攻击性抵抗:操作系统指纹规避
操作系统指纹识别是一种确定远程主机正在运行的操作系统的方法,它基于从远程主机返回的数据的特征。这可以像连接到主机并读取服务标语一样简单,也可以像对 TCP 初始序列号和标志进行统计分析一样复杂。外部人员有能力通过搜索 TCP 堆栈实现中特定于操作系统的差异来发现一般信息,例如主机正在运行哪个操作系统。在某些情况下,这些差异可以揭示操作系统版本号和处理器架构等详细信息。
通过查明主机的确切操作系统,攻击者可以对目标机器发起有针对性的精确攻击。在缓冲区溢出的世界中,了解操作系统的确切版本和架构可能是攻击者需要的全部机会。通过使用诸如 Linux 的 Netfilter 等软件,管理员可以规避准确的操作系统指纹识别方法,并且在某些情况下甚至可以操纵外部力量收集的结果。虽然这些做法绝不应被视为可靠的安全解决方案,但有时如果主机伪装成一个默默无闻的网络实体,它们可以阻止甚至迷惑潜在的攻击者。
虽然指纹规避确实为主机正在运行的实际操作系统提供了一个很好的模糊层,但它绝不会以任何方式保护主机免受各种漏洞的攻击。安全实践和策略旨在提高攻陷系统所需的技能水平,而模糊性仅试图掩盖实际目标。即使您的系统在外界看来似乎正在运行 Microsoft IIS5,如果您正在运行易受攻击版本的 sendmail(例如),并且一些脚本小子的自动扫描器试图利用您,这也无法保护您。指纹规避旨在阻止攻击,而不是阻止攻击。
在尝试通过操作系统欺骗来劝退潜在的攻击者之前,必须熟悉用于操作系统指纹识别的工具和方法。这里的“攻击者”一词是广义上的,包括那些试图对主机进行指纹识别的人,或那些可能有意图损害系统的人。安全一直是,并且在笔者看来永远都是,一系列措施和反措施的场景。通过熟悉可用于此类攻击的工具和方法,您不仅可以为当前的交战做好准备和计划,还可以深入了解未来可能发生的事情。
有几种公开可用的工具可用于尝试对操作系统进行指纹识别。在这些工具中,似乎有一种是流行的选择:nmap (www.insecure.org/nmap/index.html),由 Insecure.org 的 Fyodor 开发。nmap 使用多种技术尝试从网络级别确定主机操作系统,其中一些技术方法原始,另一些技术方法更复杂,需要对 TCP/IP 协议和协议标准有很好的理解。在 nmap 采用的方法中,一些最值得注意的是
FIN 探测—通过向主机上的开放端口发送一个数据包,该数据包中仅设置了 FIN 标志,攻击者可以从某些响应请求的主机中收集信息。此行为不符合 RFC 标准,因此是操作系统的良好指示。
TCP ISN 采样—TCP 数据包 ISN(初始序列号)采样可能是确定和分类远程主机的宝贵方法。通过观察 ISN 的模式,攻击者可以对主机操作系统进行有根据的猜测。
ICMP 错误消息—通过使用 ICMP(互联网控制消息协议)错误消息,攻击者可以根据主机响应找到有用的信息。特别感兴趣的领域是回复数据包中的校验和、错误消息回显完整性和 TOS(服务类型)字段。
TCP 选项—任何 TCP 堆栈最能揭示问题的方面可能是它如何处理可选的 TCP 标志和数据。通过向主机发出特定请求并更改窗口缩放和段大小,可以根据主机接受或响应这些可选参数的意愿来确定主机正在运行哪个操作系统。
虽然所有这些操作系统指纹识别方法都在数据包级别,但应非常注意在服务级别了解您的系统。攻击者可以排序和比较数据包结构,但通常只是查询 Web 服务器以获取 HTTP 响应标头中的“Server”字段。了解哪些服务容易识别自己,更重要的是操作系统架构,将向我们展示可用于远程信息检索的其他途径。
客户端的谦虚(或缺乏谦虚)也可能是从主机收集信息的绝佳方式。与其他选项不同,此过程可以是完全被动的。通过观察客户端应用程序如何向服务呈现自身,您可以合理地猜测操作系统,甚至可能是架构。在这些客户端中,Web 浏览器、电子邮件客户端和 IRC(互联网中继聊天)客户端通常是最主要的违规者。如果我们在 IRC 上并从用户请求 CTCP 版本,并收到“mIRC32 v5.81 K.Mardam-Bey”的回复,此时我们可以有根据地猜测该主机正在运行某种形式的 Windows 操作系统。
最后,还有漏洞利用测试。虽然不太委婉,但它仍然可以有效地发现主机的操作系统。通过发起一系列特定于操作系统的拒绝服务攻击,外部人员可以测试主机是否易受攻击。这可以确定主机正在运行哪个评级系统,通常精确到补丁级别。Windows 社区应该感谢 Fyodor 和其他指纹识别工具作者没有决定将此方法纳入他们通常的一系列扫描技术中。
如果您已阅读到此处,那么您至少无疑有点好奇,为什么有人会费力进行操作系统指纹规避。好问题!我认为这里的逻辑因人而异。每个人都有自己想要或不想要操作系统模糊性的原因。
对于某些人来说,额外的模糊层有助于他们感觉内心舒适和温暖。就像那些觉得有必要从 Telnet 登录屏幕中删除问题标语的人一样,但为了远程访问安全性而求助于 Telnet 而不是 SSH(模糊,但在技术上不太安全)。对于另一些人来说,网络级别的操作系统模糊性概念使他们能够微调和调整其 IDS(入侵检测系统),因为他们对应该进入其网络的内容以及应该离开网络的数据有相当好的了解(模糊、谨慎且希望安全)。有些人甚至可能需要安全性,因为他们插入的每个网络都是潜在的敌对网络;他们的操作系统越模糊,他们完成手头任务而不被注意到的机会窗口就越大(模糊、谨慎、安全,并且可能正在阅读您的电子邮件)。最后,我们中有些人这样做是为了好玩,因为我们可以,并且因为我们能够愚弄周围那些坚持向我们发起扫描的未知人士而感到一丝小小的刺激(是的,罪有应得)。
现在是时候尝试我们的指纹规避运气了。熟悉用于确定主机操作系统的一些常用技术,我们可以逆向工程这些概念,以帮助我们隐藏我们的操作系统身份。
首先,我们需要确保所有补丁都已到位并且系统已安全。正如我之前所说,只有在实施安全措施后,才应考虑模糊性。我确信有些人会不同意这一点,他们完全依赖模糊性作为他们获得安全系统的方式,但如果今晚那个脚本小子 33 的自动化脚本获得了您机器的 root 权限,那模糊性有什么用呢?我敢打赌,一旦他获得 root 访问权限,您正在运行的 Linux 版本就不在他的待办事项列表中了。
其次,我们需要观察我们的服务。它们是否与我们希望冒充使用的操作系统相匹配?在大多数情况下,这并不是什么大问题,因为大多数 UNIX 环境共享相似的服务,即使不是相同的服务。但是,如果您希望将自己展示为 Windows 机器,甚至 Cisco 路由器,那么显示运行 IRCd 可能对您不利。努力将您的服务与合适的诱饵主机相匹配。
当我们在谈论服务时,最好也开始 greping 服务源代码,查找服务的标语或常用标识符。一些微妙的标识符可能是支持 ASP 页面或以 gzip 格式压缩提供的 Web 内容。对于大多数人来说,这将是一项艰巨的工作。同样,这取决于您要实现的新诱饵主机的模糊性和一致性程度。
接下来,我们需要查看我们的主机在网络上的外观与我们的诱饵主机在网络上的外观应如何。为了使这更容易一些,我建议研究已记录的材料,即工具本身使用的当前指纹文件。应该花时间注意的不仅是您的诱饵主机如何响应通常的查询,还包括它在 TCP 中支持哪些特殊标志。TCP 标志是外部人员确定您正在运行哪个操作系统的有用信息。指纹文件不包括主机可能给出的所有可能响应,而只是一些可重复工作的简单技术。根据您希望实现的模糊性级别,可能值得研究 nmap 未使用的指纹信息(OSPF、OOB、IPv6 等)。或者,彻底的乐趣可能会被您花在收集此信息上的不眠之夜所抵消。
最后,需要做出决定。您是狡猾还是偏执?如果您回答后者,那么您很可能希望继续混淆您的客户端软件。如上所述,主机的客户端软件倾向于直接或间接地泄露有关系统的各种信息。在我们之前的示例中,IRC 客户端将自己列为用于 Win32,但还有更微妙的方法来确定主机,例如读取传出邮件的电子邮件标头。再一次,这一切都归结为在您的系统满足您的标准之前,您愿意花费多少个不眠之夜。
操作系统指纹规避就像安全性的任何其他方面一样;它需要计划、正确执行,最重要的是,理解。如果安全策略未正确实施,则系统可能比根本未实施这些策略时更易受到攻击。
受欢迎程度让位于认可。在大多数软件领域,受欢迎程度是一件好事;它将注意力集中到您的所有辛勤工作和决心上。在操作系统指纹规避的情况下,认可对您不利。如果您正在使用流行的工具或软件包,最终将发现漏洞和细节;这是不可避免的。这些相同的特定于软件的标识符将允许其他人准确地对您的反措施进行指纹识别,而不是操作系统本身。
大多数操作系统都试图使其 TCP ISN 序列随机化,以尝试阻止 TCP 劫持和系统上更复杂的攻击。如果您的规避实现尝试更改 TCP 初始序列号,则应格外小心,以确保您不会降低此功能并将您的主机置于这些类型攻击的风险之中。
与安装在您系统上的任何软件包一样,应用程序安全性应是首要考虑因素。规避过程的一部分是掩盖现有服务;另一部分以代码的形式出现,旨在过滤您的流量并掩盖您在线路上的内容。应格外小心,以确保为此任务生成的应用程序通过良好的编程实践和严格的测试来保证安全。只需要一个考虑不周的 strcpy() 就会迅速将此资产变成负债。
先前列出的规避策略之一是更改识别自身的软件的服务标语。请注意,因为某些附加软件包实际上使用这些相同的标语来确定与当前系统软件的兼容性。
在确定规避并不意味着安全之后,我们需要查看此过程的另一个方面,即性能。由于良好的规避设置会大规模过滤您的流量,因此系统性能可能会受到影响。显然,如果您有一个为 10,000 个客户端托管网页的站点,则性能是一个比您简单地在某处设置一个 Linux 盒子供您和您的朋友查看电子邮件和 IRC 更大的问题。作为管理员,您需要决定对您(和您的用户)而言,更大的回报是性能还是隐私。
为了说明指纹规避的可行性和相对容易性,我包含了一个用于 Linux 的小型示例用户空间应用程序 (OSFPE),它利用了 Netfilter 内核模块 [请参见 ftp://ftp.linuxjournal.com/pub/lj/listings/issue89/4750.tgz 中的列表 1]。通过使用 Linux 中的 Netfilter 等软件,操作系统指纹规避变得越来越有效。类似的修改和应用程序正在各地涌现;在 BSD 中,可以通过 ipfilter 和适量的代码来完成此任务(在撰写本文时,ipfilter 已从 BSD CVS 树中删除,对不起各位)。Windows 用户(他们在这一领域处于最大的劣势)正在发现修补其 TCP/IP 通信的方法,并且随着 Win32 的 Libpcap 的出现,他们可以捕获并伪造自己的数据包响应。
正如 Netfilter 的作者所说,Netfilter 是“一个用于数据包处理的框架”。听起来很有趣,对吧?Netfilter 与 Linux 内核(确切地说是 2.4.x 及以上内核)接口,并为每个协议注册挂钩。如果规则到位,这些挂钩会捕获与指定规则匹配的传入或传出网络流量。然后处理这些数据包并标记为 NF_DROP 以丢弃数据包,NF_ACCEPT 以接受数据包以在堆栈上进行正常处理,或 NF_QUEUE 以将数据包排队以在用户空间中进行操作。如果数据包被排队以在用户空间中进行操作,则 ip_queue 驱动程序会将其放入队列中;然后由在用户空间中运行的任何已注册这些类型数据包的应用程序异步处理。当这些应用程序从队列中拉取数据包时,它们有能力操作、接受和拒绝数据包。如果数据包被标记为 NF_ACCEPT,则将其传递给下一个已注册此类数据包的应用程序。如果数据包被标记为 NF_DROP,则数据包将被丢弃,并且对该特定数据包的处理将停止。通过使用 Netfilter,用户空间中的应用程序实际上具有内核级别的网络流量控制。
iptables 是一个用于与 Netfilter 接口以设置、查看和删除系统当前网络过滤规则的应用程序。我在这里提到 iptables 是因为在开发概念验证代码时,我们认为向用户介绍用于规则管理的 iptables 程序比让应用程序处理它们更好。这将使人们更好地了解数据包排队的情况。
通过利用 Netfilter 模块和 iptables 规则管理程序,我们能够设置规则来捕获传入的 UDP、TCP 和 ICMP 数据包。根据传入的数据包和源主机,我们要么允许它们正常访问系统,要么制作响应以使其看起来像 Windows 主机,如 nmap 的操作系统指纹条目之一中所定义的那样。这是我们试图匹配的指纹,以及关于我们如何实现此目标的简要演练
TSeq(Class=TD|RI%gcd=1|2|3|4|5|8|A|14|1E|28|5A%SI=<1F4) T1(DF=Y%W=2017|16D0|860|869F%ACK=S++%Flags =AS%Ops=M|MNWNNT) T2(Resp=Y%DF=N%W=0%ACK=S%Flags=AR%Ops=) T3(Resp=Y%DF=Y%W=0%ACK=O%Flags=AR%Ops=) T4(DF=N%W=0%ACK=S++|O%Flags=R%Ops=) T5(DF=N%W=0%ACK=S++%Flags=AR%Ops=) T6(DF=N%W=0%ACK=S++|O%Flags=R%Ops=) T7(DF=N%W=0%ACK=S++%Flags=AR%Ops=) PU(DF=N%TOS=0%IPLEN=38%RIPTL=148%RID=E%RIPCK=E%UCK =E%ULEN=134%DAT=E)
第一行指出我们需要构建一个时间相关的 (TD) TCP 序列或一个具有随机增量 (RI) 等于但不大于 0x1F4 (500) 的序列。这实际上很容易实现,或者我应该说是匹配。首先,我们抓取传入的数据包,获取 TCP 序列号,生成一个介于 1 和 500 之间的伪随机数,并将这些值加在一起。这满足了指纹的随机增量和最大公约数 (gcd) 要求。
接下来,我们分解了所有各种数据包测试 (T1-T7),并在我们的 TCP 处理程序中为它们创建了案例。所有这些都非常简单明了,只是指示主机应如何响应不同类型的数据包以打开和关闭端口,确切的测试及其参数在 Fyodor 关于远程操作系统检测的论文中进行了更深入的介绍。
接下来,我们匹配了我们对 UDP 端口不可达查询的响应。nmap 在这里所做的是向主机上的关闭端口发送一个 UDP 数据包,并等待以 ICMP 端口不可达数据包的形式进行响应。ICMP 端口不可达数据包只是告诉查询主机,它们尝试向其传递 UDP 消息的端口失败了,因为该端口上没有侦听 UDP 服务。在某些网络上,这些消息永远不会发回,而是在路由器处被丢弃。为了符合指纹,我们努力发回他们期望的内容。
最后,作为额外的奖励,如果主机恰好扫描这些 TCP 端口是否打开,我们会向主机发送 Syn-Ack 数据包以响应主机上的特定端口。同样,对于我们希望在主机上显示为打开的特定 UDP 端口,我们不发送任何响应(如上所述,只有关闭的 UDP 端口才会发回端口不可达消息)。当对我们主机的扫描完成时,它应该看起来好像 TCP 端口 135 和 139 以及 UDP 端口 135、137 和 138 已打开。如果我们尝试对我们的主机进行指纹识别,我们应该与上面列出的指纹相匹配,并获得“Windows NT4 / Windows 95 / Windows 98”的列表。
最后,概念验证代码只是那样,一小段用于证明观点的程序。帮自己一个忙,不要在关键设备上运行它。打开它,从中学习,修改它,利用它,但不要依赖它。我已尝试保持代码的安全性和一定的可读性(有争议),但我不能保证任何事情。
感谢 Rex Warren 在帮助我完成本文和示例代码方面所做的所有辛勤工作,感谢 Fyodor 允许我引用他所有的辛勤工作以及如此出色的安全工具,感谢 Dan Kurc 阅读我的代码并称其为丑陋(嘿!这是我的第一个 C 程序),Sir Dystic 的 C 语言习惯用法和 Courtnee。
