作者:[H.B.U]小金
一、开场:一个正在加注热水的浴缸
这是一个有钱人家,因此家里有个浴缸。现在,浴缸旁的水龙头正在流出热水注入浴缸,或许主人忘了调节水温,流入浴缸的水是滚烫的。这还不算太糟糕,最糟糕的是主人已经被电视剧吸引住了,似乎忘记了放水洗澡的事情,于是热水就不断的注入了浴缸,整个浴室被蒸气笼罩……
离浴室较远的卧室里,放着一台电脑,此刻它正一闪一闪的下载着文件。
二、导语:浴缸和电脑
浴缸的水正在缓缓上升,先让我们把注意力集中到那台电脑上。这是一台安装了Win2000的豪华配置,它正在下载着一个大文件,网卡指示灯不断闪动着,配合着状态栏里时刻跳动的字符:“本次缓冲256字节/本次已写入4096字节”
这些数据也如浴缸里正在进行的事件一样,慢慢的填充着电脑磁盘,只是,磁盘并不是浴缸,因为浴缸的水最终都要流入下水道,所以磁盘属于下水道。那么,电脑里有没有类似的一个浴缸呢?有的,每台电脑都有多个快速注水排水的浴缸,这就是内存。
内存是做什么的?它负责数据的通行和处理工作,所有数据都在内存里停留很短的时间让处理器作一些工作,然后再去它的目的地。有的数据只会停留一会儿,有的却会在内存里积累到一定大小才赶路去目的地,这个过程称为“缓冲”(BUFFER)。
当一个程序执行的时候,它的数据会进入内存,就像一批劳累的旅游者走进沐浴场要求洗澡一样,工作人员必须为他们在浴缸里注水,一次要注入多少热水全凭旅游者们的想法。在电脑里,系统会用内存管理技术给每个数据分配一定的内存空间,因为不可能每个数据请求的内存空间都一样大,系统就按照程序代码或者默认数据长度给每个数据设定的最大尺寸在内存里划分出来,这个最大尺寸,就是“缓冲区”,系统分配的内存空间总称为“堆栈”,前面说过,一些数据必须在内存中积累到一定大小,这就需要缓冲区的协作;即使不积累,它们也要有一个容身之所,同样要分配缓冲区。程序员都会清楚这些缓冲区的效果:调用API函数后返回的是一段固定长度的数据,它的前部分数据才是程序员期望的,后面的则是一串空格,这段空格加上全部数据,就是这个API函数在内存里的缓冲区大小,这是由程序员给它挂的最大尺码牌决定的。(图1)
于是,内存里时刻存在着一堆大大小小的缓冲区,里面有可能存在适当或者过少的数据,也可能空置着,还有一些数据溢出来了……
三、意外:溢出来的热水
1.用溢出漏洞发起的拒绝服务攻击
镜头回到浴室,水龙头仍在继续注入着热水,可是下面的浴缸已经满了,于是,热水顺着浴缸外壁洒了出去,落到冰冷的地上,迅速冒起一股水蒸气……
正在这个时候,卧室里的电脑屏幕突然黑了,继而出现了BIOS自检画面:电脑重新启动了。
网络的另一头,一个自认为是黑客的少年正在郁闷:“又失败了一台,这就是所谓的最好用的RPC溢出工具?!”
为什么电脑会重新启动?因为它刚接收到一串特殊数据,这串数据告诉系统的远程调用服务(Remote Procedure Call),它请求连接,需要长度为128字节的缓冲区,但是在系统分配缓冲区后,它却挤进了超过一倍的数据量!这些多出来的数据把系统给的缓冲区撑破了,多出来的数据还淹没了周围几个靠得最近的缓冲区。系统的内存安全管理机制立刻行动,把远程调用服务停止了。但是RPC服务却是系统必须的服务,于是,系统只好重新启动,这样一来,内存空间恢复正常,似乎什么事情也没发生过。
这是典型的RPC DCOM溢出漏洞使用失败的例子。由于设计的失误,RPC服务的一个接口函数会在接收到一串包含超长文件名的数据后产生溢出,因为它用来存储文件名的内存空间实在太小了。
用溢出漏洞发起拒绝服务攻击,是最简单常见的溢出攻击方式,它很简单就能实现,只要知道了导致溢出的关键地方,就可以塞入超大的数据量让一些程序瘫痪、系统重新启动,可以做到某种程度的干扰。但是,它无法进一步让入侵者破坏系统。
 //文章出处:网络技术论坛(http://bbs.nettf.net) 作者:小金
2.溢出导致的系统渗透
浴缸旁边放着一块香皂,由于水的溢出,香皂不时的被冲刷一下,逐渐变得滑腻,地上也有些积留的水,香皂被微微的浸泡着。主人家的猫正在散步,无意中来到了浴室,它好奇的看着地板上陆续冒出的水蒸气,走了过去,正在这时,浴缸里又一次热水飞溅,淋到这只倒霉的猫的身上,它惨叫一声,转身就跑,后腿却蹬到了香皂,被水润滑的香皂一下子滑进了下水道……
入侵者并不满足于让系统重新启动,他们要的更多。
在早期,人们就已经发现,当内存里的缓冲区发生溢出时,并不是全部会被系统干涉的,一些“恰到好处”的攻击数据会让发生溢出的程序执行特殊的命令,于是入侵者开始尝试编写一段特殊格式的数据,它让被攻击的程序发生溢出,堆栈里的指令就变得混乱,而这段特殊数据刚好挤到一个合适的内存位置,处理器认为它是程序送来的数据,就视为正常指令执行了,但是,这段指令却是致命的。(图2)
这样一段特殊构造的字符串被称为“ShellCode”,它包含一串可以导致溢出的数据和一段执行指令,这段执行指令可以让一些特定程序执行,例如CMD.EXE,这是最常见的攻击方式。有人说,既然可以执行指令,那么可以构造一个很全面的指令,例如提升用户并写入一个木马程序……很遗憾,这只能是美丽而不现实的梦,缓冲区不是无限大的,溢出的指令也不能全部占据堆栈,它不得不受到长度限制,正如那个浴缸里溢出的水再多也只能在地板上留下薄薄一层积水,多出来的只能直接流入下水道,而不是上升到淹没整个屋子的高度。
溢出成功后,从远方发来的数据变为由CMD.EXE执行,因此入侵者获得了一个攻击的入口,这个入口就是平常所说的“Shell”。CMD.EXE做了不该做的事情,正如那块香皂掉进了不该去的地方一样,可是它们没有错,香皂是被猫踢下去的,CMD.EXE是被一段异常指令推到前面的。正常情况下,这很难做到,但是在湿润地面和被破坏的缓冲区这些因素下,这一切都发生了。
如果那只猫没有进来,溢出的水只能把香皂毁坏,而不能把它送入下水道;如果没有那个精心制作的指令,或者指令错误,入侵者也只能让系统和程序非法操作而已。前面那个少年就是发送了错误的ShellCode导致的溢出不成功(拒绝服务倒是“成功”了)。
说到那台电脑,此刻……它被连续重新启动几次后,终于被少年拿到了Shell……那只被水烫到的猫,也在一旁盯着屏幕出神。
四、结局:关掉水龙头,更换自动停水的水龙头,还是换更大的浴缸?
哗啦水声终于惊动了主人,他往浴室看了一眼,突然想起洗澡的事情,马上,他冲进了蒸汽旋绕的浴室,关掉了水龙头,这时浴缸里溢出的热水淋到了他的脚趾……
外面的人们只听到一声惨叫。同时,卧室里那台电脑再次重新启动,当屏幕再次有显示的时候,却永远的停留在黑底白字的“NTLDR is Missing”信息上了,入侵者破坏了它的启动文件和整个系统。
对付溢出攻击始终是系统管理员最头痛的事情,因为很多溢出都是系统关键程序或者影响很大的程序容易出现的,人们可以修复已经发现的溢出,可是整个程序代码里有多少个缓冲区会导致溢出呢?如今针对溢出漏洞制造的病毒蠕虫越来越多,从去年初导致整个世界网络再次瘫痪的SQL蠕虫,到8月再次爆发的RPC蠕虫,无一不是使用了溢出攻击。
溢出攻击似乎正在成为主流,这是谁之过?管理员们该如何防范?也许加大程序代码预分配的缓冲区尺寸可以防范,可是它会浪费过多资源。为了避免水再次溢出而更换大浴缸是不现实的,再大的浴缸也会有满溢的时候。
那么,我们必须考虑另外的方法。增加一种称为“边界检查”技术的代码负责检查缓冲区数据是一部分程序员的方法,也是修复已知溢出漏洞的方法,可是它通常只能在漏洞发现后才能做到,而且,太多的检查代码会导致程序代码冗长。为了避免浴缸水太满而更换一个带有水位检测的自动控制水龙头,未免太奢侈了点。
如果主人没有离开,那么水龙头就会及时关上,水也就不会溢出了。这在现实中有用,可是电脑里,人只能在外部做一些相当于阻塞掉水管的事情,内存里的水龙头只有程序代码能关闭。如果那台电脑的网线被及时拔掉,入侵者的破坏也就不能得逞,但是正常的数据通讯也断了。(图3)
如何彻底根治溢出攻击,这的确是个问题……